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.
94 lines
5.6 KiB
Markdown
94 lines
5.6 KiB
Markdown
# 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](../../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),
|
|
[to-be 53](../../03-DESIGN/01-to-be/53-the-issue-tracker.md)). 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:
|
|
|
|
```yaml
|
|
---
|
|
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](02-graduation.md) 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](../../02-DECISIONS/0237-a-change-is-judged-against-the-mesh-that-runs-before-it-merges-on-the-build-seat.md);
|
|
`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.
|