AM-H1: the node-engine's composition names the withheld subjects, read through mesh-grants' withheld
The node-engine never reads the grants part's state: it asks the seat's verb withheld in its own name, and says withheld unknown when nothing answers. Builds: design 46 7 §6 (the composition reads them through the verb withheld; the node-engine's manifest gains mesh-grants in uses; "withheld unknown: the grants part does not answer"), lead answer A-239 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -18,6 +18,7 @@ import (
|
||||
occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"github.com/novox/mesh-host/internal/accounts"
|
||||
"github.com/novox/mesh-host/internal/agent"
|
||||
"github.com/novox/mesh-host/internal/apply"
|
||||
"github.com/novox/mesh-host/internal/link"
|
||||
"github.com/novox/mesh-host/internal/selfcompose"
|
||||
@@ -284,7 +285,7 @@ const compositionReadFor = 20 * time.Second
|
||||
|
||||
// compositionFrom answers the verb composition from the node agent that runs now (issue 774); refused in words while
|
||||
// none does.
|
||||
func compositionFrom(current *agentNow) func(module, digest string, diff bool) (any, error) {
|
||||
func compositionFrom(current *agentNow, withheld func(ctx context.Context, module string) ([]link.WithheldSubject, string)) func(module, digest string, diff bool) (any, error) {
|
||||
return func(module, digest string, diff bool) (any, error) {
|
||||
ag := current.get()
|
||||
if ag == nil {
|
||||
@@ -292,7 +293,17 @@ func compositionFrom(current *agentNow) func(module, digest string, diff bool) (
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), compositionReadFor)
|
||||
defer cancel()
|
||||
return ag.Composition(ctx, module, digest, diff)
|
||||
view, err := ag.Composition(ctx, module, digest, diff)
|
||||
if err != nil {
|
||||
return view, err
|
||||
}
|
||||
// And what the grants part withholds of it here, through its verb withheld (design 46 approval mark §6).
|
||||
subjects, unknown := withheld(ctx, module)
|
||||
for _, s := range subjects {
|
||||
view.Withheld = append(view.Withheld, agent.WithheldView{Subject: s.Subject, Why: s.Why})
|
||||
}
|
||||
view.WithheldUnknown = unknown
|
||||
return view, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1022,7 +1022,7 @@ func runLink(ctx context.Context, opts options) error {
|
||||
queue.Acts = engineActs(opts, mine.Node, queue, setuidSearch)
|
||||
go reconcileActs(ctx, queue.Acts)
|
||||
current := &agentNow{}
|
||||
queue.Acts.Composition = compositionFrom(current)
|
||||
queue.Acts.Composition = compositionFrom(current, queue.WithheldOf)
|
||||
queue.Acts.Compose = composeFrom(current, &composerNow)
|
||||
queue.Acts.Packages = packagesFrom(mine.Node, queue, &composerNow)
|
||||
queue.Acts.FirstViews, queue.Acts.EntryAgain = firstViewsFrom(&composerNow), entryAgainFrom(&composerNow)
|
||||
|
||||
@@ -44,6 +44,17 @@ type CompositionView struct {
|
||||
// Own is, of the node's own composition (module node), its own fields beside what runs names (issue 774: ace's
|
||||
// own composition dropped the provisioner client's account, and nothing read could show it).
|
||||
Own *OwnView `json:"own,omitempty"`
|
||||
// Withheld are the module's marked subjects the grants part withholds from this node's runtime, each with why, read
|
||||
// through the seat mesh-grants' verb withheld; WithheldUnknown says when that verb did not answer (design 46 approval
|
||||
// mark §6, lead answer A-239).
|
||||
Withheld []WithheldView `json:"withheld,omitempty"`
|
||||
WithheldUnknown string `json:"withheld-unknown,omitempty"`
|
||||
}
|
||||
|
||||
// WithheldView is one marked subject withheld, and why.
|
||||
type WithheldView struct {
|
||||
Subject string `json:"subject"`
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// OwnView is the node's own fields of a composition (engcomp.NodeOwn) beside what runs of them, and what the node's
|
||||
|
||||
@@ -139,4 +139,29 @@ func TestNatsAMoveIsAskedOfTheNodeAndTheVaultIsAskedInItsName(t *testing.T) {
|
||||
if _, err := (&Session{Membership: Membership{Node: "nowhere"}}).AskVault(t.Context(), "move-in", nil); !errors.Is(err, ErrNotAsked) {
|
||||
t.Fatalf("a queue with no link asked the vault: %v", err)
|
||||
}
|
||||
// AM-H1 (design 46 approval mark §6, lead answer A-239): the composition reads what the grants part withholds through
|
||||
// its verb withheld, asked in the engine's name, and says withheld unknown when nothing answers.
|
||||
grants, err := conn.Subscribe("mesh.seat.mesh-grants.call.withheld.node~"+m.Node, func(msg *nats.Msg) {
|
||||
_ = msg.Respond([]byte(`{"result":{"node":"` + m.Node + `","modules":[{"module":"shell","subjects":[` +
|
||||
`{"subject":"mesh.mod.shell.tool.execute","why":"its tool runner does not state approval-mark/1"}]}]}}`))
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := conn.Flush(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, unknown := q.WithheldOf(t.Context(), "shell"); unknown != "" || len(got) != 1 || got[0].Subject != "mesh.mod.shell.tool.execute" {
|
||||
t.Errorf("withheld of shell = %+v (%q)", got, unknown)
|
||||
}
|
||||
if got, unknown := q.WithheldOf(t.Context(), "other"); unknown != "" || len(got) != 0 {
|
||||
t.Errorf("withheld of a module the grants part names none of = %+v (%q)", got, unknown)
|
||||
}
|
||||
_ = grants.Unsubscribe()
|
||||
if err := conn.Flush(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, unknown := q.WithheldOf(t.Context(), "shell"); unknown != WithheldUnknown || got != nil {
|
||||
t.Errorf("with the grants part down, withheld = %+v (%q)", got, unknown)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// **The composition names the withheld subjects** (design 46 approval mark §6, lead answer A-239): the node-engine reads them
|
||||
// through the seat mesh-grants' verb withheld, never from the grants part's state; when the verb does not answer, it says so.
|
||||
|
||||
// GrantsSeat is the seat of the grants part, whose verb withheld names the marked subjects withheld from a node's runtime.
|
||||
const GrantsSeat = "mesh-grants"
|
||||
|
||||
// WithheldUnknown is what a composition says when the grants part does not answer withheld (design 46 approval mark §6).
|
||||
const WithheldUnknown = "withheld unknown: the grants part does not answer"
|
||||
|
||||
// WithheldSubject is one marked subject withheld from this node's runtime, and why.
|
||||
type WithheldSubject struct {
|
||||
Subject string `json:"subject"`
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// WithheldOf is what the grants part withholds of module on this node, asked through its verb withheld: the subjects, or,
|
||||
// when the verb does not answer or answers what is not its answer, WithheldUnknown as unknown.
|
||||
func (q *Session) WithheldOf(ctx context.Context, module string) (subjects []WithheldSubject, unknown string) {
|
||||
raw, err := q.askCalled(ctx, GrantsSeat, "withheld", map[string]string{"node": q.Membership.Node}, VaultWithin,
|
||||
"the grants part's withheld")
|
||||
if err != nil {
|
||||
return nil, WithheldUnknown
|
||||
}
|
||||
var answer struct {
|
||||
Node string `json:"node"`
|
||||
Modules []struct {
|
||||
Module string `json:"module"`
|
||||
Subjects []WithheldSubject `json:"subjects"`
|
||||
} `json:"modules"`
|
||||
}
|
||||
if json.Unmarshal(raw, &answer) != nil || answer.Node != q.Membership.Node {
|
||||
return nil, WithheldUnknown
|
||||
}
|
||||
for _, m := range answer.Modules {
|
||||
if m.Module == module {
|
||||
subjects = append(subjects, m.Subjects...)
|
||||
}
|
||||
}
|
||||
return subjects, ""
|
||||
}
|
||||
@@ -39,6 +39,9 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"uses": [
|
||||
"mesh-grants"
|
||||
],
|
||||
"invokes": [
|
||||
"seat:mesh-catalog.entry",
|
||||
{"verb": "seat:mesh-catalog.revision", "fields": ["none-yet"]},
|
||||
|
||||
Reference in New Issue
Block a user