diff --git a/modules/mesh-delivery-indexer/judge/rules.go b/modules/mesh-delivery-indexer/judge/rules.go index 61c48405..6d30c3f7 100644 --- a/modules/mesh-delivery-indexer/judge/rules.go +++ b/modules/mesh-delivery-indexer/judge/rules.go @@ -5567,6 +5567,10 @@ var seatStreamsKeptUntilMoved = map[string][]string{ // keptUntilMoved is whether a stream of a manifest is one kept until its module moves it: by the module's name, or by a // seat it claims. func keptUntilMoved(m catalogue.Manifest, stream string) bool { + // A foundation part says its findings on its old stream `-findings` for good (design 47 G §529, A-30). + if stream == FindingStreamOf(m.Module) && slices.Contains(Foundation, m.Module) { + return true + } if slices.Contains(streamsKeptUntilMoved[m.Module], stream) { return true } diff --git a/modules/mesh-delivery-indexer/judge/rules_test.go b/modules/mesh-delivery-indexer/judge/rules_test.go index 2ef9b609..0530f52f 100644 --- a/modules/mesh-delivery-indexer/judge/rules_test.go +++ b/modules/mesh-delivery-indexer/judge/rules_test.go @@ -4683,7 +4683,7 @@ func caseTheMeshsOwnSeatsAreStillClaimable(t *testing.T) { // It delivers the bus, so its holder provides the bus — the rule this check now enforces. m := catalogue.Manifest{Module: "nats", Provides: []catalogue.Offer{{Name: "mesh-bus", Scope: catalogue.ScopeMesh, Credential: &catalogue.OfferCredential{Form: catalogue.CredentialFormNone}}}, - Claims: []catalogue.Claim{{Name: "mesh-broker", Scope: catalogue.ScopeMesh, Serves: []string{"snapshot-kept", "restore-kept"}}}} + Claims: []catalogue.Claim{{Name: "mesh-broker", Scope: catalogue.ScopeMesh, Serves: []string{"snapshot-get", "snapshot-restore", "snapshot-restore-cancel", "snapshot-remove"}}}} if got := problemsFor(t, catalogue.Shelf{"nats": m}); got != "" { t.Fatalf("a mesh seat was refused by the derived check: %s", got) } @@ -5121,6 +5121,15 @@ func caseTwoModulesCannotDeclareOneStream(t *testing.T) { if got := PredefinedFormProblems(catalogue.Manifest{Module: "mesh-build-go", Streams: toolchains}); len(got) != 1 { t.Fatalf("a toolchain stream of a module claiming no build seat was kept by its name: %v", got) } + // A foundation part says its findings on its old stream `-findings` for good (design 47 G §529, A-30); a part + // that is no foundation part keeps none (its findings go by the template `events`). + findings := []catalogue.StreamDeclaration{{Name: "mesh-config-findings", Events: []string{"finding-written.*"}}} + if got := PredefinedFormProblems(catalogue.Manifest{Module: "mesh-config", Streams: findings}); len(got) != 0 { + t.Fatalf("a foundation part's findings stream was refused: %v", got) + } + if got := PredefinedFormProblems(catalogue.Manifest{Module: "mesh-config-b", Streams: []catalogue.StreamDeclaration{{Name: "mesh-config-b-findings", Events: []string{"finding-written.*"}}}}); len(got) != 1 { + t.Fatalf("a findings stream of a part outside the foundation was kept: %v", got) + } } // A seat name meaning two protocols is the failure nobody could diagnose afterwards.