enginereads: the event heard is kept in the node's statement (issue 1975 note 6)
mesh/delivery merged: source.main moved to novox/mesh-contracts@0e12819cc727
mesh/delivery-group group fix/engine-reads-and-compositions merged: every member is merged
mesh/check checking 04c6a1eead98 on 0e12819cc727 | novox/mesh-contracts#8 in group fix/engine-reads-and-compositions
mesh/delivery merged: source.main moved to novox/mesh-contracts@0e12819cc727
mesh/delivery-group group fix/engine-reads-and-compositions merged: every member is merged
mesh/check checking 04c6a1eead98 on 0e12819cc727 | novox/mesh-contracts#8 in group fix/engine-reads-and-compositions
Builds: issue 1975 note 6 (the lead's review round 1; no design line). The engine 'not yet' half is dropped there. Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -143,6 +143,15 @@ type Statement struct {
|
||||
// EventAt is when the part last heard the seat's event manifest-reads-changed for the node, beside when it asked and
|
||||
// what the engine stated then (issue 1966 note 170: was the event heard, and was it asked again after it).
|
||||
EventAt time.Time `json:"event-at,omitzero"`
|
||||
// Heard is the newest event heard of the node with what it says the engine states, kept so the state shows what the
|
||||
// rules read while the statement is behind it (issue 1975 note 6), and so a part that starts again holds it.
|
||||
Heard *HeardEvent `json:"heard,omitempty"`
|
||||
}
|
||||
|
||||
// HeardEvent is an event manifest-reads-changed as the part kept it: when the engine said it and what it said.
|
||||
type HeardEvent struct {
|
||||
At time.Time `json:"at"`
|
||||
ManifestReads []string `json:"manifest-reads,omitempty"`
|
||||
}
|
||||
|
||||
// Again is a person's ask again of one node, written by the tool and answered by the part that keeps the state.
|
||||
@@ -473,6 +482,11 @@ func (k *Keeper) take(s Statement) {
|
||||
if at := k.eventAt[s.Node]; at.After(s.EventAt) {
|
||||
s.EventAt = at
|
||||
}
|
||||
if e, ok := k.said[s.Node]; ok {
|
||||
if at, err := time.Parse(time.RFC3339Nano, e.At); err == nil {
|
||||
s.Heard = &HeardEvent{At: at.UTC(), ManifestReads: slices.Clone(e.ManifestReads)}
|
||||
}
|
||||
}
|
||||
if !s.Answered {
|
||||
s.UnansweredSince = s.AskedAt
|
||||
if old, ok := k.by[s.Node]; ok && !old.Answered && !old.UnansweredSince.IsZero() {
|
||||
@@ -481,6 +495,7 @@ func (k *Keeper) take(s Statement) {
|
||||
}
|
||||
if old, ok := k.by[s.Node]; ok && !s.Answered && old.From != FromNone {
|
||||
old.Answered, old.AskedAt, old.Words, old.AskAgain, old.UnansweredSince = false, s.AskedAt, s.Words, nil, s.UnansweredSince
|
||||
old.Heard = s.Heard
|
||||
s = old
|
||||
}
|
||||
k.by[s.Node] = s
|
||||
@@ -529,6 +544,15 @@ func (k *Keeper) Heard(node string, raw []byte, deleted bool) {
|
||||
_, had := k.by[node]
|
||||
if !had {
|
||||
k.by[node] = s
|
||||
if h := s.Heard; h != nil && !h.At.IsZero() {
|
||||
if k.said == nil {
|
||||
k.said, k.catching = map[string]nodeengine.ManifestReadsChangedEvent{}, map[string]int{}
|
||||
}
|
||||
if _, ok := k.said[node]; !ok {
|
||||
k.said[node] = nodeengine.ManifestReadsChangedEvent{V: ContractVersion, Node: node, Why: ReadsStarted,
|
||||
At: h.At.UTC().Format(time.RFC3339Nano), ManifestReads: slices.Clone(h.ManifestReads)}
|
||||
}
|
||||
}
|
||||
}
|
||||
k.mu.Unlock()
|
||||
if !had && k.Changed != nil {
|
||||
|
||||
@@ -246,6 +246,22 @@ func TestAStatementOlderThanAHeardEventDoesNotStand(t *testing.T) {
|
||||
t.Errorf("after the event's ask: %v, %d calls", s.ManifestReads, calls)
|
||||
}
|
||||
|
||||
// The event heard is in the node's statement as the part keeps it, and a part that starts again holds it.
|
||||
var kept []byte
|
||||
keeper := &Keeper{Sync: true, Put: func(_ string, v []byte) error { kept = v; return nil }, Call: func(string, string, any) (json.RawMessage, error) {
|
||||
return answer(`"seat:a"`, early), nil
|
||||
}}
|
||||
keeper.HeardChanged(event)
|
||||
keeper.AskNode("ace", "x")
|
||||
if st, err := Decode(kept); err != nil || st.Heard == nil || len(st.Heard.ManifestReads) != 2 {
|
||||
t.Errorf("the kept statement shows the event heard as %+v, %v", st.Heard, err)
|
||||
}
|
||||
restarted := &Keeper{Sync: true}
|
||||
restarted.Heard("ace", kept, false)
|
||||
if got := restarted.Reads([]string{"ace"})["ace"]; len(got) != 2 {
|
||||
t.Errorf("a part started again reads %v, want the event's", got)
|
||||
}
|
||||
|
||||
// An engine that answers the old statement to every ask: the event's reads stand, the asks are bounded.
|
||||
calls = 0
|
||||
stuck := &Keeper{Sync: true, Call: func(string, string, any) (json.RawMessage, error) {
|
||||
|
||||
Reference in New Issue
Block a user