E-1: the tool runner's manifest names no waits and keeps its approvals (issue 1966)

module.json drops waits and its five families, uses operator-channel for the
asks of marked waits, and keeps the per-machine state approvals (48 hours) in
its old state section; the manifest test holds each change.

Builds: design 34 every-event §11, §19 (the manifest test); design 46 7-approval-mark §7, §13 (manifest entry)

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 05:00:03 +01:00
parent 5bf7a4b656
commit 3f89c96d20
3 changed files with 59 additions and 27 deletions
@@ -8,19 +8,28 @@ import (
"testing"
)
// novox/hq ADR 0367 §5: the tool runner's manifest names the families a session waits on today, one by one,
// and the asker's `decided` for the claude-code module alone. Adding one is a reviewed change to this file
// (§6), so this list changes with it. The delivery's transitions are waited on under the gate's name alone (ADR 0450 §8, issues 1350
// and 1406): today's module is forgotten.
func TestTheManifestNamesTheFirstFamiliesOneByOne(t *testing.T) {
// The tool runner's manifest names no events to wait on (novox/hq ADR 0670 §2): a session waits on any event by its
// name. It holds each change of design 34 every-event §11 (E-1): no `waits`, `operator-channel` in `uses` for the asks of
// marked waits, the per-machine state `approvals` in the old `state` section with its 48 hours and none in `requires`
// (design 46 7-approval-mark §13), and the finding stream (design 47 G §387).
func TestTheManifestNamesNoWaitsAndKeepsItsApprovals(t *testing.T) {
raw, err := os.ReadFile("../../module.json")
if err != nil {
t.Fatal(err)
}
var keys map[string]json.RawMessage
if err := json.Unmarshal(raw, &keys); err != nil {
t.Fatal(err)
}
if _, ok := keys["waits"]; ok {
t.Error("the manifest still carries waits (novox/hq ADR 0670 §2)")
}
var m struct {
Module string `json:"module"`
Waits []string `json:"waits"`
Consumes []string `json:"consumes"`
Module string `json:"module"`
Uses []string `json:"uses"`
State []json.RawMessage `json:"state"`
Requires []json.RawMessage `json:"requires"`
Consumes []string `json:"consumes"`
Streams []struct {
Name string `json:"name"`
Events []string `json:"events"`
@@ -29,20 +38,33 @@ func TestTheManifestNamesTheFirstFamiliesOneByOne(t *testing.T) {
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatal(err)
}
if m.Module != "node-tools" || !reflect.DeepEqual(m.Uses, []string{"operator-channel"}) {
t.Errorf("%s uses %v, want operator-channel (design 46 7-approval-mark §7)", m.Module, m.Uses)
}
type state struct {
Name string `json:"name"`
PerMachine bool `json:"per-machine"`
TTLSeconds int `json:"ttl-seconds"`
}
var approvals []state
for _, r := range m.State {
var s state
if json.Unmarshal(r, &s) == nil && s.Name == "approvals" {
approvals = append(approvals, s)
}
}
if want := []state{{Name: "approvals", PerMachine: true, TTLSeconds: 172800}}; !reflect.DeepEqual(approvals, want) {
t.Errorf("the old state section's approvals is %+v, want %+v (design 46 7-approval-mark §13)", approvals, want)
}
for _, r := range m.Requires {
if strings.Contains(string(r), `"approvals"`) {
t.Errorf("approvals is asked in requires too: %s", r)
}
}
if len(m.Streams) != 1 || m.Streams[0].Name != "node-tools-findings" ||
!reflect.DeepEqual(m.Streams[0].Events, []string{"finding-written.*", "finding-cleared.*", "findings-stated.*"}) {
t.Errorf("the finding stream is %+v, want node-tools-findings with the three finding events (design 47 G §387)", m.Streams)
}
want := []string{"mesh-knowledge.reviewed", "mesh-delivery-gate.transition", "gitea.forge.status",
"mesh-self-check.checked", "operator-channel.decided.claude-code"}
if m.Module != "node-tools" || !reflect.DeepEqual(m.Waits, want) {
t.Errorf("%s waits on %v, want %v", m.Module, m.Waits, want)
}
for _, w := range m.Waits {
if strings.ContainsAny(w, "*>") {
t.Errorf("a family named by a wildcard: %s", w)
}
}
// A wait is no consumer (ADR 0367 §2): what it consumes is the assigner's two views families (ADR 0615 §5, issue 1966
// TLS-5), the events of the catalog half's branch follower (design 47 G §196, §614: `catalog-revision`,
// `registration-changed`, and the gate's `source-moved`; never the indexer's `views.modules` or `views.manifests`), the two
+11 -2
View File
@@ -126,7 +126,7 @@ func TestTheOfferServesThePortTheManifestServes(t *testing.T) {
var m struct {
Serves map[string]map[string]any `json:"serves"`
Moved bool `json:"moved"`
State []string `json:"state"`
State []json.RawMessage `json:"state"`
Reads []string `json:"reads"`
Provides []struct {
Name string `json:"name"`
@@ -172,9 +172,18 @@ func TestTheOfferServesThePortTheManifestServes(t *testing.T) {
// Every bucket the sdk's helper reaches is a state the manifest keeps or reads, by the sdk's names: the provider's
// own and the seat mesh-provisioner's provisions and plans (issue 1954 L6-T1); the controller's assignments no more.
declared := map[string]bool{}
for _, n := range append(m.State, m.Reads...) {
for _, n := range m.Reads {
declared[n] = true
}
for _, r := range m.State { // an old state entry is its name, or an object naming it (approvals, per machine)
var e struct {
Name string `json:"name"`
}
if json.Unmarshal(r, &e.Name) != nil {
_ = json.Unmarshal(r, &e)
}
declared[e.Name] = true
}
for bucket, want := range map[string]string{
provider.BucketOf(Module): "bound",
provisions.ProvisionsBucketOf(Provisioner): "mesh-provisioner.provisions",
+8 -7
View File
@@ -6,12 +6,8 @@
"invokes": [
"*"
],
"waits": [
"mesh-knowledge.reviewed",
"mesh-delivery-gate.transition",
"gitea.forge.status",
"mesh-self-check.checked",
"operator-channel.decided.claude-code"
"uses": [
"operator-channel"
],
"provides": [
{
@@ -68,7 +64,12 @@
},
"state": [
"bound",
"findings"
"findings",
{
"name": "approvals",
"per-machine": true,
"ttl-seconds": 172800
}
],
"consumes": [
"mesh-catalog.catalog-revision",