Judge: a store holder that builds is no longer refused by its manifest (issue 1966)

The operator's decision of 2026-11-04: the fresh-mesh circle, a holder whose build publishes through itself, is caught
where a fresh mesh starts (issue 1152), not by a rule that refuses the registry seats' holder.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
2026-11-04 20:28:52 +01:00
parent 83b962cc6d
commit 8e3c9645fd
2 changed files with 12 additions and 41 deletions
+6 -28
View File
@@ -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() {
@@ -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)
}
}