From 09b636e62859d7f01ee333fdf73ea1291b42c8f6 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 1 Oct 2026 17:58:49 +0200 Subject: [PATCH 1/2] The plans verb: what the last merges produced and where each stands (hq ADR 0162) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mesh's seat answers plans — the recent plans with their tier, what each waits for and since when, or one plan whole given its id — so the console reads a merge's progress where it reads everything else, instead of a person reading the daemon's log. --- cmd/mesh-controller/main.go | 2 + cmd/mesh-controller/release_plan.go | 61 +++++++++++++++++++++++++++++ cmd/mesh-controller/seatverbs.go | 5 +++ internal/catalogue/verbs.go | 3 ++ 4 files changed, 71 insertions(+) diff --git a/cmd/mesh-controller/main.go b/cmd/mesh-controller/main.go index 70346c6..41defbb 100644 --- a/cmd/mesh-controller/main.go +++ b/cmd/mesh-controller/main.go @@ -72,6 +72,8 @@ func run() error { return askCommand(ctx, args[1:]) case "builds": return buildsCommand(ctx, args[1:]) + case "plans": + return plansCommand(ctx, args[1:]) case "pin": return pinCommand(ctx, args[1:], true) case "unpin": diff --git a/cmd/mesh-controller/release_plan.go b/cmd/mesh-controller/release_plan.go index 550a3e9..61f7ac6 100644 --- a/cmd/mesh-controller/release_plan.go +++ b/cmd/mesh-controller/release_plan.go @@ -2,6 +2,7 @@ package main import ( "context" + "flag" "fmt" "sort" "strings" @@ -524,3 +525,63 @@ func openPlans(plans []inventory.Plan) ([]inventory.Plan, int) { } return open, late } + + +// plansCommand says what the last merges produced and where each stands; given an id, one plan +// tier by tier with every module's state. +func plansCommand(ctx context.Context, args []string) error { + set := flag.NewFlagSet("plans", flag.ContinueOnError) + limit := set.Int("n", 10, "how many to show") + positionals, err := parseAround(set, args) + if err != nil { + return err + } + open, err := openStores(ctx) + if err != nil { + return err + } + defer open.Close() + inv := open.inventory + now := time.Now() + if len(positionals) == 1 { + p, err := inv.PlanByID(ctx, positionals[0]) + if err != nil { + return err + } + fmt.Printf("%s — %s\n", p.ID, planLine(p, now)) + for i, tier := range p.Tiers { + marker := " " + if i == p.Tier && p.Open() { + marker = ">" + } + fmt.Printf("%s tier %d\n", marker, i) + for _, m := range tier { + s := p.Modules[m] + state := "not yet asked" + if s != nil && s.State != "" { + state = s.State + if s.Commit != "" { + state += " from " + short(s.Commit) + } + if s.Why != "" { + state += ": " + s.Why + } + } + fmt.Printf(" %-22s %s\n", m, state) + } + } + return nil + } + plans, err := inv.RecentPlans(ctx, *limit) + if err != nil { + return err + } + if len(plans) == 0 { + fmt.Println("no merge has produced a plan yet") + return nil + } + for _, p := range plans { + fmt.Printf("%-28s %s\n", p.ID, planLine(p, now)) + } + return nil +} diff --git a/cmd/mesh-controller/seatverbs.go b/cmd/mesh-controller/seatverbs.go index 5e7f0b6..e95a5dc 100644 --- a/cmd/mesh-controller/seatverbs.go +++ b/cmd/mesh-controller/seatverbs.go @@ -69,6 +69,11 @@ func argvFor(verb string, args map[string]any) ([]string, error) { return []string{"builds", m}, nil } return []string{"builds"}, nil + case "plans": + if id := str("id"); id != "" { + return []string{"plans", id}, nil + } + return []string{"plans"}, nil case "plan": if err := need("node"); err != nil { return nil, err diff --git a/internal/catalogue/verbs.go b/internal/catalogue/verbs.go index 4d573a5..71aeea4 100644 --- a/internal/catalogue/verbs.go +++ b/internal/catalogue/verbs.go @@ -91,6 +91,9 @@ var ControllerVerbs = []Verb{ "module": "one module's name; every module when absent", "log": "a build's id (as `builds` lists it): print what the build machine said, line by line", }, nil)}, + {Name: "plans", Description: "What the last merges produced and where each stands (novox/hq ADR 0162): " + + "the tiers, the tier a plan is at, what it waits for and since when; one plan whole, given its id.", + Input: schema(map[string]string{"id": "a plan's id (as `plans` lists them): that plan, tier by tier"}, nil)}, {Name: "plan", Description: "What one machine would run, and why: the declaration the mesh would send it.", Input: schema(map[string]string{"node": "the machine's name"}, []string{"node"})}, {Name: "assign", Description: "Put a module on a machine. Refused with the mesh's own words when it cannot resolve there.", -- 2.54.0 From 89d43e0dad61d8e752c04f459050598c1c5dfec1 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 1 Oct 2026 18:04:58 +0200 Subject: [PATCH 2/2] gofmt --- cmd/mesh-controller/release_plan.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/mesh-controller/release_plan.go b/cmd/mesh-controller/release_plan.go index 61f7ac6..92521e9 100644 --- a/cmd/mesh-controller/release_plan.go +++ b/cmd/mesh-controller/release_plan.go @@ -526,7 +526,6 @@ func openPlans(plans []inventory.Plan) ([]inventory.Plan, int) { return open, late } - // plansCommand says what the last merges produced and where each stands; given an id, one plan // tier by tier with every module's state. func plansCommand(ctx context.Context, args []string) error { -- 2.54.0