Assigner: serves the seat mesh-module-assigner's verb placement
Builds: design 47 G A-62 (§704, §402) Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -357,6 +357,33 @@ func (s *seatTools) doAgain(args map[string]any) (any, error) {
|
||||
return map[string]any{"proposal": p.ID, "again": p.Again, "follow": followProposal(p.ID)}, nil
|
||||
}
|
||||
|
||||
// doPlacement is the verb placement (design 47 G A-62, §704): the module's placements as the assignments rows hold them, one
|
||||
// per branch a row names, with the nodes that run the module from it. A row naming no branch is one placement with the branch
|
||||
// empty, as the row holds it.
|
||||
func (s *seatTools) doPlacement(args map[string]any) (any, error) {
|
||||
module := arg(args, "module")
|
||||
if module == "" {
|
||||
return nil, errors.New("placement takes the module")
|
||||
}
|
||||
all, _, err := s.assignments()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
byBranch := map[string][]string{}
|
||||
for node, a := range all {
|
||||
for _, m := range a.Modules {
|
||||
if m.Module == module {
|
||||
byBranch[m.Branch] = append(byBranch[m.Branch], node)
|
||||
}
|
||||
}
|
||||
}
|
||||
out := mma.PlacementAnswer{Placements: []mma.ModulePlacement{}}
|
||||
for _, branch := range slices.Sorted(maps.Keys(byBranch)) {
|
||||
out.Placements = append(out.Placements, mma.ModulePlacement{Branch: branch, Nodes: slices.Sorted(slices.Values(byBranch[branch]))})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// assignmentsAnswer is what the verb assignments answers.
|
||||
type assignmentsAnswer struct {
|
||||
Keys map[string]keyed `json:"keys,omitempty"`
|
||||
@@ -524,6 +551,7 @@ func (s *seatTools) tools() []stdio.Tool {
|
||||
mma.VerbPropose: func(a map[string]any) (any, error) { return s.doPropose(a, stdio.CallerOf(a)) },
|
||||
mma.VerbWithdraw: func(a map[string]any) (any, error) { return s.doWithdraw(a, stdio.CallerOf(a)) },
|
||||
mma.VerbAgain: s.doAgain,
|
||||
mma.VerbPlacement: s.doPlacement,
|
||||
}
|
||||
var out []stdio.Tool
|
||||
for _, v := range mma.Verbs() {
|
||||
|
||||
@@ -80,6 +80,18 @@ func TestTheSeatsVerbs(t *testing.T) {
|
||||
|
||||
setAssignments(st[mma.StateAssignments], anchor)
|
||||
|
||||
// placement (design 47 G A-62): a module's placements as the rows hold them, one per branch, with the nodes that run it from there.
|
||||
before := maps.Clone(st[mma.StateAssignments].values)
|
||||
setAssignments(st[mma.StateAssignments], mma.Assignments{V: keys.Version, Node: "laptop", Modules: []mma.PlacedModule{{Module: "web", Branch: "feat/x"}}})
|
||||
if got, err := s.doPlacement(map[string]any{"module": "web"}); err != nil ||
|
||||
!reflect.DeepEqual(got, mma.PlacementAnswer{Placements: []mma.ModulePlacement{{Branch: "", Nodes: []string{"anchor"}}, {Branch: "feat/x", Nodes: []string{"laptop"}}}}) {
|
||||
t.Errorf("placement of web = %+v, %v", got, err)
|
||||
}
|
||||
if got, err := s.doPlacement(map[string]any{"module": "ghost"}); err != nil || len(got.(mma.PlacementAnswer).Placements) != 0 {
|
||||
t.Errorf("placement of a module placed nowhere = %+v, %v", got, err)
|
||||
}
|
||||
st[mma.StateAssignments].values = before
|
||||
|
||||
// withdraw and again mark the proposal; the holder ends or judges it.
|
||||
got, err := s.doPropose(map[string]any{"why": "hold", "changes": `[{"op":"hold","node":"anchor","module":"web","claim":"node-shell","scope":"node"}]`}, "op")
|
||||
if err != nil {
|
||||
@@ -201,8 +213,8 @@ func TestTheSeatsVerbs(t *testing.T) {
|
||||
} else if rm := got.(proposed).Proposal; !slices.Equal(plc.Nodes(rm), []string{"joiner"}) || !slices.Equal(plc.RemovedNodes(rm), []string{"joiner"}) {
|
||||
t.Errorf("the remove-node proposed %+v", rm)
|
||||
}
|
||||
// 1126/B8: the seat serves its five verbs; adopt and hand-back went with the controller (ADR 0590).
|
||||
if tools := s.tools(); len(tools) != 5 || tools[2].Name != "mesh-module-assigner.propose" || tools[2].Run == nil {
|
||||
// 1126/B8: the seat serves its six verbs; adopt and hand-back went with the controller (ADR 0590).
|
||||
if tools := s.tools(); len(tools) != 6 || tools[2].Name != "mesh-module-assigner.propose" || tools[2].Run == nil {
|
||||
t.Errorf("the seat's verbs as served: %+v", tools)
|
||||
}
|
||||
// Issue 1966: propose checks its modules against the indexer's first view of modules, every page of it, asked again at
|
||||
|
||||
@@ -88,7 +88,7 @@ func TestManifest(t *testing.T) {
|
||||
// ADR 0565 §7, §9 (issue 1953 D4): and the seat mesh-wants, serving upgrade and bus, and (design 47 lines 1362-1366)
|
||||
// revision, entry and difference from its revisions kept as objects in the old section, form 1, capped at 512 MiB.
|
||||
if len(m.Claims) != 2 || m.Claims[0].Name != meshmoduleassigner.Seat || m.Claims[0].At() != catalogue.ScopeMesh ||
|
||||
!slices.Equal(m.Claims[0].Serves, []string{"assignments", "proposals", "propose", "withdraw", "again"}) ||
|
||||
!slices.Equal(m.Claims[0].Serves, []string{"assignments", "proposals", "propose", "withdraw", "again", "placement"}) ||
|
||||
m.Claims[1].Name != meshwants.Seat || m.Claims[1].At() != catalogue.ScopeMesh || !slices.Equal(m.Claims[1].Serves, []string{"upgrade", "bus", "revision", "entry", "difference", "input"}) {
|
||||
t.Errorf("the assigner claims %+v, want mesh-module-assigner with its five verbs and mesh-wants with its five", m.Claims)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
"proposals",
|
||||
"propose",
|
||||
"withdraw",
|
||||
"again"
|
||||
"again",
|
||||
"placement"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user