From 58373d65b47a6adfa25574814aaba7fd01fffe05 Mon Sep 17 00:00:00 2001 From: mesh-tools Date: Mon, 19 Oct 2026 22:39:29 +0000 Subject: [PATCH 1/2] Count a build record without a state as the outcome it is (issue 1281): the controller never stores state built --- go/delivery/buildrecord.go | 1 + go/delivery/buildrecord_test.go | 13 +++++++++---- .../testdata/build-record-build-agent.json | 2 +- go/delivery/trunk.go | 6 +++--- go/delivery/trunk_test.go | 19 ++++++++++--------- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/go/delivery/buildrecord.go b/go/delivery/buildrecord.go index 8802f6f..86bd87b 100644 --- a/go/delivery/buildrecord.go +++ b/go/delivery/buildrecord.go @@ -11,6 +11,7 @@ import "time" type BuildRecord struct { ID string `json:"id"` State string `json:"state,omitempty"` + Failed string `json:"failed,omitempty"` Repository string `json:"repository"` Ref string `json:"ref,omitempty"` Module string `json:"module,omitempty"` diff --git a/go/delivery/buildrecord_test.go b/go/delivery/buildrecord_test.go index c056d38..4f4f098 100644 --- a/go/delivery/buildrecord_test.go +++ b/go/delivery/buildrecord_test.go @@ -11,7 +11,8 @@ import ( // internal/inventory/chain_requests.go:19, the module's directory in its repository (Build.Path, internal/chain/records.go:100). // The fixture is a record the controller's own encoder wrote (json.Marshal of a chain.Build, mesh-controller // internal/chain, a throwaway test run once and deleted) with build-agent's build-1792395447088936520 values, plus a -// manifest-digest and one made artifact, which a BuildRecord does not carry. +// manifest-digest and one made artifact (shape: mesh-controller internal/chain/records.go:91-117 Build, json tags, at commit +// 1f708e2d8d72e2c27dbeaba02f14c128d700eada; an outcome has no state key and a build asked after a merge no ref, records.go:57-59), which a BuildRecord does not carry. func loadBuildFixture(t *testing.T) []byte { t.Helper() raw, err := os.ReadFile("testdata/build-record-build-agent.json") @@ -29,10 +30,11 @@ func TestBuildRecordDecodes(t *testing.T) { // 1223/AC1 for _, c := range []struct{ name, got, want string }{ {"id", r.ID, "build-1792395447088936520"}, {"module", r.Module, "build-agent"}, - {"repository", r.Repository, "novox/mesh-controller"}, - {"ref", r.Ref, "main"}, + {"repository", r.Repository, "http://novox.internal:20000/novox/mesh-controller.git"}, + {"ref", r.Ref, ""}, {"commit", r.Commit, "7a74cee83f88e6585a5511c3cc53bdf0f5303d20"}, - {"state", r.State, "built"}, + {"state", r.State, ""}, + {"failed", r.Failed, ""}, {"source-fingerprint", r.SourceFingerprint, "src1:e846efe73716b78e80860baaac96153de2cb76551325753838ca245d2160a3d7"}, } { if c.got != c.want { @@ -42,6 +44,9 @@ func TestBuildRecordDecodes(t *testing.T) { // 1223/AC1 if want := time.Date(2026, 10, 19, 9, 38, 0, 0, time.UTC); !r.At.Equal(want) { t.Errorf("at = %v, want %v", r.At, want) } + if !r.IsTrunkBuild() { // 1281/AC7 + t.Error("the fixture, a record as the controller writes it, is not a trunk build") + } if r.SourceInputs == nil { t.Fatal("source-inputs missing") } diff --git a/go/delivery/testdata/build-record-build-agent.json b/go/delivery/testdata/build-record-build-agent.json index 458b9e3..ea840ac 100644 --- a/go/delivery/testdata/build-record-build-agent.json +++ b/go/delivery/testdata/build-record-build-agent.json @@ -1 +1 @@ -{"id":"build-1792395447088936520","state":"built","repository":"novox/mesh-controller","ref":"main","module":"build-agent","commit":"7a74cee83f88e6585a5511c3cc53bdf0f5303d20","path":"modules/build-agent","manifest-digest":"sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef","source-fingerprint":"src1:e846efe73716b78e80860baaac96153de2cb76551325753838ca245d2160a3d7","source-inputs":{"Module":"build-agent","Tree":"11c0b4f90cae0d6e4ef9cc48cbfdf9f6dbe52bca","Bases":["sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc","sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c"],"Contexts":{"server":"narrow:d51ca744fd11af59d616c858f76c27b3e58e16e11fbdc0c720eb9d7d19d55fac"},"Toolchains":null,"Unpinned":""},"made":[{"name":"server","kind":"image","reference":"registry.example/build-agent@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc"}],"at":"2026-10-19T09:38:00Z"} +{"id":"build-1792395447088936520","repository":"http://novox.internal:20000/novox/mesh-controller.git","module":"build-agent","commit":"7a74cee83f88e6585a5511c3cc53bdf0f5303d20","path":"modules/build-agent","manifest-digest":"sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef","source-fingerprint":"src1:e846efe73716b78e80860baaac96153de2cb76551325753838ca245d2160a3d7","source-inputs":{"Module":"build-agent","Tree":"11c0b4f90cae0d6e4ef9cc48cbfdf9f6dbe52bca","Bases":["sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc","sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c"],"Contexts":{"server":"narrow:d51ca744fd11af59d616c858f76c27b3e58e16e11fbdc0c720eb9d7d19d55fac"},"Toolchains":null,"Unpinned":""},"made":[{"name":"server","kind":"image","reference":"registry.example/build-agent@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc"}],"at":"2026-10-19T09:38:00Z"} diff --git a/go/delivery/trunk.go b/go/delivery/trunk.go index c5c51cb..42d7971 100644 --- a/go/delivery/trunk.go +++ b/go/delivery/trunk.go @@ -24,9 +24,9 @@ func SameRepository(a, b string) bool { return a != "" && strings.EqualFold(a, b) } -// IsTrunkBuild is whether the record is a successful build of the trunk that recorded its source inputs: state built, and -// ref "main" or none. A build a delivery plan asked after a merge, or rebuild or build --behind, follows the branch +// IsTrunkBuild is whether the record is a successful build of the trunk that recorded its source inputs: an outcome +// (the controller writes no state on one; state asked is a request, records.go:57-59), not failed, and ref "main" or none. A build a delivery plan asked after a merge, or rebuild or build --behind, follows the branch // without naming it, so it carries no ref; a build of any other branch or of a commit names it (issue 1281). func (r BuildRecord) IsTrunkBuild() bool { - return r.State == "built" && r.SourceInputs != nil && (r.Ref == "" || r.Ref == "main") + return r.State == "" && r.Failed == "" && r.SourceInputs != nil && (r.Ref == "" || r.Ref == "main") } diff --git a/go/delivery/trunk_test.go b/go/delivery/trunk_test.go index fb84cbf..d97d94d 100644 --- a/go/delivery/trunk_test.go +++ b/go/delivery/trunk_test.go @@ -42,16 +42,17 @@ func TestIsTrunkBuild(t *testing.T) { // 1281/AC2 r BuildRecord want bool }{ - {"built on main", BuildRecord{State: "built", Ref: "main", SourceInputs: inputs}, true}, + {"built on main", BuildRecord{Ref: "main", SourceInputs: inputs}, true}, // a build a delivery plan asked after a merge, or rebuild / build --behind, carries no ref (live builds, // 2026-10-19: zsh build-1792417485811374131, mesh-delivery build-1792435539606203626) - {"built with no ref", BuildRecord{State: "built", SourceInputs: inputs}, true}, - {"another branch", BuildRecord{State: "built", Ref: "feat/x", SourceInputs: inputs}, false}, - {"a commit", BuildRecord{State: "built", Ref: "9efdd87a", SourceInputs: inputs}, false}, - {"failed", BuildRecord{State: "failed", Ref: "main", SourceInputs: inputs}, false}, - {"no state", BuildRecord{Ref: "main", SourceInputs: inputs}, false}, - {"no source inputs", BuildRecord{State: "built", Ref: "main"}, false}, - {"no ref and no source inputs", BuildRecord{State: "built"}, false}, + {"built with no ref", BuildRecord{SourceInputs: inputs}, true}, + {"another branch", BuildRecord{Ref: "feat/x", SourceInputs: inputs}, false}, + {"a commit", BuildRecord{Ref: "9efdd87a", SourceInputs: inputs}, false}, + {"asked", BuildRecord{State: "asked", SourceInputs: inputs}, false}, + {"answered, state built", BuildRecord{State: "built", SourceInputs: inputs}, false}, + {"failed", BuildRecord{Failed: "boom", Ref: "main", SourceInputs: inputs}, false}, + {"no source inputs", BuildRecord{Ref: "main"}, false}, + {"no ref and no source inputs", BuildRecord{}, false}, } { if got := c.r.IsTrunkBuild(); got != c.want { t.Errorf("%s: IsTrunkBuild = %v, want %v", c.name, got, c.want) @@ -59,7 +60,7 @@ func TestIsTrunkBuild(t *testing.T) { // 1281/AC2 } // decoded as the controller writes it: the key "ref" is absent var r BuildRecord - raw := `{"id":"build-1792417485811374131","state":"built","repository":"http://novox.internal:20000/novox/mesh-catalog.git","module":"zsh","source-inputs":{"Module":"zsh","Tree":"t"}}` + raw := `{"id":"build-1792417485811374131","repository":"http://novox.internal:20000/novox/mesh-catalog.git","module":"zsh","source-inputs":{"Module":"zsh","Tree":"t"}}` if err := json.Unmarshal([]byte(raw), &r); err != nil || !r.IsTrunkBuild() { t.Errorf("a decoded record with no ref: trunk = %v, err %v", r.IsTrunkBuild(), err) } -- 2.54.0 From 839238fec9b9c2e69a6616261d11767d39c7a915 Mon Sep 17 00:00:00 2001 From: mesh-tools Date: Mon, 19 Oct 2026 22:47:27 +0000 Subject: [PATCH 2/2] Say in the fixture's comment how it was made; test an answered state without inputs (issue 1281) --- go/delivery/buildrecord_test.go | 5 +++-- go/delivery/trunk_test.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go/delivery/buildrecord_test.go b/go/delivery/buildrecord_test.go index 4f4f098..7f9b08a 100644 --- a/go/delivery/buildrecord_test.go +++ b/go/delivery/buildrecord_test.go @@ -9,10 +9,11 @@ import ( // The fixture's path is what the controller writes for a build request: strings.Trim(path, "/") at mesh-controller // internal/inventory/chain_requests.go:19, the module's directory in its repository (Build.Path, internal/chain/records.go:100). -// The fixture is a record the controller's own encoder wrote (json.Marshal of a chain.Build, mesh-controller +// The fixture started as a record the controller's own encoder wrote (json.Marshal of a chain.Build, mesh-controller // internal/chain, a throwaway test run once and deleted) with build-agent's build-1792395447088936520 values, plus a // manifest-digest and one made artifact (shape: mesh-controller internal/chain/records.go:91-117 Build, json tags, at commit -// 1f708e2d8d72e2c27dbeaba02f14c128d700eada; an outcome has no state key and a build asked after a merge no ref, records.go:57-59), which a BuildRecord does not carry. +// 1f708e2d8d72e2c27dbeaba02f14c128d700eada; an outcome has no state key and a build asked after a merge no ref, records.go:57-59), which a BuildRecord does not carry. It was then edited by hand +// to the controller's layout for an outcome: state and ref removed, the repository a clone URL (issue 1281). func loadBuildFixture(t *testing.T) []byte { t.Helper() raw, err := os.ReadFile("testdata/build-record-build-agent.json") diff --git a/go/delivery/trunk_test.go b/go/delivery/trunk_test.go index d97d94d..eaa8357 100644 --- a/go/delivery/trunk_test.go +++ b/go/delivery/trunk_test.go @@ -49,7 +49,7 @@ func TestIsTrunkBuild(t *testing.T) { // 1281/AC2 {"another branch", BuildRecord{Ref: "feat/x", SourceInputs: inputs}, false}, {"a commit", BuildRecord{Ref: "9efdd87a", SourceInputs: inputs}, false}, {"asked", BuildRecord{State: "asked", SourceInputs: inputs}, false}, - {"answered, state built", BuildRecord{State: "built", SourceInputs: inputs}, false}, + {"answered, state answered, no source inputs", BuildRecord{State: "answered"}, false}, {"failed", BuildRecord{Failed: "boom", Ref: "main", SourceInputs: inputs}, false}, {"no source inputs", BuildRecord{Ref: "main"}, false}, {"no ref and no source inputs", BuildRecord{}, false}, -- 2.54.0