Ask the operator for a condition's answers and act on the warrant, so release, stop, start and restart can be answered from any channel that proves who answered (hq ADR 0259)

This commit is contained in:
jochen
2026-10-08 18:32:13 +02:00
parent a1b7be8896
commit 7ea2ba4ea7
27 changed files with 1709 additions and 31 deletions
+4
View File
@@ -47,6 +47,10 @@ var Contracts = map[string]Contract{
KindPullUpdated: {Unordered: "a pull request's head, asked to be checked: each head is its own commit, and its " +
"verdict is set on that commit alone, so a head heard late is checked and judged as itself and never " +
"stands for a newer one (novox/hq to-be 45 §9)"},
KindDecided: {Unordered: "the router's word on one ask, by the ask's id: an ask is ended once, by compare-and-set " +
"at the router, and the controller acts on it once, recording that it did under the ask's id — so a word " +
"heard again, or late, does nothing more (novox/hq ADR 0259)",
Tests: []string{"TestAWarrantIsActedOnOnce"}},
KindCatchUp: {Unordered: "a catalogue asking what it missed: answered from the record, whenever asked"},
KindProvisioner: {Unordered: "a provider's newest word about a consumer, said again every fifteen minutes " +
"while it holds (ADR 0224): the condition keeps the last observed, and S8 says when the words stop. " +
+10
View File
@@ -49,6 +49,16 @@ type HandAct struct {
Kind string `json:"kind,omitempty"`
// Carried is what such a push moved, one "module from → to" per module, so the log says it.
Carried []string `json:"carried,omitempty"`
// Via, Ask, Proofs and RequestedBy are an act the operator chose on a warrant (novox/hq ADR 0234 §8, ADR
// 0259): the channel it came through (module and kind, and how the sender was known), the ask's id, the
// proofs present (P1, P2, P3), and what asked (a condition's key). By then names the operator as that
// kind's identity. Absent from every other act.
Via string `json:"via,omitempty"`
Ask string `json:"ask,omitempty"`
Proofs []string `json:"proofs,omitempty"`
RequestedBy string `json:"requested-by,omitempty"`
// Outcome is what came of an act recorded after it was done: done, or the verb's refusal.
Outcome string `json:"outcome,omitempty"`
}
// KindRecordedBuilds is a push that only moved recorded builds: the person's word their `record` policy
+3
View File
@@ -44,6 +44,9 @@ const (
// KindPullUpdated is the forge announcing a pull request's new head: checked before it merges
// (novox/hq to-be 45 §9).
KindPullUpdated = "pull-updated"
// KindDecided is the router's warrant for an ask the controller made, or that ask's end without one
// (novox/hq ADR 0259): said to the controller alone, under its own name.
KindDecided = "decided"
)
// Control is one thing a node or a module said, as the controller must act on it.
+3 -1
View File
@@ -53,7 +53,7 @@ func Nats(js *broker.JetStream) Inbound {
// whatever was asked for — and not at all when nothing was.
func (n *natsInbound) Also(kind string) error {
switch kind {
case KindModuleMoved, KindCatchUp, KindSourceMoved, KindProvisioner, KindPullUpdated:
case KindModuleMoved, KindCatchUp, KindSourceMoved, KindProvisioner, KindPullUpdated, KindDecided:
n.follows[kind] = true
return nil
default:
@@ -259,6 +259,8 @@ func kindOfSubject(subject string) (string, bool) {
return KindSourceMoved, true
case PullUpdatedSubject:
return KindPullUpdated, true
case broker.DecidedSubject:
return KindDecided, true
case BuildOutcome(), BuildOutcomeOf(TheBuildMachineBefore):
// A build's outcome is the role's event now, so it arrives on the events stream rather than
// the control branch — and is acted on by the same handler, because what the controller does
+38 -1
View File
@@ -70,7 +70,7 @@ type Checker interface {
}
// PullUpdatedSubject is where the forge's pull requests land: the controller's own follow of them.
var PullUpdatedSubject = broker.ControllerFollows[len(broker.ControllerFollows)-1]
var PullUpdatedSubject = "mesh.mod.gitea.event.pull.updated"
// Server acts on what nodes and modules say.
//
@@ -96,6 +96,8 @@ type Server struct {
checker Checker
// healths keeps what machines say of their long-running resources (novox/hq ADR 0240).
healths Healths
// decider acts on the operator's warrants for what the controller asked (novox/hq ADR 0259).
decider Decider
log *log.Logger
// giveUp is how long one message is held for the store; zero means GiveUpAfter.
@@ -138,6 +140,21 @@ func (s *Server) Checks(c Checker) error {
return nil
}
// Decider is what the controller does with the router's word on an ask it made (novox/hq ADR 0259): act
// on a warrant once, or record how the ask ended without one.
type Decider interface {
Decided(ctx context.Context, body []byte) error
}
// Decides says what to do about the router's word on the controller's asks, and asks for it delivered.
func (s *Server) Decides(d Decider) error {
if err := s.inbound.Also(KindDecided); err != nil {
return err
}
s.decider = d
return nil
}
// Answers says what to do about a catalogue's catch-up request, and asks for them to be delivered.
func (s *Server) Answers(r Replayer) error {
if err := s.inbound.Also(KindCatchUp); err != nil {
@@ -210,6 +227,8 @@ func (s *Server) act(ctx context.Context, m Control) {
s.provisioner(ctx, m)
case KindPullUpdated:
s.pullUpdated(ctx, m)
case KindDecided:
s.decided(ctx, m)
default:
// Dropped: a message nothing understands will not be understood on the next attempt
// either, and asking for it again would spin.
@@ -656,6 +675,24 @@ func (s *Server) pullUpdated(ctx context.Context, m Control) {
_ = m.Took()
}
// decided hands the router's word on an ask to the decider; a failure to keep what it did is held for the
// store, like any word that must not be lost.
func (s *Server) decided(ctx context.Context, m Control) {
if s.decider == nil {
_ = m.Took()
return
}
err := s.decider.Decided(ctx, m.Body())
switch s.decide(ctx, m, "the operator's word on an ask", "", "", err) {
case Hold:
return
}
if err != nil {
s.log.Printf("the operator's word on an ask could not be kept: %v", err)
}
_ = m.Took()
}
// saysWhatItDid states what a machine now runs, or what it would not take, as a fact on the bus
// (novox/hq ADR 0134).
//