diff --git a/modules/mesh-delivery-indexer/judge/rules.go b/modules/mesh-delivery-indexer/judge/rules.go index 9a56f0a7..c41edf39 100644 --- a/modules/mesh-delivery-indexer/judge/rules.go +++ b/modules/mesh-delivery-indexer/judge/rules.go @@ -2845,19 +2845,6 @@ func parseManifest(raw []byte, predefined catalogue.PredefinedList) (catalogue.M problems = append(problems, movedProblems(m)...) problems = append(problems, collectionProblems(m)...) problems = append(problems, undeliveredBundles(m)...) - // **What provides the artifact store cannot be delivered through it** (novox/hq 04-ISSUES/029). - // - // Building publishes to the store, and the builder will not start without one. So a module - // that provides the store and also builds something asks the mesh to put an artifact into the - // thing that artifact is needed to create. - // - // It is the question the foundation record asks of every candidate — can it grant itself the - // thing it provides? The store cannot create its own database, the broker cannot create its - // own virtual host, and a registry cannot grant itself a repository. Such a module names its - // image, exactly as the bundle names the three a first node starts from. - // - // Refused here because the alternative is a build that never returns, on a mesh new enough - // that nobody is watching it yet. if m.Build != nil && len(m.Bundles) > 0 { // The output of a build, written beside the build that produces it (ADR 0175). A resource // naming a digest beside an `artifact` would be the same mistake, and is caught the same way: @@ -2867,21 +2854,12 @@ func parseManifest(raw []byte, predefined catalogue.PredefinedList) (catalogue.M "produced; a manifest states `build.artifacts` and nothing about what came out", m.Module)) } - if m.Build != nil && len(m.Build.Artifacts) > 0 { - for _, o := range m.Offers() { - if o != catalogue.ArtifactStoreProvision { - continue - } - problems = append(problems, fmt.Sprintf( - "%s provides %q and also builds %d artifact(s), which cannot both be true: "+ - "building publishes to the artifact store, so this asks the mesh to put an "+ - "artifact into the thing that artifact is needed to create. A module that "+ - "provides the store names its image instead. One that wants more beside it "+ - "— an interface, a tool server — is a second module, mirrored in the "+ - "ordinary way once this one is running", - m.Module, catalogue.ArtifactStoreProvision, len(m.Build.Artifacts))) - } - } + // **A store holder that builds is not refused by its manifest** (the operator, 2026-11-04, issue 1966; this rule + // came from novox/hq 04-ISSUES/029). A rule here refused a module that provides `artifact-store` and also builds an + // artifact: building publishes to the store, so on a fresh mesh with no store yet such a build would wait for the + // module it belongs to. The registry seats' holder builds the program it runs in front of the store (design 51), + // so it publishes through itself; that circle is a fact of a mesh's first start, not of a manifest. It is caught where + // a fresh mesh starts, by the genesis fix of issue 1152, and no rule refuses the manifest for it. for to := range m.Serves { var offered bool for _, o := range m.Offers() { diff --git a/modules/mesh-delivery-indexer/judge/rules_test.go b/modules/mesh-delivery-indexer/judge/rules_test.go index 7fbffcd9..614b7883 100644 --- a/modules/mesh-delivery-indexer/judge/rules_test.go +++ b/modules/mesh-delivery-indexer/judge/rules_test.go @@ -4239,24 +4239,17 @@ func TestTakesSettingsIsRefusedWhereItCannotMeanAnything(t *testing.T) { } } -// The module that provides the artifact store may not be delivered through it. -// -// Building publishes to the store and the builder will not start without one, so a module that -// provides the store and also builds something asks the mesh to put an artifact into the thing -// that artifact is needed to create. On a mesh new enough to have no registry, that is a build -// that never returns (novox/hq 04-ISSUES/029). -func TestTheArtifactStoreCannotBeDeliveredThroughItself(t *testing.T) { +// A store holder that builds is not refused by its manifest (the operator, 2026-11-04, issue 1966). The fresh-mesh +// circle the rule of novox/hq 04-ISSUES/029 refused, a holder whose build publishes through itself, is caught where a +// fresh mesh starts (issue 1152), not here. +func TestAStoreHolderThatBuildsIsNotRefusedByItsManifest(t *testing.T) { _, err := ParseManifest([]byte(`{"module":"registry","version":"1",` + `"provides":[{"name":"artifact-store","scope":"mesh"}],` + `"build":{"artifacts":[{"name":"registry","kind":"upstream","from":"registry:2"}]},` + `"resources":[{"id":"store","type":"container","name":"mesh-registry",` + `"artifact":"registry","ports":["5000:5000"]}]}`)) - if err == nil { - t.Fatal("a registry module that builds its own image was accepted; the build has " + - "nowhere to publish until the module it belongs to is already running") - } - if !strings.Contains(err.Error(), "artifact-store") { - t.Fatalf("refused without naming the provision the cycle turns on: %v", err) + if err != nil { + t.Fatalf("a module that provides the artifact store and builds was refused: %v", err) } }