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 connection to the server was interrupted. Trying to restore it…
Trying again…
The connection could not be restored. Reloading the page…
The server was updated. Reloading the page to pick up the latest version.