From 6c61d9facf30b80d2f63064d7429ecde86a0d8eb Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 00:49:55 +0100 Subject: [PATCH 1/2] S6: the condition keeper reads the node-engine's findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ace's node-engine wrote seat-list-unread in its state findings, and no condition rose: the keeper's observers did not name the node-engine. It now reads mesh-host.findings, so ADR 0636 §1's condition is raised and clears at the first full read (issue 1966). Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- modules/mesh-conditions/cmd/mesh-conditions/keeper.go | 6 +++++- modules/mesh-conditions/cmd/mesh-conditions/keeper_test.go | 3 ++- modules/mesh-conditions/module.json | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/mesh-conditions/cmd/mesh-conditions/keeper.go b/modules/mesh-conditions/cmd/mesh-conditions/keeper.go index 178234acb..006aac7aa 100644 --- a/modules/mesh-conditions/cmd/mesh-conditions/keeper.go +++ b/modules/mesh-conditions/cmd/mesh-conditions/keeper.go @@ -20,6 +20,7 @@ import ( assignerclient "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" mesh "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" stdio "git.novox.be/novox/mesh-sdk/go" + "git.novox.be/novox/mesh-sdk/go/buscompose" "git.novox.be/novox/mesh-sdk/go/motion" cc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" @@ -65,7 +66,10 @@ var Observers = []string{watchmanModule, selfCheckModule, healersModule, assigne // readers' of the indexer's and the assigner's state (the registry milestone's join). builder, indexer, "mesh-delivery-checker", "mesh-delivery-teller", "mesh-delivery-view", enrolment, // The tool runner's and claude-code's own first views (views.UnansweredFinding in their findings, ADR 0615 §6). - "node-tools", "claude-code"} + "node-tools", "claude-code", + // The node-engine's: a node that has read no seat list five minutes after its start, seat-list-unread (ADR 0636 §1; + // issue 1966: written on ace and raised by nobody). + buscompose.EngineModule} // The other states it reads, as `.`. const ( diff --git a/modules/mesh-conditions/cmd/mesh-conditions/keeper_test.go b/modules/mesh-conditions/cmd/mesh-conditions/keeper_test.go index 1b70ecd83..80b893b05 100644 --- a/modules/mesh-conditions/cmd/mesh-conditions/keeper_test.go +++ b/modules/mesh-conditions/cmd/mesh-conditions/keeper_test.go @@ -334,8 +334,9 @@ func TestEveryControllerKindHasItsHome(t *testing.T) { // Each module that writes a reader's unanswered first view (ADR 0615 §6), or that a delivery part's consumer stopped // hearing, is read: the delivery parts and the tool runner by name (other repositories), and every catalogue module // whose code writes a views finding, found in the catalogue's tree, so a new writer cannot go unobserved. + // And the node-engine, whose node writes seat-list-unread when it has read no seat list (ADR 0636 §1, issue 1966). readers := []string{watchman, "mesh-delivery-teller", enrolment, builder, "mesh-delivery-checker", "mesh-delivery-view", - indexer, assignerModule, configModule, vaultModule, gitea, "node-tools"} + indexer, assignerModule, configModule, vaultModule, gitea, "node-tools", "mesh-host"} readers = append(readers, viewFindingWriters(t, "../../..")...) for _, reader := range readers { if !slices.Contains(Observers, reader) { diff --git a/modules/mesh-conditions/module.json b/modules/mesh-conditions/module.json index af7ed4ac5..f6803f865 100644 --- a/modules/mesh-conditions/module.json +++ b/modules/mesh-conditions/module.json @@ -34,6 +34,7 @@ "mesh-enrolment.findings", "node-tools.findings", "claude-code.findings", + "mesh-host.findings", "mesh-healers.attempts", "node-engine.have" ], -- 2.54.0 From c39503e3227c0d6ac653d7d48b7103fd7c5fcad0 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 00:59:07 +0100 Subject: [PATCH 2/2] S7: claude-code's seat-list again answers a proved operator's session claude_code_seat_list_again answered person. and the healers, but every call through the mesh MCP server is proved as its machine's tool runner (issue 627); node-tools now stands for that session (issue 1966). Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- modules/claude-code/cmd/claude-code/insteadof_test.go | 4 +++- modules/claude-code/cmd/claude-code/main.go | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/claude-code/cmd/claude-code/insteadof_test.go b/modules/claude-code/cmd/claude-code/insteadof_test.go index 9ac5a4d37..405ca61cc 100644 --- a/modules/claude-code/cmd/claude-code/insteadof_test.go +++ b/modules/claude-code/cmd/claude-code/insteadof_test.go @@ -109,7 +109,9 @@ func TestTheMeshsToolsAreReadFromTheBus(t *testing.T) { t.Errorf("replacements: %s", got) } t.Run("the again tool answers the operator and the healers only", func(t *testing.T) { - for caller, refused := range map[string]bool{"person.jochen": false, "shanks.mesh-healers": false, "shanks.mesh-config": true} { + // An operator's session through the mesh MCP server is proved as its machine's tool runner (issue 627, issue 1966). + for caller, refused := range map[string]bool{"person.jochen": false, "shanks.node-tools": false, "shanks.mesh-healers": false, + "shanks.mesh-config": true} { if err := seatlist.Refusal("claude_code_seat_list_again", seatListAgainCallers, caller); (err != nil) != refused { t.Errorf("%s: refused=%v, err %v", caller, refused, err) } diff --git a/modules/claude-code/cmd/claude-code/main.go b/modules/claude-code/cmd/claude-code/main.go index af9c534ff..53a18a1a0 100644 --- a/modules/claude-code/cmd/claude-code/main.go +++ b/modules/claude-code/cmd/claude-code/main.go @@ -667,8 +667,10 @@ func refusals(n int) string { return fmt.Sprintf(" (after %d refusal(s))", n) } -// seatListAgainCallers may read the seat list again: every operator's session and the healers (ADR 0636 §7). -var seatListAgainCallers = []string{stdio.AnyPerson, "mesh-healers"} +// seatListAgainCallers may read the seat list again: every operator's session and the healers (ADR 0636 §7). A call through +// the mesh MCP server is proved only as its machine's tool runner (.node-tools, issue 627), so node-tools stands for +// an operator's session until a session is proved (issue 1966). +var seatListAgainCallers = []string{stdio.AnyPerson, "node-tools", "mesh-healers"} // seatListTools are the seat list reader's two tools: what the list holds and what it waits for, and a read now. The // reader starts once the runtime serves, after the tools are listed, so each tool finds it when it is called. -- 2.54.0