mesh-config keeps held values in the seat's typed state kept, through the config client's key and codec (B5g, B2f; ADR 0565 §5, ADR 0634 §4; issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -580,7 +580,7 @@ func (c *Config) propose(call Call, action string) (Proposal, oc.Ask, []string,
|
||||
}
|
||||
trusted = false
|
||||
k, kept := s.Kept[keptKey(call.Pull, level.Node, level.Module, call.Key)]
|
||||
was, had = Value{Value: k.Value}, kept
|
||||
was, had = Value{Value: heldValue(k)}, kept
|
||||
} else {
|
||||
var derr error
|
||||
r, trusted, derr = c.declared(s, level, call.Key)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.novox.be/novox/mesh-contracts/go/clients/meshconfigclient"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -61,32 +62,25 @@ type PullDecl struct {
|
||||
At time.Time `json:"at"`
|
||||
}
|
||||
|
||||
// KeptValue is an approved held value, kept apart from the values mesh-config resolves, in this module's own layout
|
||||
// (the owner's: its state `kept`), which the checker and the assigner's composition check read (novox/hq ADR 0565 §5).
|
||||
type KeptValue struct {
|
||||
Pull string `json:"pull"`
|
||||
Head string `json:"head"`
|
||||
Digest string `json:"declaration"`
|
||||
Node string `json:"node"`
|
||||
Module string `json:"module"`
|
||||
Key string `json:"key"`
|
||||
// Value is the value held, as the operator approved it.
|
||||
Value json.RawMessage `json:"value"`
|
||||
By string `json:"by"`
|
||||
Via string `json:"via"`
|
||||
Why string `json:"why"`
|
||||
// Approved is who approved it, as the proposal kept it.
|
||||
Approved string `json:"approved"`
|
||||
Proposal string `json:"proposal"`
|
||||
At time.Time `json:"at"`
|
||||
}
|
||||
// KeptValue is an approved held value, kept apart from the values mesh-config resolves, in the layout of the seat's state
|
||||
// `kept`, which the checker and the assigner's composition check read (novox/hq ADR 0565 §5, ADR 0634 §4).
|
||||
type KeptValue = meshconfigclient.Kept
|
||||
|
||||
// keptKey is a held value's key in `kept`: the pull request's key form (pullKey), the node, the module and the config
|
||||
// key, joined by dots.
|
||||
func keptKey(pull, node, module, key string) string {
|
||||
return pullKey(pull) + "." + node + "." + module + "." + key
|
||||
k, _ := meshconfigclient.HeldValueKey(pull, node, module, key) // a held value of no pull request has no key, and is found by none
|
||||
return k
|
||||
}
|
||||
|
||||
// heldValue is the value held, as the operator approved it, in its JSON form.
|
||||
func heldValue(k KeptValue) json.RawMessage {
|
||||
raw, _ := meshconfigclient.ValueOf(k) // a value that was read from JSON writes as JSON
|
||||
return raw
|
||||
}
|
||||
|
||||
func heldAt(t time.Time) *time.Time { return &t }
|
||||
|
||||
func keptStateKey(k KeptValue) string { return keptKey(k.Pull, k.Node, k.Module, k.Key) }
|
||||
|
||||
func keptLevel(k KeptValue) Level { return Level{Kind: LevelModule, Node: k.Node, Module: k.Module} }
|
||||
@@ -123,9 +117,9 @@ func (c *Config) readPulls(s *Sources) error {
|
||||
return fmt.Errorf("the pull requests' declarations cannot be read: %w", err)
|
||||
}
|
||||
if err := c.each(KeptState, func(key string, raw []byte) error {
|
||||
var k KeptValue
|
||||
if err := json.Unmarshal(raw, &k); err != nil || k.Pull == "" {
|
||||
return nil
|
||||
k, err := meshconfigclient.DecodeKept(key, raw)
|
||||
if err != nil {
|
||||
return nil // not a held value of the seat's layout under its own key
|
||||
}
|
||||
s.Kept[key] = k
|
||||
return nil
|
||||
@@ -176,11 +170,11 @@ func (c *Config) declarePull(pull, head string, declares map[string]map[string]D
|
||||
switch {
|
||||
case !declared:
|
||||
why = "the head no longer adds or changes its declaration"
|
||||
case declDigest(d) != kv.Digest:
|
||||
case declDigest(d) != kv.Declaration:
|
||||
why = "the head declares the key otherwise than the value was approved against"
|
||||
default:
|
||||
var v any
|
||||
_ = json.Unmarshal(kv.Value, &v)
|
||||
_ = json.Unmarshal(heldValue(kv), &v)
|
||||
if p := d.rule().check(v); len(p) > 0 {
|
||||
why = "the value no longer passes: " + strings.Join(p, "; ")
|
||||
}
|
||||
@@ -208,13 +202,13 @@ func (c *Config) declarePull(pull, head string, declares map[string]map[string]D
|
||||
// config-changed; what could not be done is answered, not dropped.
|
||||
func (c *Config) heldChange(kv KeptValue, action, why, approved string) []string {
|
||||
level := keptLevel(kv)
|
||||
ch := Change{Action: action, Level: &level, Key: kv.Key, Value: kv.Value, By: kv.By, Via: kv.Via, Why: why,
|
||||
ch := Change{Action: action, Level: &level, Key: kv.Key, Value: heldValue(kv), By: kv.By, Via: kv.Via, Why: why,
|
||||
Approved: approved, Pull: kv.Pull, Head: kv.Head, At: c.now(), Moved: []Moved{}}
|
||||
unsaid := c.record(level.ValueKey(kv.Key), ch)
|
||||
cause := "a value held for " + kv.Pull
|
||||
what := fmt.Sprintf("config %s %s of %s for %s, held for %s", action, kv.Key, kv.Module, kv.Node, kv.Pull)
|
||||
if action != ActionDrop {
|
||||
what += " (" + string(kv.Value) + ")"
|
||||
what += " (" + string(heldValue(kv)) + ")"
|
||||
}
|
||||
if err := c.Store.HandAct(what, why, cause); err != nil {
|
||||
unsaid = append(unsaid, "the hand-act log does not hold it: "+err.Error())
|
||||
@@ -260,8 +254,8 @@ type HeldSkipped struct {
|
||||
}
|
||||
|
||||
func heldValueOf(k KeptValue) HeldValue {
|
||||
return HeldValue{Pull: k.Pull, Head: k.Head, Node: k.Node, Module: k.Module, Key: k.Key, Value: compact(k.Value),
|
||||
Approved: k.Approved, By: k.By, Why: k.Why, At: k.At}
|
||||
return HeldValue{Pull: k.Pull, Head: k.Head, Node: k.Node, Module: k.Module, Key: k.Key, Value: compact(heldValue(k)),
|
||||
Approved: k.Approved, By: k.By, Why: k.Why, At: timeOf(k.At)}
|
||||
}
|
||||
|
||||
// Held answers the approved held values for the pull requests asked, at the heads asked, to the controller alone: a
|
||||
@@ -373,13 +367,13 @@ func (c *Config) keep(p Proposal) (string, error) {
|
||||
"propose it again", p.Pull, p.Key)
|
||||
}
|
||||
was, had := s.Kept[keptKey(p.Pull, p.Level.Node, p.Level.Module, p.Key)]
|
||||
if had != p.HadValue || valueDigest(was.Value, had) != p.WasDigest {
|
||||
if had != p.HadValue || valueDigest(heldValue(was), had) != p.WasDigest {
|
||||
return "", fmt.Errorf("the value held for %s changed since the operator was shown the change: propose it "+
|
||||
"again", p.Pull)
|
||||
}
|
||||
kv := KeptValue{Pull: p.Pull, Head: pd.Head, Digest: p.DeclDigest, Node: p.Level.Node, Module: p.Level.Module,
|
||||
kv := KeptValue{Pull: p.Pull, Head: pd.Head, Declaration: p.DeclDigest, Node: p.Level.Node, Module: p.Level.Module,
|
||||
Key: p.Key, Value: p.Value, By: "the operator", Via: p.From, Why: p.Why, Approved: p.Approved, Proposal: p.ID,
|
||||
At: c.now()}
|
||||
At: heldAt(c.now())}
|
||||
if err := c.Store.Put(KeptState, keptStateKey(kv), kv); err != nil {
|
||||
return "", fmt.Errorf("the value was not kept: %v", err)
|
||||
}
|
||||
@@ -434,7 +428,7 @@ func (c *Config) settlePull(pull string, merged bool, head string) []string {
|
||||
why = "the merged head is not known, so the value cannot be held to it"
|
||||
case !strings.EqualFold(head, kv.Head):
|
||||
why = fmt.Sprintf("it is held at head %s and the pull request merged at %s", short(kv.Head), short(head))
|
||||
case !known || !declared || declDigest(d) != kv.Digest:
|
||||
case !known || !declared || declDigest(d) != kv.Declaration:
|
||||
why = "the merged head does not declare the key as the operator was shown"
|
||||
}
|
||||
if why != "" {
|
||||
@@ -444,7 +438,7 @@ func (c *Config) settlePull(pull string, merged bool, head string) []string {
|
||||
continue
|
||||
}
|
||||
vk := keptLevel(kv).ValueKey(kv.Key)
|
||||
v := Value{Value: compact(kv.Value), By: kv.By, Via: kv.Via, Why: kv.Why + " (held for " + kv.Pull +
|
||||
v := Value{Value: compact(heldValue(kv)), By: kv.By, Via: kv.Via, Why: kv.Why + " (held for " + kv.Pull +
|
||||
", merged)", Approved: kv.Approved, At: c.now()}
|
||||
if err := c.Store.Put(ValuesState, vk, v); err != nil {
|
||||
said = append(said, fmt.Sprintf("%s of %s on %s held for %s is not written yet: %v; it is tried "+
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"git.novox.be/novox/mesh-contracts/go/clients/meshconfigclient"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -317,8 +318,11 @@ func TestTheMergeAppliesAHeldValueAndItResolvesOnceTheTrunkDeclaresTheKey(t *tes
|
||||
holdIt(t, c, m, r)
|
||||
approved := func() string {
|
||||
raw, _ := m.Get(KeptState, "novox.mesh-catalog.434.ace.n8n.media-gid")
|
||||
var k KeptValue
|
||||
_ = json.Unmarshal(raw.Value, &k)
|
||||
// the state kept is the seat's layout, under the seat's key: the checker and the assigner read it with this
|
||||
k, err := meshconfigclient.DecodeKept("novox.mesh-catalog.434.ace.n8n.media-gid", raw.Value)
|
||||
if err != nil || string(heldValue(k)) != "2000" || k.Declaration == "" || k.At == nil {
|
||||
t.Fatalf("the held value is not the seat's: %+v, %v", k, err)
|
||||
}
|
||||
return k.Approved
|
||||
}()
|
||||
m.events, m.handActs = nil, nil
|
||||
|
||||
Reference in New Issue
Block a user