Join 11: the builder sets all of the catalog half's wiring before its views reader starts

The floor wake (half.floor), the builder (half.b), ready and branches.Changed were set after reader.Start, so a
catalog view heard on the reader's goroutine read them while main wrote them: the race detector named
catalog.go:117 against main.go:501 in TestBuilderMakesADoneKeyWithTheDigests. They are now set before the start;
the builder cmd integration package then ran 21 times under -race with no race.

Builds: design 47 G §614, A-119

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 13:26:58 +01:00
parent 339964920b
commit 347db8d4fd
@@ -422,13 +422,6 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
}
}
hub.OnBound(reads.atBind) // what a node's engine said before the consumer was bound (ADR 0668 §4)
reader.Start(ctx, js, cred, process.AwaitBucket(ctx, js, conn, firstViewsBucket, "builder_first_views reads nothing and builder_first_view_again is not heard", logger.Printf))
// Every part has registered: the hub pulls the module's durable. Where the consumer is not there yet the builder says so
// and binds it at a later change (heard.Writer; ADR 0615 §5).
_ = notHeard.Bind(eventsStream, sweep.EventArtifactsGone+", what wakes a floor, the registry seats' words, the views and manifest-reads-changed are not heard until the builder starts again", func() error {
return hub.Start(ctx, js, cred)
})
go func() { <-ctx.Done(); hub.Stop() }()
// The catalog half's entries as they come: a module's entry of a branch goes to the builder, and a key left building for a
// fingerprint no branch carries is closed (issue 1660) once the first view of every followed branch is in (never at the start:
// a holder of the seat that is down holds no other part of it).
@@ -439,6 +432,18 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
}
}
branches.Changed = half.Changed(ctx)
// A catalog revision the builder hears wakes a floor whose tags or requirements were silent (design 47 G A-119).
half.floor = func(ctx context.Context) {
(&floorWake{b: b, waits: floorWaits, logf: logger.Printf}).wake(ctx, versions.WakeRevision, "", "")
}
// All of the catalog half's wiring is set before the reader starts: its views are heard on the reader's goroutines from then.
reader.Start(ctx, js, cred, process.AwaitBucket(ctx, js, conn, firstViewsBucket, "builder_first_views reads nothing and builder_first_view_again is not heard", logger.Printf))
// Every part has registered: the hub pulls the module's durable. Where the consumer is not there yet the builder says so
// and binds it at a later change (heard.Writer; ADR 0615 §5).
_ = notHeard.Bind(eventsStream, sweep.EventArtifactsGone+", what wakes a floor, the registry seats' words, the views and manifest-reads-changed are not heard until the builder starts again", func() error {
return hub.Start(ctx, js, cred)
})
go func() { <-ctx.Done(); hub.Stop() }()
// A person asks a key's build again with builder_ask_again, which writes the reasks state (issue 1945): watched, as
// every other input. The builder asks again by itself without it.
// A state not on the bus yet at this start is watched once it appears (issue 1790).
@@ -497,10 +502,6 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
go func() { errs <- follow(ctx, qw, eachRead) }()
}
})
// A catalog revision the builder hears wakes a floor whose tags or requirements were silent (design 47 G A-119).
half.floor = func(ctx context.Context) {
(&floorWake{b: b, waits: floorWaits, logf: logger.Printf}).wake(ctx, versions.WakeRevision, "", "")
}
// The state engine-reads, new with ADR 0618 (issue 1790's kind): until it is on the bus each statement is kept in memory
// only, and builder_engine_reads answers nothing.
process.AwaitBucket(ctx, js, conn, engineReadsBucket, "what each node's engine reads is kept in memory only", logger.Printf).Ready(func(kv jetstream.KeyValue) {