An issue's status lives in the issue tracker: playbook 03, cycle.py and the notes slice 3 of ADR 0275 asked for
This commit is contained in:
@@ -23,14 +23,17 @@ result is not "nothing to learn" — it is the reason the next person repeats th
|
||||
|
||||
## Steps
|
||||
|
||||
1. Read `00-report.md`. Move `status:` to `diagnosing`.
|
||||
2. Investigate. Write `01-diagnosis.md` in the same folder: the trail, **dated**, including
|
||||
what was ruled out and how. Archaeology — which commit, which pull request, which date a
|
||||
behaviour changed — is the most valuable content here.
|
||||
3. When the owner is known, set `status: located` and fill `located-in:` with repositories or
|
||||
modules from [`00-META/repos.md`](../../../00-META/repos.md).
|
||||
4. On resolution: `status: resolved`, fill `fixed-by:`. If the root cause was a design gap, run
|
||||
`hq-graduate` for the amendment and fill `amended-design:`.
|
||||
1. Read the issue: `mesh_call issue-tracker.get` with its number, and `00-report.md` when it has a
|
||||
folder. Move it to `diagnosing` with `issue-tracker.move`.
|
||||
2. Investigate. The trail goes in `issue-tracker.note`, **dated** in its text, including what was
|
||||
ruled out and how; for an issue with a folder, in `01-diagnosis.md` there as well. Archaeology —
|
||||
which commit, which pull request, which date a behaviour changed — is the most valuable content.
|
||||
3. When the owner is known, `issue-tracker.move` to `located` with `located-in`: entries of the
|
||||
tracker's registered list of owners (repositories, and modules as `mesh-catalog modules/<module>`);
|
||||
the tracker refuses an owner it does not know.
|
||||
4. On resolution: `issue-tracker.move` to `resolved` with `fixed-by`, and `replay` or `replay-none`
|
||||
for a core issue. If the root cause was a design gap, run `hq-graduate` for the amendment and give
|
||||
`amended-design`. Nothing of this is written in the report's frontmatter.
|
||||
|
||||
## Rules
|
||||
|
||||
|
||||
@@ -46,16 +46,14 @@ Decide the shape first:
|
||||
|
||||
```yaml
|
||||
---
|
||||
status: open
|
||||
opened: YYYY-MM-DD
|
||||
located-in: []
|
||||
fixed-by:
|
||||
amended-design:
|
||||
---
|
||||
```
|
||||
|
||||
Its status stays in this frontmatter until slice 3 of to-be 53 imports the folders (ADR 0275);
|
||||
`issue-tracker.move` refuses it until then. Notes and links may still go to the tracker.
|
||||
then `# NNN. The title`, the title given to the tracker. **No status key**: the issue's status, owner and fix
|
||||
live in the tracker (ADR 0275), and `cycle.py` refuses `status:`, `located-in:`, `fixed-by:`,
|
||||
`amended-design:`, `replay:` or `replay-none:` in a report opened after the import. Notes and links go to the
|
||||
tracker.
|
||||
4. Write the symptom **as observed**, in plain terms, with the evidence that it happened.
|
||||
5. Say why it matters beyond the instance. An issue that is only one occurrence is a knowledge
|
||||
base entry.
|
||||
|
||||
@@ -16,12 +16,14 @@ generated on demand and never written back to disk.
|
||||
| Research | `01-RESEARCH/NNN-*/00-overview.md` | `status` (`active` / `graduated` / `abandoned`), `initiated`, `touches`, `became` |
|
||||
| Design | `03-DESIGN/**/*.md` (not READMEs) | `layer` (`as-is` / `to-be`), `status` (`designed` / `in-progress` / `implemented` / `abandoned`), `code`, `updated`, `decisions` |
|
||||
| Decisions | `02-DECISIONS/NNNN-*.md` | `status` (`proposed` / `accepted` / `superseded`), `date`, `deciders`, `reconstructed`, `superseded-by`, `extends` |
|
||||
| Issues | `04-ISSUES/NNN-*/00-report.md` | `status` (`open` / `diagnosing` / `located` / `resolved` / `wontfix`), `opened`, `located-in`, `fixed-by`, `amended-design` |
|
||||
| Issues | the issue tracker, not a file: `mesh_call issue-tracker.list` (optionally `status`, `owner`, `phrase`) and `issue-tracker.get` | `status` (`open` / `diagnosing` / `located` / `resolved` / `wontfix`), `opened`, `located-in`, `fixed-by`, `amended-design`, `record` (the folder here, when it has one) |
|
||||
|
||||
## Steps
|
||||
|
||||
1. Read the frontmatter block from every file above — a grep across each tree is enough, no
|
||||
need to load bodies.
|
||||
need to load bodies — and the issues from the tracker. The `status:` keys the reports numbered
|
||||
001 to 364 still carry are the import's snapshot of 2026-10-10 (ADR 0275), not where an issue
|
||||
stands.
|
||||
2. Render what was asked as Markdown tables. Group and sort sensibly. The **ADR index** is one
|
||||
of these views, and it is the only place the list of records is shown
|
||||
([ADR 0248](../../../02-DECISIONS/0248-the-decision-index-is-generated-when-it-is-read-and-never-stored.md)):
|
||||
@@ -44,4 +46,5 @@ generated on demand and never written back to disk.
|
||||
already drifted after a single addition, which is why it was removed, and the generated one
|
||||
written into `02-DECISIONS/README.md` made every two open decisions conflict (issue 297), which
|
||||
is why that went too. `index.py` fails if the list is stored there again.
|
||||
- Do not infer status from prose. Trust only the frontmatter; if it is wrong, flag it.
|
||||
- Do not infer status from prose. Trust only the frontmatter, and the tracker for an issue; if
|
||||
either is wrong, flag it.
|
||||
|
||||
+29
-39
@@ -4,7 +4,7 @@
|
||||
The knowledge flow (00-META/process/00-overview.md) says work moves idea -> research ->
|
||||
decision -> to-be design -> code, and symptom -> issue -> diagnosis -> fix. Those are rules,
|
||||
and a rule states how it is checked (AGENTS.md) -- this is how. Everything here reads only
|
||||
frontmatter, because status lives in frontmatter and nowhere else.
|
||||
frontmatter, because a status this repository holds lives in frontmatter and nowhere else.
|
||||
|
||||
What is enforced:
|
||||
|
||||
@@ -12,15 +12,15 @@ What is enforced:
|
||||
known `status:`. A TO-BE doc names at least one decision (`decisions:`) -- no
|
||||
design without a decision -- and once `in-progress` or `implemented` it names
|
||||
its owning code (`code:`) -- no development without a design that says where.
|
||||
issues a known `status:`; once `located`, `located-in:` names the owner;
|
||||
once `resolved`, `fixed-by:` says what fixed it (prose counts --
|
||||
"nothing, the capability existed" is an answer). And no two records share a
|
||||
number -- the number is how a record is cited.
|
||||
replays a core issue -- one opened from 2026-10-07 whose `located-in:` names a core repository --
|
||||
resolves only with `replay:` (the replay's id in mesh-lab's replays register) or
|
||||
`replay-none:` (why no replay is possible): ADR 0237, to-be 45 §9. A replay is what
|
||||
proves a fix fails before and passes after; without one, a fixed class comes back
|
||||
through a different door (research 031: four such chains in one week).
|
||||
issues every report has frontmatter, and no two records share a number -- the number is
|
||||
how a record is cited. An issue's status, owner, fix and replay live in the issue
|
||||
tracker since the import of slice 3 (ADR 0275, to-be 53), where `issue-tracker.move`
|
||||
refuses at the write what this check refused at merge until then: a status outside
|
||||
the five, `located` without an owner, `resolved` without a fix, a core issue
|
||||
resolved without its replay (ADR 0237), `wontfix` without a reason. A report opened
|
||||
after the import carries none of those keys -- the tracker holds them, and two
|
||||
places holding one fact drift. The reports opened before keep theirs as the dated
|
||||
snapshot the import took; they are not judged, and not kept up to date.
|
||||
research a known `status:`; a `graduated` overview says what it `became:`, and every
|
||||
target it names exists.
|
||||
decisions every accepted record is REACHABLE from the cycle: cited by a design doc's
|
||||
@@ -28,9 +28,8 @@ What is enforced:
|
||||
record's extends/supersedes chain. A decision nothing points at is one nobody will
|
||||
find by following pointers -- which is how records go stale in people's heads.
|
||||
|
||||
Deliberately NOT enforced: `resolved` issues may leave `located-in` empty (a symptom that
|
||||
turned out not to be a defect has no owner), and as-is docs need no decisions (they
|
||||
describe what exists, not what was decided).
|
||||
Deliberately NOT enforced: as-is docs need no decisions (they describe what exists, not what
|
||||
was decided).
|
||||
|
||||
python3 00-META/checks/cycle.py
|
||||
"""
|
||||
@@ -43,18 +42,14 @@ import sys
|
||||
ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||
|
||||
DESIGN_STATUSES = {"proposed", "designed", "in-progress", "implemented", "abandoned"}
|
||||
ISSUE_STATUSES = {"open", "diagnosing", "located", "resolved", "wontfix"}
|
||||
RESEARCH_STATUSES = {"active", "graduated", "abandoned"}
|
||||
|
||||
# The core, as to-be 45 names it, by where its code lives: the controller, the node-engine, the node
|
||||
# tools and the console, the SDK's loops, and the catalogue's bus, forge (the announcer of merges) and
|
||||
# build agent. A `located-in:` entry naming any of these makes an issue a core issue.
|
||||
CORE = re.compile(r"\b(mesh-controller|mesh-host|mesh-tools|mesh-sdk|node-tools|"
|
||||
r"mesh-catalog\s+modules/(nats|gitea|build-agent))\b")
|
||||
|
||||
# The day the rule began (ADR 0237): issues opened before it are not held to it.
|
||||
REPLAYS_FROM = "2026-10-07"
|
||||
REPLAY_ID = re.compile(r"^R\d+\b")
|
||||
# The keys an issue report carried while its status lived here, and the day from which a report carries
|
||||
# none of them: the issue tracker holds them (ADR 0275, slice 3 of to-be 53), and `issue-tracker.move`
|
||||
# enforces the cycle's rules on them at the write. A report opened on or after this day with one of these
|
||||
# keys is a status written in two places. The reports opened before keep theirs as the import's snapshot.
|
||||
STATUS_IN_TRACKER_FROM = "2026-10-11"
|
||||
TRACKER_KEYS = ("status", "located-in", "fixed-by", "amended-design", "replay", "replay-none")
|
||||
|
||||
# A decision that defers work names the issue that tracks it (issue 354). ADR 0244 left the code
|
||||
# renames of the node-engine and the tool runner "for a later rename", with no issue, owner or date;
|
||||
@@ -231,28 +226,23 @@ def main():
|
||||
else:
|
||||
seen_records[number] = name
|
||||
|
||||
# Since the import of slice 3 (ADR 0275), an issue's status, owner, fix and replay live in the issue
|
||||
# tracker, and its `move` refuses at the write what this check refused at merge. A report opened from
|
||||
# STATUS_IN_TRACKER_FROM carries none of those keys; one opened before keeps the import's snapshot.
|
||||
for path in sorted(glob.glob(os.path.join(ROOT, "04-ISSUES", "*", "00-report.md"))):
|
||||
front = frontmatter(path)
|
||||
if front is None:
|
||||
bad(path, "no frontmatter")
|
||||
continue
|
||||
status = front.get("status")
|
||||
if status not in ISSUE_STATUSES:
|
||||
bad(path, "status %r is not one of %s" % (status, sorted(ISSUE_STATUSES)))
|
||||
if status in ("located", "resolved") and not listy(front, "located-in"):
|
||||
bad(path, "status %s but located-in is empty" % status)
|
||||
if status == "resolved" and not listy(front, "fixed-by"):
|
||||
bad(path, "status %s but fixed-by says nothing" % status)
|
||||
# A core issue resolves with its replay, or with why none is possible (ADR 0237).
|
||||
replay = " ".join(listy(front, "replay"))
|
||||
if replay and not REPLAY_ID.match(replay):
|
||||
bad(path, "replay: %r names no replay -- an id from mesh-lab's replays register, R<issue>" % replay)
|
||||
core = any(CORE.search(entry) for entry in listy(front, "located-in"))
|
||||
opened = str(front.get("opened") or "")
|
||||
if (status == "resolved" and core and opened >= REPLAYS_FROM and not replay
|
||||
and not " ".join(listy(front, "replay-none")).strip()):
|
||||
bad(path, "a core issue resolved with no replay: name it in `replay:` (mesh-lab replays "
|
||||
"register.go) or say in `replay-none:` why none is possible (ADR 0237)")
|
||||
if not opened:
|
||||
bad(path, "opened: says nothing -- the day the issue was opened, YYYY-MM-DD")
|
||||
continue
|
||||
carried = [key for key in TRACKER_KEYS if key in front]
|
||||
if opened >= STATUS_IN_TRACKER_FROM and carried:
|
||||
bad(path, "carries %s, but an issue's status, owner, fix and replay live in the issue tracker "
|
||||
"(ADR 0275): move it with `issue-tracker.move`, and keep no copy here"
|
||||
% ", ".join("`%s:`" % key for key in carried))
|
||||
|
||||
# ---- research ----------------------------------------------------------------------
|
||||
for path in sorted(glob.glob(os.path.join(ROOT, "01-RESEARCH", "*", "00-overview.md"))):
|
||||
|
||||
@@ -60,17 +60,22 @@ the expensive half.
|
||||
|
||||
The flow above is a rule, and a rule states how it is checked:
|
||||
[`00-META/checks/cycle.py`](../checks/cycle.py) refuses a to-be design that names no
|
||||
decision, an `in-progress`/`implemented` design that names no owning code, an issue marked
|
||||
`located`/`fixed` with no owner or `fixed`/`resolved` with no fix, and a `graduated`
|
||||
decision, an `in-progress`/`implemented` design that names no owning code, two issue folders
|
||||
sharing a number or an issue report carrying a status key, and a `graduated`
|
||||
research overview that does not say what it became. Run it with `records.py`, `index.py` and
|
||||
`words.py` before any HQ merge (`sh merge-check.sh` runs all four). What the checks cannot see — that code work actually started from a
|
||||
`words.py` before any HQ merge (`sh merge-check.sh` runs all four). The issue rules the check
|
||||
once held — `located` with an owner, `resolved` with a fix, a core issue with its replay — are
|
||||
refused at the write by the issue tracker's `move`
|
||||
([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)). What the checks cannot see — that code work actually started from a
|
||||
handoff — is held by playbooks [04](04-build-handoff.md) and [07](07-feature-branches.md):
|
||||
a feature branch exists because a design or an issue sent it.
|
||||
|
||||
## Status lives in frontmatter
|
||||
## Status lives in frontmatter, an issue's in the tracker
|
||||
|
||||
Research overviews, design docs, issue reports and decision records each carry their status as
|
||||
YAML frontmatter (schemas in the section READMEs and playbooks). There are **no central status
|
||||
Research overviews, design docs and decision records each carry their status as
|
||||
YAML frontmatter (schemas in the section READMEs and playbooks). An issue's status lives in the
|
||||
issue tracker, which refuses a wrong status at the write, and its report here carries only
|
||||
the day it was opened (playbook [03](03-issues.md)). There are **no central status
|
||||
files** and no decision ledger. **Every decision is a record in
|
||||
[`02-DECISIONS`](../../02-DECISIONS/)** — if it is worth recording it is worth a record, and if
|
||||
it is not worth a record it is not recorded. Cross-cutting views, the decision index included,
|
||||
|
||||
@@ -19,7 +19,7 @@ The knowledge base already holds the operational record and is indexed on sympto
|
||||
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
|
||||
## 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),
|
||||
@@ -28,16 +28,22 @@ compare-and-set, so two authors opening at the same moment never get the same on
|
||||
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 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.
|
||||
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.
|
||||
|
||||
No status is ever kept in two places for one issue.
|
||||
**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
|
||||
|
||||
@@ -52,34 +58,31 @@ No status is ever kept in two places for one issue.
|
||||
|
||||
```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.
|
||||
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: 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`
|
||||
**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);
|
||||
`cycle.py` checks it on a folder, and `issue-tracker.move` refuses the move without it).
|
||||
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
|
||||
- `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
|
||||
@@ -90,4 +93,9 @@ No status is ever kept in two places for one issue.
|
||||
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.
|
||||
- `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.
|
||||
|
||||
@@ -114,6 +114,16 @@ documents had failed.
|
||||
**Every document carries its state in YAML frontmatter** — research overviews, design documents,
|
||||
decision records, issue reports.
|
||||
|
||||
> **Progressive insight — 2026-10-10.** For issue reports this stopped holding the day slice 3 of
|
||||
> [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)
|
||||
> landed: an issue's status, owner and fix live in the issue tracker, a module of the mesh, which refuses
|
||||
> a wrong status at the write; a report here carries the day it was opened and nothing of its status, and
|
||||
> `cycle.py` refuses a status key in a report opened after the import. The reports written before keep
|
||||
> their keys as a dated snapshot. The reason stands exactly as written — one place, never two — and the
|
||||
> one place for an issue is now the tracker, because a file every branch may edit was the place where
|
||||
> numbers collided and statuses were hard to see. Research overviews, design documents and decision
|
||||
> records carry their state here as before.
|
||||
|
||||
**There are no central status files.** Every cross-cutting view — a status matrix, a decision
|
||||
index, an open-issue list — is generated from frontmatter when asked for, never written to disk.
|
||||
Two places holding one fact drift, and the written one wins by being closer to hand.
|
||||
|
||||
@@ -46,6 +46,16 @@ The development cycle is enforced mechanically, to the extent frontmatter can ca
|
||||
> refuses it now. The decision here stands exactly as written: this is one more thing frontmatter
|
||||
> and file names can carry, found by its absence rather than by reasoning.
|
||||
|
||||
> **Progressive insight — 2026-10-10.** The third rule above, no owner-less diagnosis and no fix-less
|
||||
> fix, and the replay rule `cycle.py` took on under ADR 0237, moved out of `cycle.py` the day slice 3 of
|
||||
> [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)
|
||||
> landed: an issue's status lives in the issue tracker, whose `move` refuses at the write what the
|
||||
> check refused at merge — `located` without an owner, `resolved` without a fix, a core issue without
|
||||
> its replay, `wontfix` without a reason. Enforced mechanically still, one step earlier, in the one
|
||||
> place the status is. `cycle.py` keeps the fifth rule, no two records answering to one number, and
|
||||
> refuses a status key in a report opened after the import. The decision stands: the cycle is checked,
|
||||
> not trusted; which program checks each rule is a fact that moved.
|
||||
|
||||
[`00-META/checks/cycle.py`](../00-META/checks/cycle.py) refuses violations, beside `records.py`
|
||||
and `index.py`; all three run before any merge here. What frontmatter cannot see — that code work
|
||||
actually started from a handoff — remains held by playbooks 04 and 07: a feature branch exists
|
||||
|
||||
+17
-1
@@ -139,12 +139,28 @@ along the way".
|
||||
> What slice 1 added is the SDK's `Increment`, which reads a counter and writes the next number only at the revision
|
||||
> it read. The decision stands as written.
|
||||
|
||||
> **Progressive insight — 2026-10-10, slice 3.** The import is built: `issue-tracker.import` reads every issue
|
||||
> folder through the records module over the bus, at the commit its checkout stands at, and writes each as an issue
|
||||
> at its own number — create-if-absent on the key, the counter untouched, never an overwrite, a second run changing
|
||||
> nothing and saying so; `move` no longer refuses an issue with a folder. Its dry run read `main` at commit
|
||||
> `1ea678c6bd48f62a45db3b5e4b21b9c46867d31a` on 2026-10-10: 361 folders, all readable — 27 `open`, 1 `diagnosing`,
|
||||
> 48 `located`, 284 `resolved`, 1 `wontfix` — and 29 `located-in` entries naming no registered owner, kept as
|
||||
> written and marked unverified (issue 366 owns the list). The operator runs the import once after the rollout; the
|
||||
> commit it read is on every imported issue (`imported.commit`) and in its `imported` events, where the mesh keeps
|
||||
> its own truth. Two facts differ from §8 as written. **The status keys stay in the reports**: removing 361 keys
|
||||
> would say nothing the tracker does not, and lose the record of what was known when; they are the dated snapshot
|
||||
> of that day, not kept up to date, and `cycle.py` refuses them in a report opened from 2026-10-11 on. **The four
|
||||
> consolidation passes and the audit of `fixed-by`** (to-be 53 §8) are not in the import: each is a list the
|
||||
> operator confirms before it is applied, done through `move`, `link` and `note` on the imported issues, not by
|
||||
> the import, which only ever creates. The decision stands as written.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Collisions end at the source: a number is taken from one counter, not read from a repository many branches write.
|
||||
- Until the slice 3 import has run and been checked against it, an issue that has a folder here keeps its status in
|
||||
that folder's frontmatter, and the tracker holds its number, title and folder only. An issue without a folder is
|
||||
whole in the tracker from the start. No status is written in two places for the same issue at any time.
|
||||
whole in the tracker from the start. No status is written in two places for the same issue at any time. (Since
|
||||
slice 3's pull request, dated in the insight above: every issue is whole in the tracker.)
|
||||
- [ADR 0019](0019-how-this-repository-works.md)'s rule that statuses live only in frontmatter stops holding for issues
|
||||
on the day slice 3's pull request lands, and part of [ADR 0080](0080-the-development-cycle-is-checked.md)'s check
|
||||
moves from `cycle.py` into the tracker's `move`. That pull request adds a dated note to each of them saying where
|
||||
|
||||
@@ -63,6 +63,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 |
|
||||
| `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 |
|
||||
|
||||
Each verb's description says in plain words what it does and what it refuses, without the mesh's internal names, so
|
||||
an agent picks the right one from `mesh_search` alone. A refusal says what was wrong and what would be accepted.
|
||||
@@ -84,10 +85,10 @@ caller; the module records that identity as who acted. No verb takes an actor.
|
||||
other module setting. Slice 1 starts it with the repositories in hq's `00-META/repos.md` and the modules the catalogue
|
||||
registers.
|
||||
|
||||
**An issue that has a folder in hq** keeps its status in that folder's frontmatter until the import of slice 3 has run
|
||||
and been checked. Until then the tracker holds its number, title and `record`, takes its notes and links, and `move`
|
||||
refuses it with the reason: its status lives in its folder. An issue without a folder is whole in the tracker from the
|
||||
start. No status is written in two places for one issue.
|
||||
**An issue that has a folder in hq** kept its status in that folder's frontmatter until the import of slice 3 ran;
|
||||
until then the tracker held its number, title and `record`, took its notes and links, and `move` refused it with the
|
||||
reason: its status lives in its folder. Since the import, every issue is whole in the tracker, folder or not, and a
|
||||
report in hq carries no status key. No status is written in two places for one issue.
|
||||
|
||||
## 4. State, the counter and events
|
||||
|
||||
@@ -136,8 +137,8 @@ the events sees a change as it happens. No event carries a secret, and an issue
|
||||
| `resolved`, for 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) | `replay` (an id from mesh-lab's replays register) or `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)) |
|
||||
| `wontfix` | a reason |
|
||||
|
||||
A refused move changes nothing and says which rule refused it. `cycle.py` keeps checking the folders that still carry
|
||||
status until slice 3.
|
||||
A refused move changes nothing and says which rule refused it. `cycle.py` checked the folders that carried status until
|
||||
slice 3; since then it refuses a status key in a report opened after the import, and judges no issue's status.
|
||||
|
||||
## 6. How the operator and agents use it
|
||||
|
||||
@@ -164,7 +165,7 @@ in one call, from any session, and the responder on a channel answers from it on
|
||||
|---|---|---|
|
||||
| 1 | The `mesh-issues` module: `open`, `get`, `list`, `move`, `note`, `link`; a counter by compare-and-set in the SDK; playbook 03 takes its numbers from it | an agent opens an issue and gets the next number; two agents opening at the same moment never get the same one |
|
||||
| 2 | Decision and research numbers from the tracker, each its own key in `counters`; a number is reserved when a branch starts | two agents writing a decision at once get two numbers, and no hq pull request is renumbered |
|
||||
| 3 | `import`: every issue folder in hq read through the records module and written as an issue with its own number, idempotent and showing what it would change before it does; the four consolidation passes of research 042 (fixed, duplicates, owners, grouping), each a list confirmed before it is applied; an audit of `fixed-by` against the forge; then one pull request removes the status keys from every report, changes playbook 03 and `cycle.py`, and adds dated notes to ADR 0019 and 0080 | the not-closed list comes from one call and shrinks by what was already fixed or duplicated, each with its reason |
|
||||
| 3 | `import`: every issue folder in hq read through the records module and written as an issue with its own number, idempotent and showing what it would change before it does (built 2026-10-10; the status keys stay in the reports as a dated snapshot, and one pull request changes playbook 03 and `cycle.py` and adds dated notes to ADR 0019, 0080 and 0275); then the four consolidation passes of research 042 (fixed, duplicates, owners, grouping), each a list confirmed before it is applied through `move`, `link` and `note`, and an audit of `fixed-by` against the forge | the not-closed list comes from one call and shrinks by what was already fixed or duplicated, each with its reason |
|
||||
| 4 | Conditions and pull requests linked: an issue links a condition by its key and the controller shows the link; a merged pull request notes every issue it names; after a week of that, the controller opens one issue per `healer-wanted` condition key by itself | a condition shows its issue, and a merge notes the issues it names |
|
||||
| 5 | A read-only view onto the `issues` bucket over the bus | the operator reads issues in a browser without an agent |
|
||||
|
||||
|
||||
+10
-5
@@ -22,22 +22,27 @@ both.
|
||||
|
||||
```
|
||||
NNN-short-name/
|
||||
00-report.md the symptom as observed, with the evidence; status in frontmatter
|
||||
00-report.md the symptom as observed, with the evidence
|
||||
01-diagnosis.md the investigation trail, dated, including what was ruled out
|
||||
```
|
||||
|
||||
`NNN` is the number the issue tracker gave. The issue's status, owner (`located-in`), fix (`fixed-by`),
|
||||
design amendment and replay live in the tracker — `issue-tracker.get NNN` — not here
|
||||
([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)).
|
||||
|
||||
## Frontmatter, on `00-report.md`
|
||||
|
||||
```yaml
|
||||
---
|
||||
status: open | diagnosing | located | resolved | wontfix
|
||||
opened: YYYY-MM-DD
|
||||
located-in: [] # owning repo(s) or module(s), filled by diagnosis
|
||||
fixed-by: # pull request or commit reference, filled at resolution
|
||||
amended-design: # design doc path, when the root cause was a design gap
|
||||
---
|
||||
```
|
||||
|
||||
The report's first heading is `# NNN. The title`, the title the tracker holds. A report opened after the
|
||||
import of slice 3 carries no `status:`, `located-in:`, `fixed-by:`, `amended-design:`, `replay:` or
|
||||
`replay-none:`; `cycle.py` refuses them. The folders numbered 001 to 364 keep those keys as the snapshot the
|
||||
import took, dated in ADR 0275; they are not kept up to date, and nobody edits them.
|
||||
|
||||
## Rules
|
||||
|
||||
- Anyone may open an issue. No localisation is required to report one.
|
||||
|
||||
@@ -32,12 +32,13 @@ alongside `records.py`, `index.py` and `words.py` — run all four before any me
|
||||
| How do I do X in this repo? | [`00-META/process/`](00-META/process/) — the playbook index is in `00-overview.md` |
|
||||
| What was decided, and why? | [`02-DECISIONS/README.md`](02-DECISIONS/README.md) (reading order), then the record |
|
||||
| What is being built / already runs? | [`03-DESIGN/01-to-be/`](03-DESIGN/01-to-be/) / [`03-DESIGN/00-as-is/`](03-DESIGN/00-as-is/) — each doc's frontmatter says its status, decisions and owning code |
|
||||
| What is broken or was? | [`04-ISSUES/`](04-ISSUES/) — frontmatter carries status/owner/fix |
|
||||
| What is broken or was? | the issue tracker: `issue-tracker.list` and `issue-tracker.get` through the mesh MCP server; a design or governance issue's narrative is in [`04-ISSUES/`](04-ISSUES/) under its number |
|
||||
| Which repo owns what code? | [`00-META/repos.md`](00-META/repos.md) |
|
||||
| Cross-cutting status view? | the `hq-status` skill (generated, never stored) |
|
||||
|
||||
Statuses live **only** in frontmatter; follow the pointers there (`decisions:`, `code:`,
|
||||
`became:`, `fixed-by:`) instead of reconstructing history from memory.
|
||||
Statuses of research, designs and decisions live **only** in frontmatter; follow the pointers there
|
||||
(`decisions:`, `code:`, `became:`) instead of reconstructing history from memory. An issue's status,
|
||||
owner and fix live in the issue tracker ([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)).
|
||||
|
||||
## Words
|
||||
|
||||
@@ -54,8 +55,9 @@ in running prose; a quotation keeps its words.
|
||||
runs them on every pull request as its `mesh/repo-check` ([ADR 0238](02-DECISIONS/0238-a-commit-is-the-build-at-hand-one-commit-one-change-plan-checked-off-the-trunk-and-published-only-on-it.md)).
|
||||
No new top-level folders without explicit confirmation.
|
||||
- **Status lives in YAML frontmatter** — on research overviews (`status`, `became`), design
|
||||
docs (`layer`, `status`, `code`, `updated`), issue reports (`status`, `located-in`,
|
||||
`fixed-by`, `amended-design`) and decision records (`status`, `date`, `deciders`).
|
||||
docs (`layer`, `status`, `code`, `updated`) and decision records (`status`, `date`, `deciders`).
|
||||
An issue's status, owner and fix live in the issue tracker, not here: an issue report carries
|
||||
`opened` and nothing of its status (playbook [03](00-META/process/03-issues.md)).
|
||||
Never create a central status file; cross-cutting views are generated from frontmatter.
|
||||
- **`02-DECISIONS/` records hold their meaning.** Supersede with a new record rather than rewriting
|
||||
what was decided, the options weighed, or a consequence another record relies on. Fixing a broken
|
||||
|
||||
Reference in New Issue
Block a user