Answer the build queue as data too, so a check can say how far back it waits
mesh/merge-gate pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered

The queue verb answered only a table, so nothing outside the controller
could say an ask's place in the queue. It now asks its command for JSON:
each ask's state, kind, repository, commit, machine, since and position
among the waiting, with the table carried beside it and answered as the
verb's output, both written from one read. A repository's password, if
one were ever written into it, is left out of what is shown.

novox/hq issue 455
This commit is contained in:
2026-10-12 14:22:22 +02:00
parent db9e9c5b87
commit 0cd8bc0bfb
7 changed files with 264 additions and 8 deletions
+3 -1
View File
@@ -333,7 +333,9 @@ var ControllerVerbs = []Verb{
// The build queue, controlled by hand (novox/hq ADR 0219). Every verb that drops an ask leaves a
// failed outcome for it, so a plan waiting on it fails visibly instead of hanging.
{Name: "queue", Description: "Every ask in the build queue: waiting, in flight (on which machine, for how long), " +
"and dead (handed out as often as allowed and never settled) — each with its id, repository, path, ref and when it was asked.",
"and dead (handed out as often as allowed and never settled) — each with its id, repository, path, ref and when it was asked. " +
"Answers the table as text and, as data, each ask's state, kind (check or build), repository, commit, machine, since " +
"and position among the waiting (novox/hq issue 455).",
Input: schema(nil, nil)},
{Name: "cancel", Description: "Drop one waiting or dead ask from the build queue; its outcome is recorded failed, " +
"cancelled by hand, and a plan that asked for it fails. One in flight is refused: `kill` ends it where it runs.",
+25
View File
@@ -7,6 +7,7 @@ import (
"fmt"
"regexp"
"sort"
"strings"
"time"
"github.com/nats-io/nats.go"
@@ -268,6 +269,30 @@ func ReadQueue(ctx context.Context, js *broker.JetStream, seat string) (Queue, e
return q, nil
}
// WithoutPassword is a repository as a listing may say it: a URL's password, should one ever be written
// into it, left out (novox/hq issue 455). Only for what is shown: the ask itself keeps what it was given,
// which a cancel's outcome and a rebuild's match are read from.
func WithoutPassword(repository string) string {
scheme, rest, found := strings.Cut(repository, "://")
if !found {
return repository
}
authority, path, _ := strings.Cut(rest, "/")
at := strings.LastIndex(authority, "@")
if at < 0 {
return repository
}
user, _, password := strings.Cut(authority[:at], ":")
if !password {
return repository
}
out := scheme + "://" + user + authority[at:]
if len(rest) > len(authority) {
out += "/" + path
}
return out
}
// ReadBuildEvents is every start and outcome the seat announced since a moment, from the events
// stream, with a consumer of its own that is gone when this returns.
func ReadBuildEvents(ctx context.Context, js *broker.JetStream, seat string, since time.Time) (BuildHeard, error) {