From 5f1e12a41f085b76c39869d815a94727c7ba4330 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 12:15:55 +0100 Subject: [PATCH] =?UTF-8?q?G-10=20=C2=A7479=20with=20=C2=A7495:=20view-wai?= =?UTF-8?q?ts=20names=20..;=20a=20branch=20with=20no?= =?UTF-8?q?=20revision=20yet=20raises=20none?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Builds: design 47 G §479, §495 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- go/views/findings.go | 4 ++-- go/views/follow.go | 6 +++++- go/views/views_test.go | 21 +++++++++++++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/go/views/findings.go b/go/views/findings.go index f301f689..01408205 100644 --- a/go/views/findings.go +++ b/go/views/findings.go @@ -82,8 +82,8 @@ func finding(observer, kind, id, summary, headline, explanation string, at time. // part, the owner and the revision, and clears when the part holds it. func ViewWaitsFinding(part string, s Status, at time.Time) Finding { id := part + "." + s.Owner + "." + s.State - summary := fmt.Sprintf("%s waits for %s's view %s since %s and acts on nothing new until it holds it; it holds %s", - part, s.Owner, s.WaitsFor, s.WaitingSince.UTC().Format(time.RFC3339), cmp.Or(s.View, "none")) + summary := fmt.Sprintf("%s waits for the view %s.%s at revision %s since %s and acts on nothing new until it holds it; it holds %s", + part, s.Owner, s.State, s.WaitsFor, s.WaitingSince.UTC().Format(time.RFC3339), cmp.Or(s.View, "none")) return finding(part, ViewWaitsKind, id, summary, fmt.Sprintf("%s waits for a view of %s", part, s.Owner), fmt.Sprintf("%s keeps running what it runs and takes up nothing new until %s's view %s reaches it. Look at %s: is it running, and has it "+ diff --git a/go/views/follow.go b/go/views/follow.go index ac14a5ac..7feb26f1 100644 --- a/go/views/follow.go +++ b/go/views/follow.go @@ -112,6 +112,10 @@ var waitBound = func(ctx context.Context, d time.Duration) bool { return ctx.Err() == nil } +// NoneYet is what a seat's revision verb answers for a branch with no revision yet (design 47 G §479, §495): that view +// is ready and empty, and nothing waits for it. +const NoneYet = "none yet" + // WaitFor says the part waits for view, a revision of the revision view its seat's client names, and acts on nothing // new until it holds it (Holds): the status says what and since when, and if it still waits for it past WaitsBound, // RaiseWaits is told (the part writes view-waits, ViewWaitsFinding). Waiting for the view it waits for already changes @@ -119,7 +123,7 @@ var waitBound = func(ctx context.Context, d time.Duration) bool { func (f *Follower) WaitFor(ctx context.Context, view string) { f.mu.Lock() f.init() - if f.st.WaitsFor == view || view == "" { + if f.st.WaitsFor == view || view == "" || view == NoneYet { f.mu.Unlock() return } diff --git a/go/views/views_test.go b/go/views/views_test.go index 3327f1ec..ca313534 100644 --- a/go/views/views_test.go +++ b/go/views/views_test.go @@ -309,7 +309,7 @@ func TestViews(t *testing.T) { defer func() { waitBound = kept }() var raised, cleared, said []Status var mu sync.Mutex - f := &Follower{Owner: "o", Tool: "o.revision", State: "catalog", Now: now, + f := &Follower{Owner: "o", Tool: "o.revision", State: BranchState("catalog", "trunk"), Now: now, RaiseWaits: func(s Status) { mu.Lock(); raised = append(raised, s); mu.Unlock() }, ClearWaits: func(s Status) { mu.Lock(); cleared = append(cleared, s); mu.Unlock() }, Said: func(s Status) { mu.Lock(); said = append(said, s); mu.Unlock() }} @@ -329,10 +329,27 @@ func TestViews(t *testing.T) { } mu.Unlock() fd := ViewWaitsFinding("mesh-delivery-builder", f.Status(), now()) - if fd.Kind != ViewWaitsKind || fd.ID != "mesh-delivery-builder.o.catalog" || !strings.Contains(fd.Summary, "waits for o's view trunk.12") || + if fd.Kind != ViewWaitsKind || fd.ID != "mesh-delivery-builder.o.catalog.trunk" || !strings.Contains(fd.Summary, "waits for the view o.catalog.trunk at revision trunk.12") || !strings.Contains(fd.Summary, "it holds trunk.11") { t.Errorf("finding = %+v", fd) } + none := &Follower{Owner: "o", Tool: "o.revision", State: BranchState("catalog", "new"), Now: now, + Ask: func(context.Context, string) (Answer, error) { return Answer{V: 1, Owner: "o", State: "catalog.new", At: now()}, nil }} + if err := none.Start(context.Background()); err != nil { + t.Fatal(err) + } + select { + case <-none.Ready(): + default: + t.Errorf("a view of a branch with no revision is not ready") + } + if len(none.Values()) != 0 { + t.Errorf("not empty") + } + none.WaitFor(context.Background(), NoneYet) // a branch with no revision yet: ready, empty, nothing awaited (§495) + if s := none.Status(); s.WaitsFor != "" || !s.WaitingSince.IsZero() { + t.Errorf("a view of none yet is awaited: %+v", s) + } f.Holds("trunk.12") if s := f.Status(); s.View != "trunk.12" || s.WaitsFor != "" || s.WaitsRaised { t.Errorf("after holding: %+v", s)