Fix 9.4a: views: the entry-not-found finding and a reader's first views say added lists (entries by fingerprint)
Builds: design 47 G A-103, A-15, §614 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -20,6 +20,9 @@ const (
|
||||
// ViewWaitsKind is a part's finding of a view it has waited for past WaitsBound (design 47 *A part with no view says it
|
||||
// waits*).
|
||||
ViewWaitsKind = "view-waits"
|
||||
// EntryNotFoundKind is a part's finding of an entry by fingerprint the seat mesh-catalog's `entry` answered it does not hold
|
||||
// (design 47 G §614, A-15, A-103): scope the part, id the fingerprint.
|
||||
EntryNotFoundKind = "entry-not-found"
|
||||
)
|
||||
|
||||
// viewFinding is what a views part says is wrong with a state it owns or follows, in plain fields: kind and id name it, summary
|
||||
@@ -99,3 +102,18 @@ func viewWaitsFinding(part string, s Status, at time.Time) viewFinding {
|
||||
part, s.Owner, s.State, s.WaitsFor, s.WaitingSince.UTC().Format(time.RFC3339), cmp.Or(s.View, "none"))
|
||||
return viewFinding{kind: ViewWaitsKind, id: part + "." + s.Owner + "." + s.State, summary: summary, headline: fmt.Sprintf("%s waits for a view of %s", part, s.Owner), at: at.UTC()}
|
||||
}
|
||||
|
||||
// WriteEntryNotFound writes the part's finding that the catalog holds no entry for fingerprint, asked since `since`
|
||||
// (design 47 G A-103); ClearEntryNotFound clears it, when nothing the part holds names the fingerprint or a later ask answers.
|
||||
func WriteEntryNotFound(w WireWriter, fingerprint string, since, at time.Time) error {
|
||||
module, _ := w.ModuleNode()
|
||||
return viewFinding{kind: EntryNotFoundKind, id: fingerprint,
|
||||
summary: fmt.Sprintf("%s holds a build of fingerprint %s that the catalog holds no entry for (asked since %s): what it needs of the entry stands still",
|
||||
module, fingerprint, since.UTC().Format(time.RFC3339)),
|
||||
headline: fmt.Sprintf("%s cannot find the catalog entry of a build", module), at: at.UTC()}.put(w)
|
||||
}
|
||||
|
||||
// ClearEntryNotFound clears the finding WriteEntryNotFound wrote for fingerprint.
|
||||
func ClearEntryNotFound(w WireWriter, fingerprint string) error {
|
||||
return viewFinding{kind: EntryNotFoundKind, id: fingerprint}.drop(w)
|
||||
}
|
||||
|
||||
+26
-1
@@ -28,6 +28,8 @@ type Reader struct {
|
||||
// says it in the log only.
|
||||
Findings WireWriter
|
||||
Logf func(string, ...any)
|
||||
|
||||
rows map[string]func() any // the lists Rows added to the first views (guarded by mu)
|
||||
// Changed is told of each key a followed state changes, after the picture holds it (state is `<owner>.<state>`).
|
||||
Changed func(state, key string, value json.RawMessage, deleted bool)
|
||||
// Acts records a re-read by hand before it is asked (nil records nothing
|
||||
@@ -574,13 +576,36 @@ func (r *Reader) Statuses() []Status {
|
||||
return out
|
||||
}
|
||||
|
||||
// Rows adds a list the part's `<prefix>_first_views` says beside the views' statuses, under name (design 47 G A-103: the
|
||||
// entries by fingerprint the catalog half's follower keeps). rows is called at each reading.
|
||||
func (r *Reader) Rows(name string, rows func() any) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if r.rows == nil {
|
||||
r.rows = map[string]func() any{}
|
||||
}
|
||||
r.rows[name] = rows
|
||||
}
|
||||
|
||||
// firstViews is what `<prefix>_first_views` answers: the views' statuses and each list added by Rows.
|
||||
func (r *Reader) firstViews() map[string]any {
|
||||
out := map[string]any{"views": r.Statuses()}
|
||||
r.mu.Lock()
|
||||
rows := maps.Clone(r.rows)
|
||||
r.mu.Unlock()
|
||||
for name, f := range rows {
|
||||
out[name] = f()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Tools are the reader's two tools (design 25 § 12.2): `<prefix>_first_views` says where each first view stands and
|
||||
// `<prefix>_first_view_again` asks one again by hand (a hand act, counted in the status).
|
||||
func (r *Reader) Tools(prefix string) []stdio.Tool {
|
||||
return []stdio.Tool{
|
||||
{Name: prefix + "_first_views",
|
||||
Description: "Where each first view this module keeps stands (novox/hq ADR 0615): the owner and its tool, the revision the picture holds, when it was asked and answered, how many asks (by hand among them) and whether its condition is raised. (r)",
|
||||
Run: func(map[string]any) (any, error) { return map[string]any{"views": r.Statuses()}, nil }},
|
||||
Run: func(map[string]any) (any, error) { return r.firstViews(), nil }},
|
||||
{Name: prefix + "_first_view_again",
|
||||
Description: "Ask a first view again by hand, for the state named (<owner>.<state>), or every one with none: a hand act, for a picture the operator doubts. Answers where it stands after. (a)",
|
||||
Input: map[string]any{"type": "object", "properties": map[string]any{
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -558,6 +559,19 @@ func subViews(t *testing.T) {
|
||||
if err != nil || len(again.(map[string]any)["views"].([]Status)) != 1 || f.Status().ByHand != 1 || acted != 1 {
|
||||
t.Errorf("asked again by hand: %v, %v", again, err)
|
||||
}
|
||||
// a list added by Rows is said beside the statuses (design 47 G A-103), and the entry-not-found finding is written and cleared
|
||||
r.Rows("entries", func() any { return []string{"fp1"} })
|
||||
seen, _ := r.Tools("reader")[0].Run(nil)
|
||||
if got := seen.(map[string]any)["entries"]; !reflect.DeepEqual(got, []string{"fp1"}) {
|
||||
t.Errorf("first views entries = %v", got)
|
||||
}
|
||||
nf := &findingsMap{}
|
||||
if err := WriteEntryNotFound(nf, "fp1", now(), now()); err != nil || nf.m[EntryNotFoundKind+"/fp1"].Summary == "" {
|
||||
t.Errorf("entry-not-found not written: %+v, %v", nf.m, err)
|
||||
}
|
||||
if err := ClearEntryNotFound(nf, "fp1"); err != nil || len(nf.m) != 0 {
|
||||
t.Errorf("entry-not-found not cleared: %+v, %v", nf.m, err)
|
||||
}
|
||||
})
|
||||
t.Run("a process's reader follows the read tool its caller names, reads the state as a read-only bucket, each key's written time handed on, and opens no other", func(t *testing.T) {
|
||||
r := &ProcessReader{Module: "reader", Bound: time.Nanosecond, Wait: time.Millisecond, Now: now,
|
||||
|
||||
Reference in New Issue
Block a user