From 86d13a41a3cdad8147a9ad05317955e25efafdd8 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 9 Nov 2026 21:49:30 +0100 Subject: [PATCH] W4.15: a commit the forge does not have is the seat git's kind not-found, never a 404 in the error's words (milestone 47/G) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit askGit wraps the kind not-found as index.ErrNotFound; order.go's notFound reads only that. Needs feat/47-G-seats' seat git at 3b355a35 at the join. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ Builds: design 47 G §310, §317 --- .../cmd/mesh-delivery-indexer/named.go | 5 ++++- .../cmd/mesh-delivery-indexer/named_test.go | 10 ++++++++++ .../internal/index/modules_test.go | 2 +- .../mesh-delivery-indexer/internal/index/order.go | 12 +++++++----- .../internal/index/registered_test.go | 2 +- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named.go index 36bd663b..cc1f6db7 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named.go @@ -31,7 +31,7 @@ func gitVerb(verb string) string { return "seat:" + git.Seat + "." + verb } type toolCaller func(key string, body any) (json.RawMessage, error) // askGit asks one verb of the seat git and decodes its answer into out; an answer whose error is set is the failure it -// names, with its kind. +// names, with its kind; the kind not-found is index.ErrNotFound, so a caller never reads it from the words. func askGit(call toolCaller, verb string, in, out any) error { raw, err := call(gitVerb(verb), in) if err != nil { @@ -44,6 +44,9 @@ func askGit(call toolCaller, verb string, in, out any) error { if err := json.Unmarshal(raw, &failed); err != nil { return fmt.Errorf("the answer of %s is not its seat's answer: %w", gitVerb(verb), err) } + if failed.Error != "" && failed.ErrorKind == git.ErrorKindNotFound { + return fmt.Errorf("%s (%s): %s: %w", gitVerb(verb), failed.ErrorKind, failed.Error, index.ErrNotFound) + } if failed.Error != "" { return fmt.Errorf("%s (%s): %s", gitVerb(verb), failed.ErrorKind, failed.Error) } diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named_test.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named_test.go index a434f66a..0386dd2a 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named_test.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named_test.go @@ -27,6 +27,16 @@ func TestNamedViaAsksTheTreesToolWithThePathsAndTakesTheirObjects(t *testing.T) if asked.Repository != "novox/mesh-sdk" || asked.Commit != "s" || asked.Paths == nil { t.Errorf("asked %v, want the repository, commit and paths", asked) } + // The seat's kind not-found is index.ErrNotFound whatever its words; a failed answer whose words carry a 404 is not. + for answer, gone := range map[string]bool{ + `{"error":"the forge holds no commit bbbb","error-kind":"not-found"}`: true, + `{"error":"Gitea API /compare/aaa: 404 Not Found","error-kind":"failed"}`: false, + } { + contains := func(string, any) (json.RawMessage, error) { return json.RawMessage(answer), nil } + if _, err := containsVia(contains)(context.Background(), "novox/a", "aaaa", "bbbb"); err == nil || errors.Is(err, index.ErrNotFound) != gone { + t.Errorf("contains answering %s: %v, want not-found %v", answer, err, gone) + } + } // An answer that carries the seat's error is the failure it names, never an empty answer. failed := func(string, any) (json.RawMessage, error) { return json.RawMessage(`{"error":"the forge did not answer","error-kind":"failed"}`), nil diff --git a/modules/mesh-delivery-indexer/internal/index/modules_test.go b/modules/mesh-delivery-indexer/internal/index/modules_test.go index 9a4d6fc7..8dd4d180 100644 --- a/modules/mesh-delivery-indexer/internal/index/modules_test.go +++ b/modules/mesh-delivery-indexer/internal/index/modules_test.go @@ -291,7 +291,7 @@ func TestAnUnchangedModuleWritesNothing(t *testing.T) { // 1239 (unchanged value said: "is not known to be older", then: func(ix *Indexer) error { ix.Contains = func(context.Context, string, string, string) (bool, error) { - return false, errors.New("gitea_contains: the forge answered: 404 Not Found") + return false, fmt.Errorf("seat:git.contains (not-found): the forge holds no commit bbbb: %w", ErrNotFound) // the kind, no status in its words } later := mergedMsg(repo, map[string]string{"modules/a": "treeA2", "modules/b": "treeB", "modules/c": "treeC"}) later.Merge = next diff --git a/modules/mesh-delivery-indexer/internal/index/order.go b/modules/mesh-delivery-indexer/internal/index/order.go index d2f7fda1..1ceaa773 100644 --- a/modules/mesh-delivery-indexer/internal/index/order.go +++ b/modules/mesh-delivery-indexer/internal/index/order.go @@ -53,11 +53,13 @@ func newMergeWrite(f forgemsg.Forge) *mergeWrite { return &mergeWrite{repository: f.Repository, merge: f.Merge} } -// notFound says the forge answered that a commit it was asked about does not exist: the seat git's contains answers it as -// an error of kind failed whose words carry the forge's status ": 404 " (cmd/mesh-delivery-indexer named.go). A bare "404" -// is not enough: the error's words carry both shas, and a 502 naming a sha with 404 in it would read as not older and -// write a late message backwards. The thorough form, a typed not-found in the seat's error kinds, is noted on issue 1126. -func notFound(err error) bool { return err != nil && strings.Contains(err.Error(), ": 404 ") } +// ErrNotFound is the forge saying that what it was asked about (here a commit) does not exist: the seat git's error kind +// not-found, which the indexer's call of the seat wraps (cmd/mesh-delivery-indexer named.go). Only the kind says it, +// never the error's words: a 502 whose words carry "404" inside a sha is a failure. +var ErrNotFound = errors.New("the forge does not have it") + +// notFound says the forge answered that a commit it was asked about does not exist. +func notFound(err error) bool { return errors.Is(err, ErrNotFound) } // older says commit is older than later: the forge says commit is in later and the two differ. A commit the forge does not // have is not known to be older (false, said); a forge that cannot say otherwise is an error. Without a forge to ask diff --git a/modules/mesh-delivery-indexer/internal/index/registered_test.go b/modules/mesh-delivery-indexer/internal/index/registered_test.go index c2437640..ad4eb251 100644 --- a/modules/mesh-delivery-indexer/internal/index/registered_test.go +++ b/modules/mesh-delivery-indexer/internal/index/registered_test.go @@ -131,7 +131,7 @@ func TestARegisteredModuleIsIndexedFromItsRepository(t *testing.T) { // the gran {name: "a source head of a diverged history is read again", moved: true, contains: func(context.Context, string, string, string) (bool, error) { return false, nil }}, {name: "a record whose commit the forge no longer has is read again", moved: true, contains: func(context.Context, string, string, string) (bool, error) { - return false, errors.New("gitea_contains: 404 Not Found") + return false, fmt.Errorf("seat:git.contains (not-found): the forge holds no commit: %w", ErrNotFound) // the kind, no status in its words }}, {name: "a forge that cannot say the order reads nothing", contains: func(context.Context, string, string, string) (bool, error) { return false, errors.New("timeout")