102 lines
6.6 KiB
Markdown
102 lines
6.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 and the status
|
|
|
|
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.
|
|
|
|
**The tracker is the truth for every issue's status, owner, fix and links**, whether or not the issue has a folder
|
|
here. `issue-tracker.get NNN` says where an issue stands; `issue-tracker.list` says what is open. Nothing here
|
|
says it: a report's frontmatter carries no status.
|
|
|
|
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 folder holds the report and the diagnosis; the tracker holds everything else.
|
|
- **Wholly in the tracker** — every other issue. Nothing is written here.
|
|
|
|
**The reports written before the tracker** (the folders numbered 001 to 364) still carry `status:`, `located-in:`,
|
|
`fixed-by:`, `amended-design:`, `replay:` and `replay-none:` in their frontmatter. Those keys are the snapshot the
|
|
import of slice 3 took from `main` on the day it ran (the note in ADR 0275 says which day and which commit), and
|
|
they are not kept up to date: the tracker is. They are left in place because they are the record of what was known
|
|
then, reviewed in pull requests at the time, and because removing 361 keys would say nothing the tracker does not.
|
|
Nobody edits them.
|
|
|
|
## 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
|
|
---
|
|
opened: YYYY-MM-DD
|
|
---
|
|
```
|
|
|
|
Then `# NNN. The title`, the title as the tracker holds it, and the symptom as observed, as in the tracker's
|
|
report. **No status key**: `cycle.py` refuses `status:`, `located-in:`, `fixed-by:`, `amended-design:`,
|
|
`replay:` or `replay-none:` in a report opened after the import, because the tracker holds them and two places
|
|
holding one fact drift.
|
|
|
|
**An issue wholly in the tracker** is done at this step.
|
|
2. Investigate. The trail goes in `issue-tracker.note`, and, for a folder-backed issue, in `01-diagnosis.md` in the
|
|
same folder as well — dated, including what was ruled out. The status moves through `issue-tracker.move`:
|
|
to `diagnosing`, then to `located` with `located-in`, which the tracker refuses without a registered owner.
|
|
3. Resolve. `issue-tracker.move` to `resolved` with `fixed-by`, and, if the root cause was a design gap,
|
|
run playbook [02](02-graduation.md) and give `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));
|
|
`issue-tracker.move` refuses the move without it. `wontfix` needs a `reason`.
|
|
|
|
## 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.
|
|
- `wontfix` is legitimate and requires a sentence saying why; the tracker refuses it without one.
|
|
- **A rule states how it is checked.** The cycle's rules on an issue — a status one of the five, `located` with an
|
|
owner, `resolved` with a fix, a core issue with its replay, `wontfix` with a reason — are refused at the write by
|
|
`issue-tracker.move` (mesh-catalog `modules/mesh-issues`, its table test over the rules). `cycle.py` checks what
|
|
this repository still holds: no two folders share a number, every report has frontmatter, and a report opened
|
|
after the import carries no status key.
|