C4: a statement keeps since when its engine is unanswered, and the keeper names each node unanswered past a bound while heard (issue 1966, ADR 0668 §3; review round 1, finding 4)

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-10 04:15:01 +01:00
parent c584f4570b
commit c6689bfdb3
2 changed files with 59 additions and 1 deletions
@@ -71,6 +71,30 @@ func TestTheAnswerAndTheEventAreJudged(t *testing.T) {
if during.From != FromRecord || during.Answered || f.Statements()["g14"].From != FromVerb {
t.Errorf("while the kept call asked g14 read %+v; after it %+v", during, f.Statements()["g14"])
}
// ADR 0668 §3: a node unanswered keeps when it first was not, across asks; past the bound it is unanswered while its
// machine is heard, none while away, and none once it answers.
clock := time.Date(2026, 11, 10, 3, 0, 0, 0, time.UTC)
answers := false
u := &Keeper{Sync: true, Now: func() time.Time { return clock }, Call: func(node, _ string, _ any) (json.RawMessage, error) {
if answers {
return json.RawMessage(`{"v":1,"node":"` + node + `","manifest-reads":["a.b"],"stated-at":"` + at + `"}`), nil
}
return nil, context.DeadlineExceeded
}}
first := clock
u.AskNode("ace", "the first view")
clock = clock.Add(6 * time.Minute)
u.AskNode("ace", "asked on")
heard := func(string) bool { return true }
if got := u.Unanswered([]string{"ace"}, heard, 5*time.Minute, clock); !got["ace"].Equal(first) ||
len(u.Unanswered([]string{"ace"}, func(string) bool { return false }, 5*time.Minute, clock)) != 0 {
t.Errorf("unanswered past the bound: %v", got)
}
answers = true
u.AskNode("ace", "answered")
if got := u.Unanswered([]string{"ace"}, heard, 5*time.Minute, clock); len(got) != 0 || !u.Statements()["ace"].UnansweredSince.IsZero() {
t.Errorf("an answered node is unanswered: %v", got)
}
if Verb != "manifest-reads" || Event != "manifest-reads-changed" || Consumes != "node-engine.manifest-reads-changed" || Invoke != "seat:node-engine.manifest-reads" {
t.Errorf("the names are %s %s %s %s", Verb, Event, Consumes, Invoke)
}