A board that reads through the same interfaces and holds nothing

novox/hq 03-DESIGN/01-to-be/11-a-board.md, built. The board being replaced is
one service reading every context's database directly — ADR 0008 violated by
the one component with a reason to violate it. The cost is not hypothetical: a
boundary nothing may cross can move, and one thing crossing it is enough to
freeze it. A board that reads the provisioning tables breaks when provisioning
changes them, and the change then gets weighed against the board.

So the three questions are read once, by one function, for all three ways of
saying them — a person's status, its JSON, and this page. Three
implementations of "which machine is not doing what it was told" would be three
chances to disagree.

Refused and failed stay distinct all the way to the page: refused means the
machine is exactly as it was and what is wrong is in what was sent; failed
means it is in a state nobody declared. Different places to fix, so one word
for both would send half the readers to the wrong one.

It stores nothing, changes nothing, and every action it might offer already
exists as a command. A board that cannot reach the mesh says so rather than
rendering an empty page — an empty page says "nothing is wrong" in the one
situation where nobody can know that.

One test earns its place twice: a machine's own words are the whole reason the
page is useful and the one thing on it nobody in this repository wrote, so they
are shown and are not markup.
This commit is contained in:
2026-08-31 04:49:21 +02:00
parent 29b336bb8d
commit 92133c340b
3 changed files with 428 additions and 32 deletions
+59 -32
View File
@@ -71,6 +71,8 @@ func run() error {
return buildCommand(ctx, args[1:])
case "builder":
return builderCommand(ctx, args[1:])
case "board":
return boardCommand(ctx, args[1:])
case "licence":
return licenceCommand(ctx, args[1:])
case "rotate":
@@ -140,6 +142,7 @@ func usage() {
module moved <name> <commit> the source has a newer commit than the mesh built
module forget <name> remove one, unless a node is running it
status [--json] what is wrong, what is quiet, and what is out of date
board [--listen ADDR] the same three questions, as a page that holds nothing
assign <node> <module> put a module on a node
unassign <node> <module> take it off
settings set <module> <file> what a module's config should say, for the whole mesh
@@ -1791,41 +1794,12 @@ func statusCommand(ctx context.Context, args []string) error {
}
defer inv.Close()
// Three questions, in the order somebody asks them: is anything broken, is anything not
// answering, is anything out of date. The first has consequences now, the second may, and
// the third is a plan for later — and a status that led with the third would bury the first.
//
// All three are gathered before anything is said, so the two ways of saying it answer the
// same questions from the same reads rather than being two implementations.
wrong, err := inv.NotDoingWhatTheyWereTold(ctx)
asked, err := theThreeQuestions(ctx, inv)
if err != nil {
return err
}
nodes, err := inv.Nodes(ctx)
if err != nil {
return err
}
var quiet []inventory.Node
for _, n := range nodes {
// Never heard from, or not lately. Different from failing: a machine that says nothing
// may be new, switched off, or unreachable, and none of those is a machine that tried
// and could not.
if n.LastSeen.IsZero() || time.Since(n.LastSeen) > time.Hour {
quiet = append(quiet, n)
}
}
behind, err := inv.Behind(ctx)
if err != nil {
return err
}
sources := map[string]inventory.Source{}
for module := range behind {
from, err := inv.SourceOf(ctx, module)
if err != nil {
return err
}
sources[module] = from
}
wrong, nodes, quiet := asked.wrong, asked.nodes, asked.quiet
behind, sources := asked.behind, asked.sources
if *asJSON {
body, err := statusAsJSON(wrong, nodes, quiet, behind, sources)
@@ -2533,3 +2507,56 @@ func buildAndShow(ctx context.Context, repository, ref string, wait time.Duratio
fmt.Println(string(body))
return nil
}
// answers is what the three questions came back with, read once.
type answers struct {
wrong []inventory.Doing
nodes []inventory.Node
quiet []inventory.Node
behind map[string][]string
sources map[string]inventory.Source
}
// theThreeQuestions reads what anything answering "is the mesh alright" needs.
//
// **One reading, for every way of saying it** (novox/hq 03-DESIGN/01-to-be/11-a-board.md). There
// are three now — a person's status, its JSON, and a page — and three implementations of "which
// machine is not doing what it was told" would be three chances to disagree about it.
//
// The order is the design and not a convenience: is anything broken, is anything not answering, is
// anything out of date. The first has consequences now, the second may, the third is a plan for
// later — and anything that led with the third would bury the first.
func theThreeQuestions(ctx context.Context, inv *inventory.Inventory) (answers, error) {
var out answers
var err error
out.wrong, err = inv.NotDoingWhatTheyWereTold(ctx)
if err != nil {
return answers{}, err
}
out.nodes, err = inv.Nodes(ctx)
if err != nil {
return answers{}, err
}
for _, n := range out.nodes {
// Never heard from, or not lately. Different from failing: a machine that says nothing
// may be new, switched off, or unreachable, and none of those is a machine that tried
// and could not.
if n.LastSeen.IsZero() || time.Since(n.LastSeen) > time.Hour {
out.quiet = append(out.quiet, n)
}
}
out.behind, err = inv.Behind(ctx)
if err != nil {
return answers{}, err
}
out.sources = map[string]inventory.Source{}
for module := range out.behind {
from, err := inv.SourceOf(ctx, module)
if err != nil {
return answers{}, err
}
out.sources[module] = from
}
return out, nil
}