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
+75 -5
View File
@@ -22,14 +22,15 @@ import (
const Seat = "issue-tracker"
// Verbs are the seat's verbs, as the manifest declares and claims them.
var Verbs = []string{"open", "get", "list", "move", "note", "link"}
var Verbs = []string{"open", "get", "list", "move", "note", "link", "import"}
func logf(format string, a ...any) { fmt.Fprintf(os.Stderr, format+"\n", a...) }
// Settings are the operator's values for this module, merged by the mesh into one JSON file.
type Settings struct {
// FirstNumber is the number the first issue gets: read once, when the counter is started, and never
// again. The counter never starts below an issue already kept.
// FirstNumber is the number the first issue gets: read once for the counter, when it is started, and
// never again for it. The counter never starts below an issue already kept. The import reads it as the
// boundary between the folders hq numbered before the tracker and the issues the tracker numbered.
FirstNumber uint64 `json:"first-number"`
// Owners is the registered list `located-in` may name (to-be 53 §3).
Owners []string `json:"owners"`
@@ -67,6 +68,9 @@ func main() {
Logf: logf,
Sleep: time.Sleep,
}
if len(os.Args) > 1 && os.Args[1] == "dry-run" {
os.Exit(dryRunAtTheTerminal(t, os.Args[2:]))
}
if _, err := t.Settings(); err != nil {
logf("[mesh-issues] the settings cannot be read (%v): every open and move will say so", err)
}
@@ -76,6 +80,43 @@ func main() {
}
}
// dryRunAtTheTerminal is `mesh-issues dry-run <hq checkout> <settings.json>`: the import's dry run over a
// checkout on disk, against no tracker, before the module is rolled out. It reads, writes nothing, and
// prints what the import would create from that checkout, as the verb's dry run would answer against an
// empty tracker. Not a way to import: only the verb writes, and it reads the record over the bus.
func dryRunAtTheTerminal(t *Tracker, args []string) int {
if len(args) != 2 {
logf("usage: mesh-issues dry-run <hq checkout> <settings.json>")
return 2
}
t.Store = emptyStore{}
t.Settings = func() (Settings, error) { return readSettings(args[1]) }
out, err := t.Import(ImportAsk{DryRun: true}, dirRecords{dir: args[0]})
if err != nil {
logf("%v", err)
return 1
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
_ = enc.Encode(out)
return 0
}
// emptyStore is a Store that holds nothing and takes nothing: the terminal's dry run.
type emptyStore struct{}
func (emptyStore) Get(string, string) (*Entry, error) { return nil, nil }
func (emptyStore) Create(string, string, any) (uint64, error) {
return 0, errors.New("a dry run at the terminal writes nothing")
}
func (emptyStore) Update(string, string, any, uint64) (uint64, error) {
return 0, errors.New("a dry run at the terminal writes nothing")
}
func (emptyStore) Keys(string) ([]string, error) { return nil, nil }
func (emptyStore) Increment(string, string, int) (uint64, error) {
return 0, errors.New("a dry run at the terminal writes nothing")
}
// args reads one call's arguments strictly: an argument the verb does not take is refused, never ignored,
// so nothing a caller sends is silently dropped — and no argument can say who the caller is.
type args struct {
@@ -285,8 +326,7 @@ func Tools(t *Tracker) []stdio.Tool {
"development cycle's rules: located needs located-in, owners on the registered list; resolved needs " +
"located-in and fixed-by (the pull requests or commits), and an issue in the mesh's core also replay " +
"(the replay's id, such as R361) or replay-none (why no replay is possible); wontfix needs a reason. " +
"A refused move changes nothing and says which rule refused it. An issue with a folder in hq keeps its " +
"status there and is refused here.",
"A refused move changes nothing and says which rule refused it.",
Input: schema([]string{"number", "status"}, map[string]any{
"number": number("the issue's number"),
"status": map[string]any{"type": "string", "enum": Statuses, "description": "the status it moves to"},
@@ -379,5 +419,35 @@ func Tools(t *Tracker) []stdio.Tool {
}
return t.Link(ask)
}},
{Name: Seat + ".import",
Description: "Import hq's issue folders (04-ISSUES/NNN-slug/00-report.md), read through the records module at " +
"the commit its checkout stands at, each as an issue under its own number with the status, owners, fix " +
"and replay its report gives. It only ever creates an issue that is not there, or fills the status of one " +
"the tracker holds without a status; it never overwrites an issue, never advances the counter, refuses a " +
"folder numbered past the counter or past the tracker's first number without an issue, and run again " +
"changes nothing and says so. With dry-run it writes nothing and says what it would do; with commit it " +
"refuses a checkout standing elsewhere. The answer is a row per folder and the counts.",
Input: schema(nil, map[string]any{
"dry-run": map[string]any{"type": "boolean", "description": "read everything, write nothing, say what would be done"},
"commit": text("the commit of hq the record must stand at (a prefix is enough); refused when it stands elsewhere"),
}),
Run: func(m map[string]any) (any, error) {
a, err := readArgs("import", m, "dry-run", "commit")
if err != nil {
return nil, err
}
var ask ImportAsk
if ask.DryRun, err = a.flag("dry-run"); err != nil {
return nil, err
}
if ask.Commit, err = a.str("commit"); err != nil {
return nil, err
}
records := t.Records
if records == nil {
records = busRecords{}
}
return t.Import(ask, records)
}},
}
}