The executed registry-install fixture

RegistryPackages.Install is the Store's registry lane: list the registry's catalog, fetch the package's files, install its nodes — and then land its compiled module from the same registry's bundle index. That last branch is the one #1585 found production had never taken; #1590 could only pin the report's wording, because nothing in this repository could execute the lane.

Nothing could execute it for a mundane reason: no project compiled it. Store/Publishing/Source/RegistryPackages.cs is an in-mesh NodeType source, and src/MeshWeaver.PluginCatalog.Test compiled Store/Licensing, Store/Core, Store/Coupon, Store/Install and two files of Store/Publishing — not this one. Adding Store/Publishing/Source/*.cs to that project (the whole L2 library, which is how Store/Publishing.json describes itself) compiles clean under -warnaserror, and that is the entire enabling step.

The seam is IHttpClientFactory — there was never a missing hook

Install constructs its collaborators itself:

var bundles = new PluginBundleClient(hub, url, token);                       // RegistryPackages.cs:319
var feed = new RegistryPackageSource(hub, url, token) { Bundles = bundles }; // RegistryPackages.cs:320

so an IPackageSource cannot be handed in. It does not need to be. Both types take their HttpClient from the host's IHttpClientFactoryPluginBundleClient.cs:80, RegistryPackageSource.cs:53 — falling back to a shared client when none is registered. Registering a stub factory on the test mesh therefore puts an in-memory serving registry under the real lane, with no production change at all:

route what the fixture answers
GET /api/plugins PluginRegistryPayloads.List([manifest]) — the catalog
POST /api/plugins/files { files: [...] } — the package's files
GET /api/plugins/bundles/index.json the bundle index
GET /api/plugins/bundles/{id}/{version} a real NuGetPackageWriter bundle carrying real assembly bytes

Anything else is a 404, on purpose: a route the lane starts calling has to be staged deliberately rather than silently succeeding.

🚨 module on the index entry is load-bearing. AdoptModule selects the bundle with !string.IsNullOrWhiteSpace(b.Module) (PluginBundleClient.cs:373-375). An index entry with plugin/version/url and no module is the #1585 miss, not a landing — which is exactly what BundleAdoptionMissTest stages, and why copying its Index(...) builder verbatim produces a test that can never see a landing.

Why it could not be an in-mesh case

Store/Publishing's Tests area is the natural home and cannot host this. Substituting an IHttpClientFactory is a host-service act, and an in-mesh Tests area has no ConfigureMesh — the single facility Hosting/InMeshTestMigration.md records as missing. The live-case shape (public static IObservable<Unit> …(IMessageHub hub)) gives a test the hub's services; it does not let it change what the host registered before the hub existed.

What makes it able to fail

RegistryPackages.LandModule swallows every fault and answers 0 (RegistryPackages.cs:373-379) — correctly, since the content half has already landed. So a lane that is absent is indistinguishable from one that succeeded, except by two things: the landed count, and the set the wave proposed. The test asserts both, and both were watched to fail:

control applied to RegistryPackages.cs result
the LandModule call replaced by new RegistryInstallOutcome(content, manifest.Module, 0)the exact pre-#1585 shape RED: "AdoptModule must consult the bundle index; the lane called: GET /api/plugins · POST /api/plugins/files"
only the ProposeModuleSet call dropped, LandModule intact RED: "Expected value not to be <null> because the landing wave must propose the set it just created" — the adopt half stayed green
neither (as shipped) 2/2 green

The second control matters as much as the first: it proves the proposal assertion is not carried by the landing assertion, so neither half is decorative.

The failure message names the routes the lane actually called, not a bare 0 — so the next occurrence says whether the adopt never started, or started and was refused.

The negative case is part of the fixture

ARegistryInstall_OfAPackageDeclaringNoModule_LandsNoModule_AndProposesNothing installs a package with Module = null through the same fixture and asserts 0 landed and no proposal. Without it a ModuleFilesLanded that was always 1 would read identically to a working lane.

Relationship to #1794 — two seams, not one harness

#1794 asks for a seam that holds a node between its write committing and its owner materialising it. That is a mesh write-timing stand-in, on the storage/materialisation path, for the "it worked the second time" family. This fixture is an HTTP response stand-in, on IHttpClientFactory, for the registry lane. They share no layer, no injection point and no failure mode, and a single harness bundling both would be two unrelated stand-ins wearing one name.

More usefully: this fixture demonstrates that #1592 never needed #1794. The seam already existed in the platform; what was missing was a project that compiled the code under test. #1794 remains genuinely open — nothing here reaches it.

Reconnecting…
The server was updated. Reloading the page to pick up the latest version.