Name the decision this builds: hq ADR 0236 (0235 is the bus's snapshot)

This commit is contained in:
jochen
2026-10-06 18:56:54 +02:00
parent c6f3d8cdfa
commit d7bf1bae83
27 changed files with 55 additions and 55 deletions
+1 -1
View File
@@ -570,7 +570,7 @@ func takeIn(ctx context.Context, inv *inventory.Inventory, result link.BuildResu
return manifest, kept, fmt.Errorf("%s built %s (%s), and the mesh does not register it: %w",
result.On, result.Repository, short(result.Commit), err)
}
// **A build that failed its gate is never registered again** (novox/hq ADR 0235): an outcome heard
// **A build that failed its gate is never registered again** (novox/hq ADR 0236): an outcome heard
// twice, or replayed, would otherwise make the build a rollback put back what the module is again,
// and the next push would send it.
if failed, err := inv.GateFailed(ctx, kept.ID); err != nil {
+3 -3
View File
@@ -17,7 +17,7 @@ import (
"github.com/novox/mesh-controller/internal/link"
)
// The bus as a planned step (novox/hq to-be 45 §8, ADR 0227 rule 8, ADR 0235).
// The bus as a planned step (novox/hq to-be 45 §8, ADR 0227 rule 8, ADR 0236).
//
// **A bus upgrade is never rolled out.** The bus carries every declaration, every report and the
// controller's own lease; a new bus build that does not come up is a mesh nobody can tell anything,
@@ -101,7 +101,7 @@ func pendingBus(ctx context.Context, inv *inventory.Inventory) (busPending, erro
}
// busHeld names the machines a push may not send because sending them would replace the bus: the
// planned step's, not a push's (ADR 0235). Said with the remedy.
// planned step's, not a push's (ADR 0236). Said with the remedy.
func busHeld(ctx context.Context, inv *inventory.Inventory, machines []string) (map[string]string, error) {
b, err := pendingBus(ctx, inv)
if err != nil {
@@ -112,7 +112,7 @@ func busHeld(ctx context.Context, inv *inventory.Inventory, machines []string) (
for _, holder := range b.machines {
if n == holder && b.moves(n) {
out[n] = fmt.Sprintf("sending %s would replace the bus (%s %s → %s), which is a planned step: "+
"`bus upgrade --why …` snapshots its streams first and checks them after (novox/hq ADR 0235)",
"`bus upgrade --why …` snapshots its streams first and checks them after (novox/hq ADR 0236)",
n, b.module, short(orNotKnown(b.from[n])), short(b.to))
}
}
+9 -9
View File
@@ -113,26 +113,26 @@ var probeRegistry = []probe{
Asks: []broker.SeatVerb{{Seat: "node-backup", Verb: "backed-up"}}},
{ID: "DW", Asserts: "the watchdogs of the signals table ran within three of their intervals",
From: "ADR 0227 rule 6: the watchers are watched", Kind: "watchdogs-silent", Phase: 1, run: probeWatchdogs},
// The core's health definitions (novox/hq to-be 45 §8, ADR 0235): what a core component's new build is
// The core's health definitions (novox/hq to-be 45 §8, ADR 0236): what a core component's new build is
// judged by on its first machine, run against every machine between upgrades too.
{ID: "H-controller", Asserts: "the controller lease is held, renewed in time, by a controller that says it is " +
"ready: its self-check ran and status answered in full within ten seconds", From: "ADR 0235, to-be 45 §8",
"ready: its self-check ran and status answered in full within ten seconds", From: "ADR 0236, to-be 45 §8",
Kind: kindCoreUnhealthy, Phase: 4, run: probeControllerHealth},
{ID: "H-engine", Asserts: "every machine heard from has reported its current declaration, under a node-engine " +
"build it names", From: "ADR 0235, to-be 45 §8", Kind: kindCoreUnhealthy, Phase: 4, run: probeEngineHealth},
"build it names", From: "ADR 0236, to-be 45 §8", Kind: kindCoreUnhealthy, Phase: 4, run: probeEngineHealth},
{ID: "H-tools", Asserts: "every machine heard from that runs the node tools has them answering the bus",
From: "ADR 0235, to-be 45 §8", Kind: kindCoreUnhealthy, Phase: 4, run: probeToolsHealth},
From: "ADR 0236, to-be 45 §8", Kind: kindCoreUnhealthy, Phase: 4, run: probeToolsHealth},
{ID: "H-bus", Asserts: "every stream and durable consumer the mesh defines is on the bus, and a request crosses " +
"it to the machines' node tools and back", From: "ADR 0235, to-be 45 §8", Kind: kindCoreUnhealthy, Phase: 4,
"it to the machines' node tools and back", From: "ADR 0236, to-be 45 §8", Kind: kindCoreUnhealthy, Phase: 4,
run: probeBusHealth},
// The gate's verdicts and the witnesses' rollbacks (ADR 0235): each build that failed its gate keeps its
// The gate's verdicts and the witnesses' rollbacks (ADR 0236): each build that failed its gate keeps its
// condition until a newer build passes; each rollback a witness stands by is said.
{ID: gateProbe, Asserts: "no build that failed its gate, and no core component a witness put back, goes unsaid; " +
"a newer build that passes its gate clears it", From: "ADR 0235, to-be 45 §8", Kind: kindRolledBack,
"a newer build that passes its gate clears it", From: "ADR 0236, to-be 45 §8", Kind: kindRolledBack,
Raises: []string{kindRollbackFailed}, Phase: 4, run: probeGates},
// The bus's planned step (ADR 0235): open while a person's bus upgrade runs, then checked by H-bus.
// The bus's planned step (ADR 0236): open while a person's bus upgrade runs, then checked by H-bus.
{ID: busStepProbe, Asserts: "a bus upgrade a person started is said while it runs, and is followed by the bus's " +
"health within its bound — or is said failed, with its snapshot as the way back", From: "ADR 0235, to-be 45 §8",
"health within its bound — or is said failed, with its snapshot as the way back", From: "ADR 0236, to-be 45 §8",
Kind: kindBusMaintenance, Raises: []string{kindBusUpgradeFailed}, Phase: 4, run: probeBusStep},
}
+3 -3
View File
@@ -20,7 +20,7 @@ import (
"github.com/novox/mesh-controller/internal/link"
)
// The gate on a release plan's first machine, and the rollback after it (novox/hq ADR 0235, to-be 45
// The gate on a release plan's first machine, and the rollback after it (novox/hq ADR 0236, to-be 45
// §8, ADR 0227 rule 8).
//
// **"Reported applied" is not enough.** ADR 0218 sent a module to one machine first and the rest once
@@ -328,7 +328,7 @@ func gatePassed(ctx context.Context, open *stores, p *inventory.Plan, module str
}
// carryUserList sends the machine holding the bus the user list a new controller composes, once that
// controller passed its gate (ADR 0235). The controller's own grants travel in that list, and the old
// controller passed its gate (ADR 0236). The controller's own grants travel in that list, and the old
// controller composed the list the plan sent; on 2026-10-06 eight pushes by hand carried a new
// controller's grant into it. Not when a build its policy or a plan holds back would go with it (ADR
// 0221): then it is said, as a push would say it.
@@ -457,7 +457,7 @@ func gateFailed(ctx context.Context, open *stores, p *inventory.Plan, module str
sayRollback(ctx, open, module, g, "")
}
// rolledBackEvent is the body of `rolled-back` (ADR 0235): a contract, like a condition's events.
// rolledBackEvent is the body of `rolled-back` (ADR 0236): a contract, like a condition's events.
type rolledBackEvent struct {
Event string `json:"event"`
At time.Time `json:"at"`
+1 -1
View File
@@ -17,7 +17,7 @@ import (
"github.com/novox/mesh-controller/internal/link"
)
// The gate on a plan's first machine and the rollback after it (novox/hq ADR 0235, to-be 45 §8).
// The gate on a plan's first machine and the rollback after it (novox/hq ADR 0236, to-be 45 §8).
// gateMesh is a mesh with `app` running on anchor and laptop at build c1, a newer build c2 registered,
// a plan whose tier built c2, and every send recorded and answered — the machine applies what it is
+1 -1
View File
@@ -193,7 +193,7 @@ func TestANamedPushLeavesAMachineAPolicyHoldsBack(t *testing.T) {
t.Fatal(err)
}
}
// Recorded by a person's choice: the default rolls out since novox/hq ADR 0235.
// Recorded by a person's choice: the default rolls out since novox/hq ADR 0236.
if err := inv.SetUpgradeOf(ctx, "resolver", inventory.Upgrade{Why: "each machine checked by hand"}); err != nil {
t.Fatal(err)
}
+1 -1
View File
@@ -116,7 +116,7 @@ func run() error {
return serve(ctx)
case "upgrade":
return upgradeCommand(ctx, args[1:])
// The bus as a planned step (novox/hq to-be 45 §8, ADR 0235).
// The bus as a planned step (novox/hq to-be 45 §8, ADR 0236).
case "bus":
return busCommand(ctx, args[1:])
case "declare":
+1 -1
View File
@@ -178,7 +178,7 @@ func retryRefusal(p inventory.Plan, plans []inventory.Plan) error {
return fmt.Errorf("nothing in tier %d of %s failed to build or stopped rolling out — it stopped at: %s",
p.Tier, p.ID, p.Note)
}
// **A build that failed its gate is not sent again** (novox/hq ADR 0235): it was put back on its first
// **A build that failed its gate is not sent again** (novox/hq ADR 0236): it was put back on its first
// machine, and retrying would judge the build the mesh put back, or send the failed one by hand.
for _, m := range stopped {
if g := p.Modules[m].Gate; g != nil && g.Verdict == inventory.GateFailed {
+1 -1
View File
@@ -466,7 +466,7 @@ func pushCommand(ctx context.Context, args []string) error {
which, len(asked), strings.Join(asked, ", "))
}
// **The bus is replaced only as a planned step** (novox/hq ADR 0235, to-be 45 §8): a machine whose bus
// **The bus is replaced only as a planned step** (novox/hq ADR 0236, to-be 45 §8): a machine whose bus
// would move is not sent by a push — named, it is refused; otherwise it is left and said.
busKept, err := busHeld(ctx, inv, asked)
if err != nil {
+1 -1
View File
@@ -625,7 +625,7 @@ func TestAPlanStoppedAtItsFirstMachineIsRetried(t *testing.T) {
}
t.Cleanup(func() { sendRollout = was })
// a records: a person's choice, since the default rolls out (novox/hq ADR 0235).
// a records: a person's choice, since the default rolls out (novox/hq ADR 0236).
if err := open.inventory.SetUpgradeOf(ctx, "a", inventory.Upgrade{Why: "test"}); err != nil {
t.Fatal(err)
}
+5 -5
View File
@@ -432,7 +432,7 @@ func planBuilt(ctx context.Context, open *stores, module, commit, failed string,
continue
}
if failed != "" && deletedAtSource(failed) {
// Deleted at its source by the merge, not broken (novox/hq ADR 0235): the plan goes on.
// Deleted at its source by the merge, not broken (novox/hq ADR 0236): the plan goes on.
state.State, state.Why = planDeleted, "deleted at its source: "+firstLine(failed)
forgetDeleted(ctx, inv, module, p)
} else if failed != "" {
@@ -629,7 +629,7 @@ func advanceOnce(ctx context.Context, open *stores, p *inventory.Plan,
switch {
case step.failed != "":
// The first machine refused or failed what it was sent, or never said: the gate failed, and
// the build is put back there (novox/hq ADR 0235); the rest are left as they were.
// the build is put back there (novox/hq ADR 0236); the rest are left as they were.
gateFailed(ctx, open, p, m, state, firstRunning(state.First, running), step.failed)
p.Note += fmt.Sprintf("; %s left as it was", orNone(strings.Join(step.rest, ", ")))
fmt.Printf("%s: %s\n", p.ID, p.Note)
@@ -638,7 +638,7 @@ func advanceOnce(ctx context.Context, open *stores, p *inventory.Plan,
pending = append(pending, fmt.Sprintf("%s on %s, sent first at %s", m, step.waiting, state.FirstAt.Local().Format("15:04")))
continue
}
// **The gate** (novox/hq ADR 0235, to-be 45 §8): the first machine reported the build applied;
// **The gate** (novox/hq ADR 0236, to-be 45 §8): the first machine reported the build applied;
// it is judged by its health before anything else is sent — the rest, or, where it is the only
// machine, the plan's next step.
if !policy.Together && state.FirstAt != nil && len(state.First) > 0 {
@@ -684,7 +684,7 @@ func advanceOnce(ctx context.Context, open *stores, p *inventory.Plan,
return false, fmt.Errorf("sending %s to %s after tier %d: %w", m, strings.Join(step.send, ", "), p.Tier, err)
}
if step.first {
// What the first machine ran before: what a failed gate puts back (ADR 0235).
// What the first machine ran before: what a failed gate puts back (ADR 0236).
if beforeKnown {
state.Previous = before[m]
}
@@ -1328,7 +1328,7 @@ func firstRunning(first, running []string) []string {
}
// planDeleted is a plan's module that the merge deleted at its source: not built, not sent, and no
// failure of the plan (novox/hq ADR 0235).
// failure of the plan (novox/hq ADR 0236).
const planDeleted = "deleted"
// forgetDeleted forgets a module a plan found deleted at its source, where nothing holds it, and says
+1 -1
View File
@@ -195,7 +195,7 @@ func (l nudgingListener) Heard(ctx context.Context, report link.Report) (bool, e
if report.Ordered() {
link.StaleRefusals.Lifetime(report.Node, report.RefusedOlder, now)
}
// What the machine's witnesses put back and stand by (novox/hq ADR 0235): read by the gate and its
// What the machine's witnesses put back and stand by (novox/hq ADR 0236): read by the gate and its
// probe. Only from an account of the machine — not a word that a declaration was set aside, nor a rekey.
if report.Superseded == "" && report.Rekey == nil && report.Node != "" {
witnessed.heard(report.Node, report.Rollbacks, now)
+4 -4
View File
@@ -205,7 +205,7 @@ func upgradeCommand(ctx context.Context, args []string) error {
}
if decision.Why == "" {
return fmt.Errorf("holding %s back from every merge is a choice a person reads later: --why <text> "+
"(novox/hq ADR 0235). Nothing was changed", module)
"(novox/hq ADR 0236). Nothing was changed", module)
}
case "default":
if err := inv.ClearUpgradeOf(ctx, module); err != nil {
@@ -354,7 +354,7 @@ func (f following) SourceMoved(ctx context.Context, m link.SourceMoved) error {
}
}
touched := whatTheMergeTouched(from, entries, m)
// **A module the merge deleted is not built** (novox/hq ADR 0235): its manifest is gone, so the build
// **A module the merge deleted is not built** (novox/hq ADR 0236): its manifest is gone, so the build
// seat finds nothing saying what it is, and the plan failed on it (`has no module.json at …`) with
// every other module of its tier left unsent. It is forgotten where nothing holds it, said otherwise.
touched, deleted := splitDeleted(touched, m)
@@ -521,7 +521,7 @@ func wouldMove(m link.SourceMoved, entries []inventory.Entry,
}
// splitDeleted parts the modules a merge touched into those it changed and those whose manifest it
// removed — deleted at their source (ADR 0235).
// removed — deleted at their source (ADR 0236).
func splitDeleted(touched []inventory.Entry, m link.SourceMoved) (kept, deleted []inventory.Entry) {
removed := map[string]bool{}
for _, p := range m.Removed {
@@ -852,7 +852,7 @@ const moduleManifestFile = "module.json"
// deletedAtSource is whether a build failed because its module's manifest is not at its source any
// more — the build seat's own words (internal/builder) — which a merge that deleted the module causes
// when its announcer did not say which files went (ADR 0235). Such a module is not a failure of the plan.
// when its announcer did not say which files went (ADR 0236). Such a module is not a failure of the plan.
func deletedAtSource(failed string) bool {
return strings.Contains(failed, "has no "+moduleManifestFile+" at ") &&
strings.Contains(failed, "so there is nothing saying what it is")
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"github.com/novox/mesh-controller/internal/lease"
)
// The controller's side of its own rollback witness (novox/hq to-be 45 §8, ADR 0235; the contract is
// The controller's side of its own rollback witness (novox/hq to-be 45 §8, ADR 0236; the contract is
// internal/lease/witness.go): what the serving controller says of itself in every write of the lease's
// key, for the node-engine on the control node to judge a new controller build by.