mesh-issues: import hq's issue folders at their own numbers, and an issue with a folder is whole in the tracker (hq ADR 0275, to-be 53 slice 3)
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 delivered

This commit is contained in:
jochen
2026-10-10 16:00:19 +02:00
parent 1fcf46fbfc
commit d05308a419
19 changed files with 2243 additions and 49 deletions
+66 -7
View File
@@ -22,16 +22,66 @@ what it refuses.
| `move` | `number`, `status`, and what that status needs | changes the status under the development cycle's rules |
| `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 |
**The cycle's rules, at the write** (hq `00-META/checks/cycle.py`): a status is one of `open`,
**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`,
`diagnosing`, `located`, `resolved`, `wontfix`; `located` needs `located-in`; `resolved` needs
`located-in` and `fixed-by`; a core issue (located in the controller, the node-engine, the tool runner,
the SDK, or the catalogue's bus, forge or build agent) resolves with `replay` or `replay-none`; `wontfix`
needs a `reason`. A refused change changes nothing and says which rule refused it.
**An issue with a folder in hq** (`record` at open: the folder's name, without the number) keeps its
status in that folder's report until the import of slice 3. The tracker holds its number, title, folder,
notes and links, and `move` refuses it, saying where its status lives.
**An issue with a folder in hq** (`record` at open: the folder's name, without the number) is whole in the
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 import of hq's issues (slice 3)
`issue-tracker.import` reads every `04-ISSUES/NNN-slug/00-report.md` of hq **through the records module
over the bus** (`records_status`, `records_list`, `records_read`, called as this module through the SDK's
`Ask`), at the commit the records module's checkout stands at, and writes each as an issue **at its own
number**: `record` is the folder, the title is the report's heading, the report is the text under it, and
`status`, `opened`, `located-in`, `fixed-by`, `amended-design`, `replay` and `replay-none` are the
frontmatter's, as written — the status one to one, no cycle rule applied, because the folder's status is
the snapshot hq kept. The issues and decision records the report names (`issue 155`, `ADR 0275`,
`04-ISSUES/203-…`, `02-DECISIONS/0019-…`) become `relates-to` and `decision` links.
It is a verb on the seat, callable by any agent, because it can do only this:
- **It only creates an issue that is not there** (`create` on the key, refused when the key exists) **or
fills the status of one the tracker holds without a status** — an issue opened with a folder before this
slice, whose folder kept its status; one whose folder hq does not hold at the commit becomes `open`.
- **It never overwrites.** An issue the tracker holds differently is reported as `differs`, naming the
fields, and left alone: the tracker is the truth. An issue it holds the same is `same`. So a second run
changes nothing and says so.
- **It never touches the counter**, and refuses the whole run when a folder is numbered past the counter.
A folder numbered at or past the setting `first-number` without an issue in the tracker is refused on its
own: a folder from the tracker's time carries a number the tracker gave.
- **It cannot forge an issue's history.** An issue it writes is `opened-by: import`, its `opened` the date
the report gave, and it carries `imported: {at, by, commit, from}` — when, who ran the import (the bus
credential, `unproven` until the runtime proves it), the commit of hq and the document. Each is an
`imported` event. What it writes comes from hq's `main` as the records module holds it, which is reviewed
in pull requests.
- **What it keeps is bounded** as every verb's text is: the title 200 bytes, the report 64 KiB, one issue
256 KiB; a report that trips a credential's shape is refused. An entry of `located-in` or `fixed-by` hq
wrote is prose at times, so it is taken within a reason's bound (2000 bytes), not a line's.
- **An owner hq named that the registered list does not know is kept as written and marked**, in
`located-in-unverified` on the issue and `unverified-owners` in the answer, never refused: the list is
the setting's problem (hq issue 366), and a `move` that gives `located-in` anew checks every entry and
clears the mark.
- **With `dry-run`** it reads everything, writes nothing, and answers the same rows with `would-` on each
action. **With `commit`** it refuses a checkout standing elsewhere; it refuses a run during which the
checkout moved.
The answer is a row per folder — number, action (`create`, `fill`, `same`, `differs`, `refused`, with why),
status, title — the counts, the counts by status, what was odd (a title numbered otherwise than its
folder, a frontmatter key not imported, an `amended-design` that is no design document) and the unverified
owners. A call that outlives the tool runner's wait is still running: the import goes on, and the answer
is read back through the controller's `calls` verb by the call's id.
Before the module is rolled out, `mesh-issues dry-run <hq checkout> <settings.json>` at the terminal runs
the same dry run over a checkout on disk against an empty tracker, and prints the answer. It is not a way
to import: only the verb writes, and it reads the record over the bus.
**Every argument is read strictly.** One a verb does not take is refused, never dropped. Every field has a
size limit, a one-line field takes no control character, a folder's name is no path, and text shaped like
@@ -64,8 +114,8 @@ Two states, declared in the manifest and made by the controller; the module neve
(ADR 0201): `issues`, one key per issue by number, keeping 8 values per key; `counters`, one value per key.
Neither expires. One issue is kept in at most 256 KiB; a note that would pass it is refused.
Every change is an event: `mesh-issues.opened`, `.moved`, `.noted`, `.linked`, each with the number, what
changed, who changed it and when. The events are the audit trail. A change whose event the bus did not take
Every change is an event: `mesh-issues.opened`, `.moved`, `.noted`, `.linked`, `.imported`, each with the
number, what changed, who changed it and when. The events are the audit trail. A change whose event the bus did not take
is kept, and its answer says the event was not said (`announced: false`).
## Who acted
@@ -85,7 +135,9 @@ verb takes an argument naming a caller, and none can be given one.
```
- `first-number` — the number the first issue gets: the one after the highest issue number on hq's main
and every open branch at the moment the module is switched on. Read once.
and every open branch at the moment the module is switched on. Read once for the counter; the import
reads it as the boundary between the folders hq numbered before the tracker and the numbers the tracker
gave.
- `owners` — the registered list `located-in` may name: repositories, and modules as
`mesh-catalog modules/<module>`. An entry of `located-in` is an owner, or an owner followed by a space or a
slash and where in it (`mesh-controller internal/broker`, `mesh-catalog modules/nats/module.json`). A module
@@ -102,3 +154,10 @@ the test, two instances of this bundle (the test binary started again) speaking
test against the buckets the manifest declares, fifty opens at once, fifty distinct numbers from the first
in a row. It counts the writes lost to another opener, so it fails when the openers never collided. With
the counter written without its revision check, it fails.
The import is tested over `testdata/hq`, copies of seven real reports of hq chosen for their shapes (a
bracketed list with commas inside parentheses, quoted block items, an apostrophe in prose, a `resolved:`
date, a replay, an empty list, a wontfix): the frontmatter read as written, each issue created at its own
number with the counter untouched, a second run changing nothing, an issue the tracker holds differently
left alone, a folder-backed issue filled once, a folder past the counter or past the first number refused,
a dry run writing nothing, and a report that cannot be read refused alone with why.