Compare commits

..
Author SHA1 Message Date
jschoubben 878690697e A push raises what a module hears, not only a start
A module's declaration and its consumer are derived from the same records, and only one of them
followed a push: the consumers were raised when the control plane started serving, so a module that
gained a `consumes` was sent a declaration it could act on and a consumer that never delivered the
event — with nothing anywhere saying the two disagreed. Found on review: the catalogue's own replay
subscription was recorded, granted and never delivered.

Everything the raise does is idempotent, so a push may do it.
2026-09-28 16:46:42 +02:00
mesh-admin ad97297576 Merge pull request 'The seat declares the facts its holder states' (#131) from fix/the-seat-declares-the-facts-its-holder-states into main 2026-09-28 14:37:05 +00:00
mesh-admin 04f9f378b0 Merge pull request 'Two faults found on review' (#130) from fix/review-two-small-faults into main 2026-09-28 14:32:43 +00:00
jschoubben 1c3f44a526 Two faults found on review
A second Accept value would have overwritten the first, because the header was Set per value rather
than Added. One value is all any caller passes today, so nothing was wrong — but a helper that
quietly keeps only the last of what it was given is a trap for whoever passes two.

And a replayed announcement that could not be written was published as an empty body: a fact on the
mesh that says nothing, which the reader can only log and drop. It is now said and skipped, because a
body that cannot be marshalled is this program's fault rather than the bus's.
2026-09-28 16:32:41 +02:00
3 changed files with 17 additions and 15 deletions
+7 -1
View File
@@ -162,7 +162,13 @@ func declare(ctx context.Context, args []string) error {
return err
}
server, err := connectLink(ctx, nil, nil, nil)
// **With the inventory, so the bus is raised** (novox/hq ADR 0134, design 30). A module's
// declaration and how it hears what it consumes move together: its consumer is derived from the
// same records this declaration is composed from. Raised only when the control plane started
// serving, a module that gained a `consumes` was sent a declaration it could act on and a
// consumer that never delivered the event — and nothing anywhere said the two disagreed
// (found on review, 2026-09-28). Everything the raise does is idempotent.
server, err := connectLink(ctx, inv, nil, nil)
if err != nil {
return err
}
+1 -1
View File
@@ -137,7 +137,7 @@ func (r Registry) has(ctx context.Context, url string, accept ...string) (bool,
return false, err
}
for _, media := range accept {
request.Header.Set("Accept", media)
request.Header.Add("Accept", media)
}
response, err := r.client().Do(request)
if err != nil {
+9 -13
View File
@@ -476,7 +476,15 @@ func (s *Server) catchingUp(ctx context.Context, m Control) {
// module's event from a module called "control-plane", which does not exist — so the
// controller's own account refused it, every catalogue that asked what it missed was
// answered with nothing, and its graph kept the gap (found 2026-09-28).
if err := s.bus.PublishSeatEvent(ctx, MeshControllerSeat, KeyBuiltBefore, replayed(a)); err != nil {
body, err := json.Marshal(a)
if err != nil {
// A body that cannot be written is this program's fault, not the bus's, and publishing
// an empty one would put a fact on the mesh that says nothing.
s.log.Printf("cannot re-announce %s at %s: %v", a.Module, short(a.Commit), err)
_ = m.Took()
return
}
if err := s.bus.PublishSeatEvent(ctx, MeshControllerSeat, KeyBuiltBefore, body); err != nil {
// Said and abandoned rather than retried: the catalogue asks again every time it
// starts, and half a graph delivered twice is no better than half delivered once.
s.log.Printf("replaying %s at %s failed, and the rest is abandoned: %v",
@@ -601,15 +609,3 @@ func (s *Server) saysWhatItDid(ctx context.Context, report Report) {
s.log.Printf("could not say that %s %s: %v", report.Node, event, err)
}
}
// replayed is one announcement as the control plane states it. The same body the build machine's
// outcome carries, because what the catalogue does with it is the same.
func replayed(a Announcement) []byte {
raw, err := json.Marshal(a)
if err != nil {
// A body that cannot be marshalled is a programming error, not a bus failure, and an empty
// one is refused by the reader rather than silently taken as an announcement of nothing.
return nil
}
return raw
}