Assert the bus's objects on every send, not only at start (hq issue 208) #81

Merged
mesh-admin merged 1 commits from fix/issue-208-bus-objects-on-send into main 2026-10-06 09:47:01 +00:00
Contributor

novox/hq issue 208, and the live symptom of 2026-10-06: messenger, carried by the runtime on novox, assigned after the controller started, said binding messenger's consumer novox_messenger: nats: consumer not found; D6 confirmed novox_messenger on EVENTS is not on the bus.

Cause

assertBusObjects (streams, seat workers, every module's consumer) ran only in raiseTheBus at controller start. A push ensured a module's consumer only inside issueWith, when a bus credential is minted, and a module carried by the runtime is never minted one. A seat holder assigned later (issue 208, the build agents on 2026-10-03) had the same gap for its worker.

Fix

  • overTheBus.grant, the one step every real send passes through (push, the push's cascade, sendTo/sendToEach: plan sends, rotate, secret, upgrades), now calls assertOnSend before the memberships: the same assertBusObjects derivation the start and the self-check use, no second list. Idempotent; the JetStream Note is printed as at start.
  • A failure is said in the send's output (THE BUS DOES NOT HOLD WHAT THIS SEND IMPLIES: …) and raised as condition bus.objects.unasserted (source bus-objects, Reconcile), which the next send that asserts everything clears. The send goes on: the objects are the mesh's, and holding every machine back for one consumer would turn one fault into all.
  • assertBusObjects tries every module consumer and joins the failures, instead of stopping at the first.
  • The start raise is unchanged and still refuses to serve without its objects.

Three files: busobjects.go, a 7-line hook in push.go, and a new busobjects_test.go, so this rebases easily against the Phase 2 work.

Tests

  • TestASendAssertsWhatWasAssignedAfterStart: a carried consumer (runtime and messenger on laptop) and a late node-build-agent holder are not in the start's assertion and are in the send's.
  • TestASendThatCannotAssertTheBusSaysSoAndRaisesACondition: a refused consumer is said in the output and raised with its evidence. The seat worker after it is still asked for. The next good send clears the condition.
  • TestNatsAModuleAssignedAfterStartGetsItsConsumerAtItsFirstSend: against a real bus, through overTheBus.grant. Both the consumer and the worker exist after the send. Without the hook it fails with laptop_messenger on EVENTS is not on the bus after a send: nats: consumer not found, the live symptom.

The full suite (go test -p 1 ./...) passes with MESH_TEST_POSTGRES (postgres:17) and MESH_TEST_NATS (nats:2.11 -js), and vet and gofmt are clean.

Rollout

Once merged and the controller is delivered, push novox creates novox_messenger and D6's bus.EVENTS.novox_messenger.missing clears at the next self-check. No restart is needed beyond the controller's own upgrade, and that upgrade's start raise would also create it.

novox/hq issue 208, and the live symptom of 2026-10-06: messenger, carried by the runtime on novox, assigned after the controller started, said `binding messenger's consumer novox_messenger: nats: consumer not found`; D6 confirmed `novox_messenger on EVENTS is not on the bus`. ## Cause `assertBusObjects` (streams, seat workers, every module's consumer) ran only in `raiseTheBus` at controller start. A push ensured a module's consumer only inside `issueWith`, when a bus credential is minted, and a module carried by the runtime is never minted one. A seat holder assigned later (issue 208, the build agents on 2026-10-03) had the same gap for its worker. ## Fix - `overTheBus.grant`, the one step every real send passes through (push, the push's cascade, `sendTo`/`sendToEach`: plan sends, rotate, secret, upgrades), now calls `assertOnSend` before the memberships: the same `assertBusObjects` derivation the start and the self-check use, no second list. Idempotent; the JetStream `Note` is printed as at start. - A failure is said in the send's output (`THE BUS DOES NOT HOLD WHAT THIS SEND IMPLIES: …`) and raised as condition `bus.objects.unasserted` (source `bus-objects`, Reconcile), which the next send that asserts everything clears. The send goes on: the objects are the mesh's, and holding every machine back for one consumer would turn one fault into all. - `assertBusObjects` tries every module consumer and joins the failures, instead of stopping at the first. - The start raise is unchanged and still refuses to serve without its objects. Three files: `busobjects.go`, a 7-line hook in `push.go`, and a new `busobjects_test.go`, so this rebases easily against the Phase 2 work. ## Tests - `TestASendAssertsWhatWasAssignedAfterStart`: a carried consumer (runtime and messenger on laptop) and a late `node-build-agent` holder are not in the start's assertion and are in the send's. - `TestASendThatCannotAssertTheBusSaysSoAndRaisesACondition`: a refused consumer is said in the output and raised with its evidence. The seat worker after it is still asked for. The next good send clears the condition. - `TestNatsAModuleAssignedAfterStartGetsItsConsumerAtItsFirstSend`: against a real bus, through `overTheBus.grant`. Both the consumer and the worker exist after the send. Without the hook it fails with `laptop_messenger on EVENTS is not on the bus after a send: nats: consumer not found`, the live symptom. The full suite (`go test -p 1 ./...`) passes with MESH_TEST_POSTGRES (postgres:17) and MESH_TEST_NATS (nats:2.11 -js), and vet and gofmt are clean. ## Rollout Once merged and the controller is delivered, `push novox` creates `novox_messenger` and D6's `bus.EVENTS.novox_messenger.missing` clears at the next self-check. No restart is needed beyond the controller's own upgrade, and that upgrade's start raise would also create it.
mesh-admin added 1 commit 2026-10-06 09:46:08 +00:00
A module or seat holder assigned after the controller started was sent
its declaration and found nothing to bind: messenger on novox
("consumer novox_messenger not found", 2026-10-06) and every first
build-agent holder (2026-10-03). assertBusObjects ran only in the start
raise; a push ensured a module's consumer only when a bus credential was
minted, which a module carried by the runtime never is.

The send's grant now runs the same derivation (assertOnSend) before the
memberships, on every push, cascade, plan send and rotation. A failure
is said in the send's output and raised as bus.objects.unasserted, which
the next send that asserts everything clears; the send itself goes on,
because the objects are the mesh's and holding every machine back for
one would turn one fault into all. Module consumers are each tried and
every failure named. The start raise stays as it was.
mesh-admin merged commit 722682f1c4 into main 2026-10-06 09:47:01 +00:00
mesh-admin deleted branch fix/issue-208-bus-objects-on-send 2026-10-06 09:47:02 +00:00
Author
Contributor

Delivery novox/mesh-controller@722682f1c4ca — held since 2026-10-06T23:05:28Z

Held for a person: merged without a passing check: only a person decides that it goes on — mesh-delivery.release with why lets it go on.

Delivery plan — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step

  • tier 0: mesh-controller
  • tier 1: build-agent
  • tier 2: route-proxy
  • ace: receives build-agent, route-proxy
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent, route-proxy
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • route-proxy provides route: its consumers are sent again after it
  • route-proxy keeps data (ADR 0233): what it holds is backed up before it moves

Transitions

  • 2026-10-06 23:05 (new) → proposed (announced): novox/mesh-controller#81 merged; its head was not heard by this owner, so it is made from the forge's word
  • 2026-10-06 23:05 proposed → held (merged-unchecked): merged without a passing check — a verdict known with it is taken first: only a person decides that it goes on

The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.

<!-- mesh-delivery:view --> **Delivery** `novox/mesh-controller@722682f1c4ca` — **held** since 2026-10-06T23:05:28Z Held for a person: merged without a passing check: only a person decides that it goes on — `mesh-delivery.release` with why lets it go on. **Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - tier 2: route-proxy - ace: receives build-agent, route-proxy - g14: receives build-agent - novox: receives mesh-controller, build-agent, route-proxy - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - route-proxy provides route: its consumers are sent again after it - route-proxy keeps data (ADR 0233): what it holds is backed up before it moves **Transitions** - 2026-10-06 23:05 (new) → proposed (announced): novox/mesh-controller#81 merged; its head was not heard by this owner, so it is made from the forge's word - 2026-10-06 23:05 proposed → held (merged-unchecked): merged without a passing check — a verdict known with it is taken first: only a person decides that it goes on The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
This repo is archived. You cannot comment on pull requests.
No Reviewers
1 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-controller#81