Files
hq/00-META/process/03-issues.md
T
jochen beaeedca79
mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
Playbook 03 takes an issue's number from the issue tracker
Issue numbers collided while each author read main and the open pull
requests; since 2026-10-10 the tracker (ADR 0275) hands them out. The
playbook and the hq-new-issue skill now open every issue there, keep a
folder only for a design or governance issue, and say where its status
lives until slice 3. Research 042's finding that the SDK had no
compare-and-set is corrected, and to-be 53 names the module's code.
2026-10-10 13:06:13 +02:00

5.6 KiB

Playbook 03 — Issues

Trigger. Something is wrong at the level of the mesh's design or governance — a rule that turns out to be unenforced, a stated behaviour that does not happen, a silent failure the design permits.

Who runs it. Anyone may open an issue. No localisation is required to report one.

What belongs here, and what does not

Belongs in 04-ISSUES Belongs in the knowledge base
The design permits a failure to be silent How to fix one occurrence of it
A documented rule is enforced by nothing A command that works around it
A stated invariant is false in practice A node-specific quirk
The owning component is unknown and finding it needs the whole mesh in view Symptom → fix, once the answer is known

The knowledge base already holds the operational record and is indexed on symptoms. This folder is not a second copy of it. An issue here is a question HQ must answer, not an incident someone must clear.

The issue tracker holds the number

Every issue is opened in the issue tracker, the module mesh-issues holding the seat issue-tracker on the control-node (ADR 0275, to-be 53). Its number comes from one counter advanced by compare-and-set, so two authors opening at the same moment never get the same one. A number is never taken by reading main or the open pull requests — that is how numbers collided (issue 155: two records sharing a number reached main with every check passing). A session that cannot reach the tracker takes no number and says so.

An issue takes one of two shapes:

  • With a folder here — a design or governance issue, whose narrative belongs in this public record and is reviewed in a pull request. The tracker holds its number, title, notes and links; its status stays in the folder's frontmatter until slice 3 of to-be 53 has imported the folders and been checked, and until then the tracker's move refuses it, saying its status lives in its folder.
  • Wholly in the tracker — every other issue. Its status, owner, notes and links live in the tracker from the start, and nothing is written here.

No status is ever kept in two places for one issue.

Steps

  1. Open the issue in the tracker, through the mesh MCP server: mesh_call issue-tracker.open with title, report (the symptom as observed, in plain terms, with the evidence that it happened), optionally located-in (entries of the tracker's registered list of owners), and, for an issue that gets a folder here, record — the folder's name without its number, short-name. The tracker answers with the number NNN; a refused open spends none.

    An issue with a folder then creates 04-ISSUES/NNN-short-name/00-report.md under exactly that number, on a branch:

    ---
    status: open
    opened: YYYY-MM-DD
    located-in: []       # owning repo(s)/module(s), filled by diagnosis
    fixed-by:            # PR or commit reference, filled at resolution
    amended-design:      # design doc path, when the root cause was a design gap
    ---
    

    Then the symptom as observed, as in the tracker's report.

    An issue wholly in the tracker is done at this step: what follows happens through the tracker's verbs.

  2. Investigate. For a folder-backed issue, the trail goes in 01-diagnosis.md in the same folder — dated, including what was ruled out — and status: moves to diagnosing, then located once the owner is known. For an issue wholly in the tracker, the trail goes in issue-tracker.note and the status changes through issue-tracker.move, which refuses located without a registered owner in located-in.

  3. Resolve. Set status: resolved (in the frontmatter, or through issue-tracker.move for an issue wholly in the tracker), fill fixed-by:, and if the root cause was a design gap, run playbook 02 and fill amended-design:. A core issue — its located-in names the controller, the node-engine, the tool runner, the SDK, or the catalogue's bus, forge or build agent — resolves with replay:, the id of its replay in mesh-lab's replays register, proved to fail on the commit before the fix and pass on it, or with replay-none: saying why none is possible (ADR 0237; cycle.py checks it on a folder, and issue-tracker.move refuses the move without it).

Rules

  • Closed issues are never deleted — they are the mesh's symptom-to-component memory.
  • fixed-by: names something that will still exist: a commit or a pull request, never a branch. A branch is deleted when it merges, so a branch name there is a pointer that resolves to nothing by the time anybody follows it.
  • A fix that turns out to have broken something else is written back into the record that asked for it, pointing at the new issue. Somebody arriving at a record to learn why the code is the way it is must not have to already know there was a sequel.
  • A number never moves. A folder carries the number the tracker gave; a folder whose number collides with another (one numbered before the tracker, or by hand) is a fault, and cycle.py refuses two records sharing a number.
  • An issue whose answer is a general lesson should also be written to the knowledge base, so the next person searching a symptom finds it. Both, not either.
  • status: wontfix is legitimate and requires a sentence saying why.