diff --git a/node-tools/internal/console/firstviews.go b/node-tools/internal/console/firstviews.go index 0a7b05d..4a36fe1 100644 --- a/node-tools/internal/console/firstviews.go +++ b/node-tools/internal/console/firstviews.go @@ -16,6 +16,10 @@ type FirstViewer interface { Again(ctx context.Context, why string) []error } +// entryLister is a FirstViewer that also keeps entries by fingerprint (design 47 G A-103): mesh_first_views lists them with their +// state and since when. +type entryLister interface{ Entries() any } + // firstViewsAnswer is mesh_first_views and mesh_first_view_again. func (s *Surface) firstViewsAnswer(ctx context.Context, name string, args map[string]any) map[string]any { fv, ok := s.src.(FirstViewer) @@ -24,7 +28,11 @@ func (s *Surface) firstViewsAnswer(ctx context.Context, name string, args map[st "another source") } if name == verbFirstViews { - return answerText(map[string]any{"first views": fv.Status()}) + out := map[string]any{"first views": fv.Status()} + if el, ok := fv.(entryLister); ok { + out["entries"] = el.Entries() + } + return answerText(out) } why, _ := args["why"].(string) if why = strings.TrimSpace(why); why == "" { diff --git a/node-tools/internal/console/firstviews_test.go b/node-tools/internal/console/firstviews_test.go index 1af9b3b..abd7af9 100644 --- a/node-tools/internal/console/firstviews_test.go +++ b/node-tools/internal/console/firstviews_test.go @@ -20,6 +20,9 @@ func (v *viewer) Status() []views.Status { return []views.Status{{Owner: "mesh-delivery-assigner", Tool: "assignments", State: "assignments", Revision: 4, Asks: 1}} } +// Entries are the entries by fingerprint the viewer keeps (design 47 G A-103). +func (v *viewer) Entries() any { return []string{"fp:a asked"} } + func (v *viewer) Again(_ context.Context, why string) []error { v.asked = append(v.asked, why) return nil @@ -35,7 +38,7 @@ func TestTheConsoleShowsAndAsksAgainTheRunnersFirstViews(t *testing.T) { v := &viewer{} var recorded []map[string]any s := &Surface{src: v, who: "desk.node-tools", Record: func(a map[string]any) error { recorded = append(recorded, a); return nil }} - if got := text(s.discover(context.Background(), "mesh_first_views", nil)); !strings.Contains(got, "mesh-delivery-assigner") || !strings.Contains(got, "assignments") { + if got := text(s.discover(context.Background(), "mesh_first_views", nil)); !strings.Contains(got, "mesh-delivery-assigner") || !strings.Contains(got, "assignments") || !strings.Contains(got, "fp:a asked") { t.Errorf("mesh_first_views shows %s", got) } got := s.discover(context.Background(), "mesh_first_view_again", map[string]any{"why": "doubted"}) diff --git a/node-tools/internal/firstview/firstview.go b/node-tools/internal/firstview/firstview.go index 77b7427..9e60efd 100644 --- a/node-tools/internal/firstview/firstview.go +++ b/node-tools/internal/firstview/firstview.go @@ -289,6 +289,14 @@ func (v *Views) Status() []views.Status { return out } +// Entries are the entries by fingerprint the catalog half's follower keeps, with their state and since when (design 47 G A-103). +func (v *Views) Entries() any { + if v.branches == nil { + return []meshcatalogclient.EntryStatus{} + } + return v.branches.EntryStatuses() +} + // all is every first view held now: the owners', then the adopted ones. func (v *Views) all() []*views.Follower { v.mu.Lock() @@ -436,30 +444,21 @@ func (v *Views) Modules() (map[string]catalogseat.CatalogEntry, error) { if v.branches == nil { return nil, fmt.Errorf("the tool runner does not follow the catalog half") } - followed := v.branches.Followed() - if len(followed) == 0 { - return nil, fmt.Errorf("the catalog half names no watched branch yet (the registrations of %s are asked until they answer)", catalogseat.Seat) + if err := v.branches.Ready(); err != nil { + return nil, err } out := map[string]catalogseat.CatalogEntry{} - var waiting []string - for _, branch := range followed { + for _, branch := range v.branches.Followed() { c, ok := v.branches.Catalog(branch) if !ok { continue } - half, held := c.View() - if !held { - waiting = append(waiting, branch) - continue - } + half, _ := c.View() // a branch at none-yet is ready and empty (ยง614) for m, e := range half.Entries { if _, taken := out[m]; !taken { out[m] = e } } } - if len(waiting) > 0 { - return nil, fmt.Errorf("the catalog half has no first view of %s yet", strings.Join(waiting, ", ")) - } return out, nil }