mesh-issues: triage every new issue — severity, needs, suggested owner, dependencies — and say an issue ready when what it depends on is delivered (hq ADR 0279)
mesh/merge-gate pass: builds mesh-issues → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request

The operator asked for the judgement that already reads the backlog to fill what an
issuer cannot know, and for dependencies between issues, an issue being workable
only once what it waits for is on the machines. The triage fills only empty values,
adds links and removes none; readiness follows mesh-delivery's own events.
This commit is contained in:
jochen
2026-10-10 17:49:27 +02:00
parent 25c7bee3b8
commit 745985fd23
13 changed files with 1863 additions and 821 deletions
+40 -26
View File
@@ -23,7 +23,8 @@ what it refuses.
| `note` | `number`, `text` | appends a note; a note is never edited |
| `link` | `number`, `kind`, `target`, optionally `remove` | adds or removes a typed link |
| `import` | optionally `dry-run`, `commit` | hq's issue folders, read through the records module, each written as an issue under its own number; see below |
| `groom` | optionally `pass`, `confirm` | the duplicate check over the backlog: without arguments a dry run in the background, answering its pass id; with `pass` its progress and findings; with `confirm` and `pass` exactly that pass's findings applied, each checked again; see below |
| `set` | `number`, any of `severity`, `needs`, `suggested-owner` | changes them as a person decides |
| `groom` | optionally `pass`, `confirm` | the triage over the backlog: without arguments a dry run in the background, answering its pass id; with `pass` its progress and findings; with `confirm` and `pass` exactly that pass's findings applied, each checked again; see below |
**The cycle's rules, at the write** (hq playbook 03, which `00-META/checks/cycle.py` enforced on the
folders until the import): a status is one of `open`,
@@ -36,36 +37,49 @@ needs a `reason`. A refused change changes nothing and says which rule refused i
tracker like any other: the folder holds its narrative, the tracker its status. Until the import of
slice 3 ran, such an issue kept its status in the folder and `move` refused it; the import filled it.
## The duplicate check (hq ADR 0278)
## The triage (hq ADR 0278, ADR 0279)
Every issue opened is checked by the module itself: it consumes its own `mesh-issues.opened` event, and for an
Every issue opened is triaged by the module itself: it consumes its own `mesh-issues.opened` event, and for an
issue still at `open` asks the agent on its node — `claude-code.claude_code_judge@<node>`, which runs the agent
with no tools under the node's licence — two questions: a shortlist of at most five older issues by their titles,
then a verdict on the reports (`duplicate`, `related` or `none`, whether it is certain, and why). Only issues at
`open`, `diagnosing` or `located` that are older than the new one are offered; a resolved or wontfix issue is
never offered, linked or noted.
then, always, a judgement on the reports: whether it repeats one (`duplicate`, `related` or `none`, whether it is
certain, and why), its **severity** (critical, major, minor), what it **needs** (none, decision, operator), a
suggested owner when it names none, and the shortlisted issues it **depends on** or **relates to**, each with a
one-line reason. Only issues at `open`, `diagnosing` or `located` that are older than the new one are offered.
| verdict | done, through the module's own `note`, `link` and `move`, each recorded as by `duplicate-check` |
|---|---|
| `duplicate`, certain | the verdict noted on the new issue; linked `duplicates` to the older; its title and report noted on the older; moved to `wontfix`, reason "duplicate of N" |
| `related`, or `duplicate` not certain | the verdict noted; linked `relates-to`; noted on the older; nothing moves |
| `none` | one note: checked against so many issues |
The judgement is kept on the issue (`triage`) and said as the event `triaged` before anything else, then applied
through the module's own `note`, `set`, `link` and `move`, each recorded as by `triage` and each only when not
done yet:
The older issue never moves. The verdict note is written first, its first line in a fixed form (`Duplicate
check: duplicate of N.`), so a second delivery of the event, or the backlog's pass, does only the steps not yet
done and never judges again. A judgement that fails — no answer, a late one, an answer outside the shape, an
issue not offered — leaves one note saying why, and the event is not taken: the bus offers it again, and after
its fifth delivery the controller raises `max-deliveries` on the module's consumer and keeps the event for a
person to deliver again.
1. one note 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 the given value stands;
2. each empty value filled — never one a person gave;
3. each `depends-on` and `relates-to` link added, unless it is there, would close a cycle, or its target is
resolved or wontfix; the older issue gains a note;
4. a certain duplicate: linked `duplicates`, its title and report noted on the older, moved to `wontfix`
"duplicate of N" last. The older issue never moves.
`groom` runs the same judgement over every issue not resolved and not yet checked, oldest first, against the
older ones not resolved and not found a duplicate in the same pass. It writes only the pass, in the state
`grooming` (the last pass, one key), and applies that pass's findings on `confirm` — never a fresh judgement.
A finding whose issues have moved on is skipped and said. It adds no "none" note to the backlog.
A second delivery of the event finds the kept judgement and finishes what is missing without judging again. A
judgement that fails leaves one note, and the event is not taken: after its fifth delivery the controller raises
`max-deliveries` on the module's consumer. Notes the duplicate check of ADR 0278 wrote as `duplicate-check` are
still read as that issue's verdict.
The tracker asks one judgement at a time; the pass waits while a new issue's check is waiting, and one verdict is
applied at a time. A check may take up to a minute (two judgements of at most 25 seconds each), which the tool
runner allows a handler while keeping its event alive (hq issue 387).
**Ready.** An issue is ready when it is not resolved or wontfix and every issue it depends on is wontfix, or
resolved with every pull request its `fixed-by` names (`<repository>#<number>`) delivered. The module consumes
`mesh-delivery.transition` and marks a pull request delivered on every issue naming it; at its own `moved` to
resolved it asks `seat:mesh-delivery.checks` once for what it has not heard. An issue made ready gains a note and
the event `ready`. `get` answers `waits-for`, `needed-by` and `ready`; `list` filters on `severity`, `needs` and
`ready` — an agent looking for work asks `severity=minor`, `needs=none`, `ready=true`. `set` changes severity,
needs and suggested owner as a person decides. `link` refuses a `depends-on` that would close a cycle.
`groom` triages every issue not resolved and not yet triaged, oldest first, against the older ones not resolved
and not found a duplicate in the same pass. It writes only the pass, in the state `grooming` (the last pass, one
key), each finding saying what applying it would do, and applies exactly that pass on `confirm`, each issue
checked again. One judgement runs at a time; the pass waits while a new issue's triage waits. A triage may take up
to two minutes, which the tool runner allows a handler while keeping its event alive (hq issue 387).
The new values are optional keys of an issue's one JSON value: an issue kept before reads as having none; the
key-value state has no schema to migrate.
## The import of hq's issues (slice 3)
@@ -159,8 +173,8 @@ verb takes an argument naming a caller, and none can be given one.
## Settings
`duplicate-check` (`on`; `off` stops the check on new issues) and `judge-model` (`sonnet`; `haiku` or `opus`)
steer the duplicate check.
`duplicate-check` (`on`; `off` stops the triage of new issues), `judge-model` (`sonnet`; `haiku` or `opus`) and
`forge-owner` (the forge's owner of the repositories `fixed-by` names, for asking a delivery) steer the triage.
```
{