Repository navigation
feat(modules): Markdown.Export ships via modules/ — the first step-2 flip (#1644) - #1666
Conversation
…flip (#1644) Memex.Portal.Shared drops the ships-the-bits ProjectReference; the module's bits now ship ONLY through the modules/<Name>/ closure layout. The targets file grows the closure lane: full per-module Publish (explicit Restore — the module left the host graph; Build with ordinary globals so a concurrent solution build merges into the same scheduled instance instead of racing one obj/; Publish NoBuild), then prunes RID trees (LoadFrom never reads the module's deps.json), same-relative-path app duplicates, and shared-framework assemblies globbed from the targeting packs — measured survivors: exactly the module's own dll/pdb/xml/deps.json, zero private deps (its package closure still rides the app via other references). The closure lane also runs after plain Build so dotnet run keeps working; the publish lane fails hard if the module's dll reappears in the app root (the same-identity double-load guard). Export test projects reference the engine directly now; Modules:Assemblies entries are unchanged — the loader probes modules/ first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR advances the #1644 “modules/ publish layout” migration by performing the first step‑2 flip: MeshWeaver.Markdown.Export no longer ships via any host ProjectReference and instead ships exclusively through the modules/ publish layout, while keeping activation via Modules:Assemblies.
Changes:
- Removed
MeshWeaver.Markdown.ExportfromMemex.Portal.Shared’s ships-the-bits references and updated tests to reference the export engine directly. - Extended
memex/MeshModulesPublish.targetswith a new closure lane (@(MeshModuleClosure)) and added a post-Buildhook to lay out flipped modules intobin/.../modules/fordotnet run. - Updated architecture documentation to describe the first flipped module and the closure lane behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| test/MeshWeaver.Security.Test/MeshWeaver.Security.Test.csproj | Adds a direct reference to the export engine so export-related security tests remain pinned after the host reference is removed. |
| test/MeshWeaver.Hosting.Monolith.Test/MeshWeaver.Hosting.Monolith.Test.csproj | Adds a direct reference to the export engine so monolith export tests don’t rely on host transitive closure. |
| test/Memex.Portal.Shared.Test/Memex.Portal.Shared.Test.csproj | Adds a direct reference to the export engine to anchor module lane switching behavior in tests. |
| src/MeshWeaver.Documentation/Data/Architecture/Modules.md | Documents the first step‑2 flipped module and how the closure lane supports dotnet run. |
| memex/MeshModulesPublish.targets | Implements the closure lane publish+prune pipeline and the after-build layout needed when a flipped module no longer exists in the host output by default. |
| memex/Memex.Portal.Shared/Memex.Portal.Shared.csproj | Drops the ships-the-bits ProjectReference to MeshWeaver.Markdown.Export, leaving only the contract in the app closure. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Test Results (shard 3) 11 files ±0 11 suites ±0 4m 51s ⏱️ - 1m 18s Results for commit 8923b59. ± Comparison against base commit 20b793b. This pull request skips 8 tests. |
Test Results 65 files ±0 65 suites ±0 43m 47s ⏱️ - 1m 44s Results for commit 8923b59. ± Comparison against base commit 20b793b. This pull request skips 8 tests. |
First step-2 flip of #1644 (after step-1 in #1653): MeshWeaver.Markdown.Export no longer rides any ships-the-bits ProjectReference — the
modules/folder is the only way its bits ship. This flip sets the pattern for the remaining 11 modules.What flipped
Memex.Portal.Shareddrops theMeshWeaver.Markdown.ExportProjectReference (no other host referenced it). Only the wire contract stays in the app closure, viaMeshWeaver.Blazor → MeshWeaver.Markdown.Export.Contract. Activation is unchanged:Modules:Assemblies→MarkdownExportProviderAttribute→AddMarkdownExport(); appsettings entries stay exactly as-is (MeshWeaver.Markdown.Export.dll— the loader probesmodules/first per feat(modules): the modules/ publish layout, step 1 — probing, compile surface, fingerprint (#1644) #1653).memex/MeshModulesPublish.targetsgains the closure lane (@(MeshModuleClosure)), with Markdown.Export as its first entry; the other 11 modules stay on the thin step-1 lane. The closure lane:Publishof the module intomodules/<Name>/(explicit Restore first — post-flip the module is outside the host graph;Buildruns with the module's ordinary global properties so a concurrent solution-level build merges into the same scheduled instance instead of racing one obj/, thenPublishwithNoBuild=true);runtimes/<rid>/trees —Assembly.LoadFromnever consults the module's deps.json, so a RID tree under a module folder is unreachable by construction;Microsoft.AspNetCore.App.Ref/Microsoft.NETCore.App.Reftargeting packs (self-maintaining — no hand list to drift), with a hard error if the packs cannot be found (a silent skip would ship framework duplicates);SatelliteResourceLanguages=en) instead of deleted afterwards;Buildof a host (intobin/…/modules/): a flipped DLL exists nowhere else, so without thisdotnet runwould die inAssembly.LoadFromat startup.Measured private deps (the closure, empirically)
Standalone module publish (160 files) diffed against the post-flip app closure (
-p:PublishMeshModules=falsepublish of the Monolith): 22 leftovers — 4 module-own files, 18 shared-framework assemblies, zero true private deps.modules/MeshWeaver.Markdown.Export/(each hand-verified):MeshWeaver.Markdown.Export.dll,.pdb,.xml,.deps.json— the module's own outputs only.Microsoft.AspNetCore.App.Reftargeting pack): 17 ×Microsoft.Extensions.*(Caching.Abstractions,Caching.Memory,Configuration,Configuration.Abstractions,Configuration.Binder,DependencyInjection,DependencyInjection.Abstractions,Diagnostics,Diagnostics.Abstractions,FileProviders.Abstractions,Hosting.Abstractions,Http,Logging,Logging.Abstractions,Options,Options.ConfigurationExtensions,Primitives) +System.Diagnostics.EventLog.dll.Markdig,DocumentFormat.OpenXml(+Framework),HtmlAgilityPack,UglyToad.PdfPig.*) still rides the app via other live references (ContentCollections, Blazor, DataSetReader.Excel); if one of those ever leaves, the closure publish+prune starts keeping the dep automatically.Publish assertions (real
dotnet publish memex/Memex.Portal.Monolith -c Release)modules/MeshWeaver.Markdown.Export/MeshWeaver.Markdown.Export.dllexists ✔MeshWeaver.Markdown.Export.dll✔ (the Contract dll rightly remains)bin/Release/net10.0/modules/after a plain build (thedotnet runpath) ✔Tests
MeshWeaver.Hosting.Monolith.Test,MeshWeaver.Security.Test,Memex.Portal.Shared.Test— they previously rode Portal.Shared's transitive reference); test projects keep shipping the DLL in their own bins.ExportBootPackTest.InstallingTheAssembly_AppliesAddMarkdownExport(real built DLL viaAssembly.Location) andModuleLaneSwitchTest.MarkdownExportModule_FoldsTheEngineRegistrations— no third copy added.InstalledModulesTest.ResolveModulePath_PrefersTheModulesFolder_AndFallsBackToBaseDirectory.Gate parity (design §5)
Companion PR in MeshWeaver.Plugins (
Systemorph/MeshWeaver.Plugins#498):fetch-refs.pyhoistsmodules/*/*.dllfrom the extracted platform image/apptree to the flat ref-set top level, so the compile gate keeps seeing a flipped module exactly as in-mesh compilation does (TPA + installed modules). Merge order: this PR first, Plugins after (the sweep is a no-op until an image carries a populated modules/ folder).Verification
Memex.Portal.Shared,Memex.Portal.Monolith,Memex.Portal.Distributed:-c Release -warnaserrorclean (0 warnings / 0 errors each).MeshWeaver.Hosting.Monolith.Testexport classes (DocumentExportAreaAccessTest,DocumentExportLayoutAreaTest,EmailDocumentExportTest): Passed! Failed: 0, Passed: 9 (fresh .trx) — they compose.AddMarkdownExport()via their now-direct project reference, proving the module code itself.MeshWeaver.Graph.Testfull suite: Passed! Failed: 0, Passed: 1183 (includes theResolveModulePathmodules-folder probing pin).MeshWeaver.Markdown.Export.Testfull suite: 137/137 (includesExportBootPackTest—InstallAssemblieson the real built DLL foldsAddMarkdownExport());Memex.Portal.Shared.TestModuleLaneSwitchTest: 2/2.validate-repos.py✔ (161 nodes / 20 repos),compile-check.py✔ (34 clean · 0 known-debt · 0 NEW breaks · 22s) — the sweep change is inert for both until the pinned image carries a populatedmodules/.No What's New entry: deployment machinery, no user-visible change.
🤖 Generated with Claude Code