From 5b77eb18f8944b61de940dde8ee6c0978a808b06 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 12 Nov 2026 09:24:45 +0100 Subject: [PATCH] B5g3-4: mesh-build-go takes a run's input of any size from the chunked verb input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Builds: design 47 G §712, §354, A-237, A-249 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h --- .../mesh-build-go/cmd/mesh-build-go/run.go | 53 ++++++++++--------- .../cmd/mesh-build-go/run_test.go | 31 ++++++++--- 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/modules/mesh-build-go/cmd/mesh-build-go/run.go b/modules/mesh-build-go/cmd/mesh-build-go/run.go index dd62ebfec..cdd204725 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/run.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/run.go @@ -11,6 +11,7 @@ package main import ( "context" + "encoding/base64" "encoding/json" "errors" "fmt" @@ -18,7 +19,7 @@ import ( "path/filepath" "strings" - checkerclient "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/clients/meshdeliverycheckerclient" + checker "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshdeliverychecker" sdkstep "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/buildstep" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/steprun" ) @@ -45,41 +46,45 @@ func (m *Module) run(ctx context.Context, fetch steprun.StepFunc, a sdkstep.Step return nil, err } ans.Built, ans.Key = built, key - // The answer is written on the step key as one value: one over the bus's value limit (the input's bound) fails the - // step, naming its size, before it is written. - if raw, err := json.Marshal(ans); err != nil { - return nil, err - } else if len(raw) > sdkstep.RunInputBound { - return nil, fmt.Errorf("the checking program's answer is %d bytes, over the bound of %d bytes a step key holds", len(raw), sdkstep.RunInputBound) - } return ans, nil } // inputOf is the input a run names, asked of the seat verb the ask names (module.json invokes it) and checked against -// its digest and size: bytes that are not the input named refuse the ask. +// its digest and size: bytes that are not the input named refuse the ask. A verb marked chunks (the seat mesh-catalog's +// input, of any size) is asked by the plain seat call: the tool runner receives the chunks and hands this module the +// whole file in the member `file` (design 47 G A-237, A-249); another seat's input answers its bytes in `input`. func (m *Module) inputOf(in sdkstep.RunInput) ([]byte, error) { if m.AskInput == nil { return nil, errors.New("this module asks no seat verb for a run's input") } // A verb that does not answer fails the step; an answer that is not the input named refuses the ask. - var askErr error - // The verb is the one the asker's ask names: the runner's, the indexer's (mesh-catalog's input) or the assigner's - // (mesh-wants' input) own, each serving the input by its digest alike (design 47 G §125, §206, §338, §354). - raw, err := checkerclient.ReadInput(func(_ string, body any) (json.RawMessage, error) { - out, err := m.AskInput(in.Verb, body) - askErr = err - return out, err - }, in.Digest) + raw, err := m.AskInput(in.Verb, checker.InputInput{Digest: in.Digest}) if err != nil { - if askErr != nil { - return nil, err - } + return nil, fmt.Errorf("asking %s for the input %s: %w", in.Verb, in.Digest, err) + } + var a struct { + Digest string `json:"digest"` + File string `json:"file"` + Input string `json:"input"` + } + if err := json.Unmarshal(raw, &a); err != nil { + return nil, steprun.Refuse("%s answered no input for %s: %v", in.Verb, in.Digest, err) + } + if a.Digest != in.Digest { + return nil, steprun.Refuse("%s answered the input %q, asked %s", in.Verb, a.Digest, in.Digest) + } + text := a.File + if text == "" { + text = a.Input + } + body, err := base64.StdEncoding.DecodeString(text) + if err != nil { + return nil, steprun.Refuse("%s answered the input %s in no base64: %v", in.Verb, in.Digest, err) + } + if err := in.Verify(body); err != nil { return nil, steprun.Refuse("%v", err) } - if err := in.Verify(raw); err != nil { - return nil, steprun.Refuse("%v", err) - } - return raw, nil + return body, nil } // placeHead lays one head's commit as a plain directory at dir, with no symlink in it; a test sets its own. diff --git a/modules/mesh-build-go/cmd/mesh-build-go/run_test.go b/modules/mesh-build-go/cmd/mesh-build-go/run_test.go index 253185fd1..87e2b4ef2 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/run_test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/run_test.go @@ -109,19 +109,12 @@ func TestAKeptProgramIsRunOverItsHeadsAndInput(t *testing.T) { if ws < 0 || heads < ws || !strings.Contains(line, " "+sdkstep.RunHeadsEnv+" "+argv[heads+1]+" ") { t.Errorf("the heads are not bound read-only over the workspace: %s", line) } - // An answer over the bus's value limit fails the step, naming its size. - m.Run = func(context.Context, []string, string, ...string) forgekey.Ran { - return forgekey.Ran{Status: 0, Stdout: `"` + strings.Repeat("x", sdkstep.RunInputBound) + `"`} - } - if _, err := m.run(context.Background(), fetch, a); err == nil || !strings.Contains(err.Error(), "over the bound") { - t.Errorf("an answer over the bound: %v", err) - } m.Inside, m.Run = true, StepRunner(t.TempDir()) // An input other than the one named is refused, naming it; one the verb does not answer fails the step, saying why. m.AskInput = func(_ string, b any) (json.RawMessage, error) { return json.Marshal(checker.InputAnswer{Digest: b.(checker.InputInput).Digest, Input: base64.StdEncoding.EncodeToString([]byte(`{"other":1}`))}) } - if _, err := m.run(context.Background(), fetch, a); err == nil || !strings.Contains(err.Error(), "whose digest is") || !errors.As(err, new(*steprun.Refusal)) { + if _, err := m.run(context.Background(), fetch, a); err == nil || !strings.Contains(err.Error(), "is not the one the ask names") || !errors.As(err, new(*steprun.Refusal)) { t.Errorf("a wrong input: %v", err) } m.AskInput = answersInput(`{"v":2}`) @@ -196,3 +189,25 @@ func answersInputOn(wanted, body string) func(key string, b any) (json.RawMessag return json.Marshal(checkerclient.InputAnswerOf([]byte(body))) } } + +// Design 47 G A-237, A-249: the input of a run is of any size: the mesh-catalog seat's verb input, marked chunks, is asked by the +// plain seat call and answers {digest, chunks, file}, the file whole as the tool runner received it; the bytes are checked. +func TestARunsInputOfAnySizeIsTakenFromTheChunkedVerb(t *testing.T) { + body := strings.Repeat("0123456789", 300000) // 3 MB + sum := sha256.Sum256([]byte(body)) + in := sdkstep.RunInput{Verb: "seat:mesh-catalog.input", Digest: hex.EncodeToString(sum[:]), Size: len(body)} + m := &Module{AskInput: func(key string, b any) (json.RawMessage, error) { + if ask, _ := b.(checker.InputInput); key != in.Verb || ask.Digest != in.Digest { + return nil, fmt.Errorf("%s keeps no input %v", key, b) + } + return json.Marshal(map[string]any{"digest": in.Digest, "chunks": 6, "file": base64.StdEncoding.EncodeToString([]byte(body))}) + }} + got, err := m.inputOf(in) + if err != nil || string(got) != body { + t.Fatalf("the input of %d bytes: %d bytes, %v", len(body), len(got), err) + } + in.Size-- + if _, err := m.inputOf(in); err == nil || !errors.As(err, new(*steprun.Refusal)) { + t.Errorf("a file of another size than the ask names: %v", err) + } +}