G findings: the writers name the ask's subject from the contract; the readers take the writer whole

WriterConfig.Asked is the generated mesh-conditions contract's SubjectFindingsAsked (A-17), a reader's Findings is the
module's writer itself (A-22, A-26), and mesh-build-go's single manifest ask writes and clears its first-view-unanswered
finding through the writer with the contract's Finding (A-28). mesh-config and mesh-vault's own followers (A-28) are not
yet moved onto a views.Reader: they do not compile at mesh-sdk 24f876a.

Builds: design 47 G §687, §689, A-17, A-22, A-26, A-28

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-11 22:03:09 +01:00
parent 95f29d6c54
commit 06ebd7e8cf
14 changed files with 59 additions and 33 deletions
@@ -32,13 +32,13 @@ func main() {
h.Acts = meshhandactsclient.Recorder{Ask: stdio.Ask}
// The module's one writer (design 47 G §689, A-16): the holder's findings and the reader's go through it.
w, err := views.NewStdioWriter(views.WriterConfig[meshconditions.Finding, meshconditions.FindingCleared, meshconditions.FindingsStated, meshconditions.FindingsAskedEvent]{
Module: Module, State: stdio.State("findings"), Kinds: []string{ImageMissing, SweepRefused}, Observe: h.FindingsObserved})
Asked: meshconditions.SubjectFindingsAsked, Module: Module, State: stdio.State("findings"), Kinds: []string{ImageMissing, SweepRefused}, Observe: h.FindingsObserved})
if err != nil {
logf("%v", err)
os.Exit(1)
}
h.Findings = w
v.Findings = views.WriterFindings{Writer: w}
v.Findings = w
// the runtime answers the writer's subscription only once Serve has its channel
go func() {
<-stdio.Served()
+2 -2
View File
@@ -234,7 +234,7 @@ var meshViews *views.Reader
// observes none of its own.
func newFindingsWriter() (*views.Writer[condseat.Finding, condseat.FindingCleared, condseat.FindingsStated, condseat.FindingsAskedEvent], error) {
return views.NewStdioWriter(views.WriterConfig[condseat.Finding, condseat.FindingCleared, condseat.FindingsStated, condseat.FindingsAskedEvent]{
Module: "claude-code", State: stdio.State("findings"),
Asked: condseat.SubjectFindingsAsked, Module: "claude-code", State: stdio.State("findings"),
Observe: func(context.Context) (views.Observed[condseat.Finding], error) {
return views.Observed[condseat.Finding]{}, nil
}})
@@ -503,7 +503,7 @@ func main() {
say("%v", err)
os.Exit(1)
}
meshViews.Findings = views.WriterFindings{Writer: findings}
meshViews.Findings = findings
// the runtime answers the writer's subscription only once Serve has its channel
go func() {
<-stdio.Served()
@@ -508,7 +508,7 @@ func main() {
// The tools bundle's findings go through the module's writer (design 47 G §689, A-16); it owns the transfers' kinds, the
// program's writer the driver's.
w, err := views.NewStdioWriter(views.WriterConfig[meshconditions.Finding, meshconditions.FindingCleared, meshconditions.FindingsStated, meshconditions.FindingsAskedEvent]{
Module: holder.Self, State: stdio.State("findings"), Kinds: holder.TransferFindingKinds, Observe: findings.Observed, Logf: logf})
Asked: meshconditions.SubjectFindingsAsked, Module: holder.Self, State: stdio.State("findings"), Kinds: holder.TransferFindingKinds, Observe: findings.Observed, Logf: logf})
if err != nil {
logf("%v", err)
os.Exit(1)
@@ -68,7 +68,7 @@ func run(ctx context.Context, getenv func(string) string) error {
// bundle's writer the transfers'.
findings := &holder.Findings{Logf: logger.Printf}
w, err := views.NewWriter(views.WriterConfig[meshconditions.Finding, meshconditions.FindingCleared, meshconditions.FindingsStated, meshconditions.FindingsAskedEvent]{
Module: holder.Self, Node: cred.Node, State: calls.State("findings"), Kinds: holder.DriverFindingKinds,
Asked: meshconditions.SubjectFindingsAsked, Module: holder.Self, Node: cred.Node, State: calls.State("findings"), Kinds: holder.DriverFindingKinds,
JS: js, Conn: conn, Observe: findings.Observed, Logf: logger.Printf})
if err != nil {
return err
+1 -1
View File
@@ -31,7 +31,7 @@ var findings = &Findings{Logf: func(f string, a ...any) { fmt.Fprintf(os.Stderr,
func main() {
// The bundle's findings go through the module's writer (design 47 G A-16).
w, err := views.NewStdioWriter(views.WriterConfig[meshconditions.Finding, meshconditions.FindingCleared, meshconditions.FindingsStated, meshconditions.FindingsAskedEvent]{
Module: packageseats.Self, State: stdio.State("findings"), Kinds: findingKinds, Observe: findings.Observed})
Asked: meshconditions.SubjectFindingsAsked, Module: packageseats.Self, State: stdio.State("findings"), Kinds: findingKinds, Observe: findings.Observed})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
@@ -30,6 +30,7 @@ import (
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-delivery/sdk/composer"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/keys"
"git.novox.be/novox/mesh-sdk/go/views"
)
@@ -37,6 +38,9 @@ import (
// module invokes it, module.json invokes). A manifest by its digest never changes and a compose ask is rare, so the
// module asks for the one key it needs when it needs it, and keeps no picture of the indexer's whole state (ADR 0615).
// indexerModule is the module that holds the seat mesh-catalog, whose manifests state a compose ask reads.
// findingsModule is the module whose findings these are.
const findingsModule = "mesh-build-go"
const indexerModule = "mesh-delivery-indexer"
const ManifestsTool = indexerModule + ".indexer_read_manifests"
@@ -88,31 +92,48 @@ func askManifest(key string) (json.RawMessage, error) {
// manifestFindings is where askManifest writes its finding: the module's writer once main has started it (design 47 G §689,
// A-16); outside a launch by the runtime, which has no node and no state, nothing is written.
var manifestFindings views.FindingWriter = noFindings{}
var manifestFindings findingWriter = noFindings{}
// findingWriter is the module's writer (views.Writer) as askManifestVia writes and clears through it.
type findingWriter interface {
Write(ctx context.Context, finding seat.Finding) error
Clear(ctx context.Context, scope, id, kind, why string) error
}
type noFindings struct{}
func (noFindings) Put(views.Finding) error { return nil }
func (noFindings) Delete(views.Finding) error { return nil }
func (noFindings) Write(context.Context, seat.Finding) error { return nil }
func (noFindings) Clear(context.Context, string, string, string, string) error { return nil }
// unansweredManifest is the finding of an ask the indexer did not answer, laid out as mesh-sdk lays out a views reader's
// first-view-unanswered (design 47 G §687, A-28): the id is the view's name, escaped as a key's name is.
func unansweredManifest(err string, asked, at time.Time) seat.Finding {
id := keys.EscapeName(findingsModule + "." + indexerModule + ".manifests")
return seat.Finding{Kind: views.UnansweredKind, Scope: "module", ID: id, Severity: seat.FindingSeverityWarning, Source: "views",
Subject: seat.Subject{Scope: "module", ID: findingsModule, Machine: os.Getenv("MESH_NODE")},
Summary: fmt.Sprintf("%s has no first view of %s's manifests: %s has not answered since %s (%s)", findingsModule, indexerModule, ManifestsTool,
asked.Format(time.RFC3339), strings.TrimSpace(err)),
Headline: fmt.Sprintf("%s waits for %s", findingsModule, indexerModule), At: at.UTC()}
}
// newFindingsWriter is this module's finding writer: it states the finding of askManifest and observes none of its own.
func newFindingsWriter() (*views.Writer[seat.Finding, seat.FindingCleared, seat.FindingsStated, seat.FindingsAskedEvent], error) {
return views.NewStdioWriter(views.WriterConfig[seat.Finding, seat.FindingCleared, seat.FindingsStated, seat.FindingsAskedEvent]{
Module: "mesh-build-go", State: stdio.State("findings"),
Asked: seat.SubjectFindingsAsked, Module: findingsModule, State: stdio.State("findings"),
Observe: func(context.Context) (views.Observed[seat.Finding], error) {
return views.Observed[seat.Finding]{}, nil
}})
}
func askManifestVia(ask func(string, any) (json.RawMessage, error), findings views.FindingWriter, now func() time.Time, key string) (json.RawMessage, error) {
st := views.Status{Owner: indexerModule, Tool: ManifestsTool, State: "manifests", Asked: now().UTC()}
func askManifestVia(ask func(string, any) (json.RawMessage, error), findings findingWriter, now func() time.Time, key string) (json.RawMessage, error) {
asked := now().UTC()
raw, err := ask(ManifestsTool, map[string]any{"key": key})
f := unansweredManifest("", asked, now())
if err != nil {
st.Error = err.Error()
_ = findings.Put(views.UnansweredFinding("mesh-build-go", st, now()))
_ = findings.Write(context.Background(), unansweredManifest(err.Error(), asked, now()))
return nil, err
}
_ = findings.Delete(views.UnansweredFinding("mesh-build-go", st, now()))
_ = findings.Clear(context.Background(), f.Scope, f.ID, f.Kind, "the indexer answered")
var a struct {
Found bool `json:"found"`
Value json.RawMessage `json:"value"`
@@ -11,12 +11,12 @@ import (
"testing"
"time"
seat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions"
catalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
meshcat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-delivery/sdk/composer"
"git.novox.be/novox/mesh-sdk/go/forgekey"
"git.novox.be/novox/mesh-sdk/go/views"
)
// nodeEngineComposer is the composer command of the node-engine's repository, whose Go module is at its root (ADR 0593 §7):
@@ -187,8 +187,14 @@ func TestAnIndexerThatDoesNotAnswerIsAFinding(t *testing.T) {
type mapFindings map[string][]byte
func (m mapFindings) Put(f views.Finding) error { m[f.ID+"."+f.Kind] = []byte(f.Summary); return nil }
func (m mapFindings) Delete(f views.Finding) error { delete(m, f.ID+"."+f.Kind); return nil }
func (m mapFindings) Write(_ context.Context, f seat.Finding) error {
m[f.ID+"."+f.Kind] = []byte(f.Summary)
return nil
}
func (m mapFindings) Clear(_ context.Context, _, id, kind, _ string) error {
delete(m, id+"."+kind)
return nil
}
// ADR 0593 §7 (C9): a compose ask naming the node-engine's repository builds its composer command in the Go module at the
// repository's root, under a key of that repository, commit and package, and runs the program over the request with its
@@ -17,7 +17,6 @@ import (
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/busstate"
"git.novox.be/novox/mesh-sdk/go/views"
)
// The directories the module's manifest places outside its home (ADR 0466 §1): the workspaces, and the Go module
@@ -77,7 +76,7 @@ func main() {
logf("the finding writer: %v", err)
os.Exit(1)
}
manifestFindings = views.WriterFindings{Writer: findings}
manifestFindings = findings
// the runtime answers the writer's subscription only once Serve has its channel
go func() {
<-stdio.Served()
+2 -2
View File
@@ -123,7 +123,7 @@ func main() {
Delivery: toolDelivery{ask: stdio.Ask}, Vault: toolVault{ask: stdio.Ask, recorder: recorder}}
// The module's one writer (design 47 G §689, A-16): the renames' and retirements' findings and the views reader's go through it.
w, err := views.NewStdioWriter(views.WriterConfig[meshconditions.Finding, meshconditions.FindingCleared, meshconditions.FindingsStated, meshconditions.FindingsAskedEvent]{
Module: Module, State: stdio.State(FindingsState), Observe: c.FindingsObserved,
Asked: meshconditions.SubjectFindingsAsked, Module: Module, State: stdio.State(FindingsState), Observe: c.FindingsObserved,
Kinds: []string{FindingRenameRefused, FindingWindowHeld, FindingRetireUnanswered, FindingRetireHeld}})
if err != nil {
logf("%v", err)
@@ -136,7 +136,7 @@ func main() {
case moved <- struct{}{}:
default:
}
}, views.WriterFindings{Writer: w})
}, w)
// the runtime answers the writer's subscription only once Serve has its channel
go func() {
<-stdio.Served()
@@ -219,7 +219,7 @@ func watchedOver(v *views.Reader) func(string, func(stdio.StateChange) error) er
// observes none of its own.
func newFindingsWriter() (*views.Writer[seat.Finding, seat.FindingCleared, seat.FindingsStated, seat.FindingsAskedEvent], error) {
return views.NewStdioWriter(views.WriterConfig[seat.Finding, seat.FindingCleared, seat.FindingsStated, seat.FindingsAskedEvent]{
Module: "mesh-provisioner", State: stdio.State("findings"),
Asked: seat.SubjectFindingsAsked, Module: "mesh-provisioner", State: stdio.State("findings"),
Observe: func(context.Context) (views.Observed[seat.Finding], error) {
return views.Observed[seat.Finding]{}, nil
}})
@@ -233,7 +233,7 @@ func main() {
logf("%v", err)
os.Exit(1)
}
v.Findings = views.WriterFindings{Writer: findings}
v.Findings = findings
// One copy of what it reads, each state by its watch (ADR 0580 clause 2); the indexer's and the assigner's by their
// pictures, a manifest by its digest from the picture too.
ws := stdio.WatchedStatesOver(watchedOver(v), func(state, key string) ([]byte, bool, error) {
+2 -2
View File
@@ -122,7 +122,7 @@ func watchLeavers(env *Env) bool {
// The vault's finding writer (design 47 G §689, A-16): it states the assignments view's findings and observes none of
// its own, and names no secret.
w, err := views.NewStdioWriter(views.WriterConfig[meshconditions.Finding, meshconditions.FindingCleared, meshconditions.FindingsStated, meshconditions.FindingsAskedEvent]{
Module: Seat, State: stdio.State(FindingsState),
Asked: meshconditions.SubjectFindingsAsked, Module: Seat, State: stdio.State(FindingsState),
Observe: func(context.Context) (views.Observed[meshconditions.Finding], error) {
return views.Observed[meshconditions.Finding]{}, nil
}})
@@ -137,7 +137,7 @@ func watchLeavers(env *Env) bool {
env.Vault.NodeLeft(node)
}
}, env.Vault.AssignmentsChanged,
views.WriterFindings{Writer: w})
w)
beginAssignmentsView(assignmentsView)
}
return true
@@ -82,7 +82,7 @@ func main() {
logf("%v", err)
os.Exit(1)
}
assignments.Findings = views.WriterFindings{Writer: findings}
assignments.Findings = findings
assignments.Follow(assignerModule, mesh.StateSeatHolders, SeatHoldersTool)
// the runtime answers the writer's subscription only once Serve has its channel
go func() {
@@ -103,7 +103,7 @@ func main() {
// observes none of its own.
func newFindingsWriter() (*views.Writer[seat.Finding, seat.FindingCleared, seat.FindingsStated, seat.FindingsAskedEvent], error) {
return views.NewStdioWriter(views.WriterConfig[seat.Finding, seat.FindingCleared, seat.FindingsStated, seat.FindingsAskedEvent]{
Module: "mesh-watcher", State: stdio.State("findings"),
Asked: seat.SubjectFindingsAsked, Module: "mesh-watcher", State: stdio.State("findings"),
Observe: func(context.Context) (views.Observed[seat.Finding], error) {
return views.Observed[seat.Finding]{}, nil
}})
+2 -2
View File
@@ -36,7 +36,7 @@ func main() {
fmt.Fprintf(os.Stderr, "[object-store] %v\n", err)
os.Exit(1)
}
seats.Findings = views.WriterFindings{Writer: findings}
seats.Findings = findings
seats.Follow(assignerModule, mesh.StateSeatHolders, SeatHoldersTool)
s, err := newServer(os.Getenv(ConfigWord), os.Getenv(LinksWord), os.Getenv(ReceivesWord),
func() (map[string][]byte, error) { return seats.Values(assignerModule + "." + mesh.StateSeatHolders) })
@@ -70,7 +70,7 @@ const SeatHoldersTool = assignerModule + ".assigner_read_seat_holders"
// observes none of its own.
func newFindingsWriter() (*views.Writer[seat.Finding, seat.FindingCleared, seat.FindingsStated, seat.FindingsAskedEvent], error) {
return views.NewStdioWriter(views.WriterConfig[seat.Finding, seat.FindingCleared, seat.FindingsStated, seat.FindingsAskedEvent]{
Module: "minio", State: stdio.State("findings"),
Asked: seat.SubjectFindingsAsked, Module: "minio", State: stdio.State("findings"),
Observe: func(context.Context) (views.Observed[seat.Finding], error) {
return views.Observed[seat.Finding]{}, nil
}})
+2 -2
View File
@@ -52,7 +52,7 @@ func main() {
}
m := Here()
w, err := views.NewStdioWriter(views.WriterConfig[seat.Finding, seat.FindingCleared, seat.FindingsStated, seat.FindingsAskedEvent]{
Module: "nfs-server", State: stdio.State("findings"),
Asked: seat.SubjectFindingsAsked, Module: "nfs-server", State: stdio.State("findings"),
Observe: func(context.Context) (views.Observed[seat.Finding], error) {
return views.Observed[seat.Finding]{}, nil
}})
@@ -60,7 +60,7 @@ func main() {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
seats.Findings = views.WriterFindings{Writer: w}
seats.Findings = w
seats.Follow(assignerModule, mesh.StateSeatHolders, SeatHoldersTool)
seats.Start(context.Background())
// the runtime answers the writer's subscription only once Serve has its channel