Repository navigation
feat(ci): the compile stage bakes shipped content — boot loads, never compiles (#1660 WS1 step 1) - #1674
Conversation
… compiles (#1660 WS1 step 1) The content gates (doc-gate: Doc + samples; plugin-gate: the build-time MeshWeaver.Plugins sync, Store + Essentials) stop being verdict-only: mw-plugin-test --bake-output persists every NodeType the gate compiled to Ok as one prebuilt-assembly bundle per package (BundleWriter — the write half of the BundleReader codec), keyed to the run's framework MVID plus the synced source sha, and CI uploads the sets as baked-assemblies-<mvid> / baked-plugins-<mvid>. A green gate that produced no bake identity fails RED — no skip-trapdoors. The portal publish takes -p:PrebuiltBakeDir to lay bundles into prebuilt/ beside the app, and ShippedPrebuiltBundles seeds them at boot (after the static repo import settles, before the sweep probes the store) through PrebuiltAssemblySeeder — the sweep then reads those types Baked: pending=0 for covered shipped content, zero Roslyn on the pod. Best-effort and loud: anything unadoptable degrades to today's compile path. The MVID gate holds everywhere: a bundle from a DIFFERENT compilation declines wholesale. main-cd's image legs recompile under their own run number, so cross-workflow adoption waits for #1660 WS3 — the docker thinning follow-up is planned on the issue, and this change ships no main-cd wiring rather than a silently-inert artifact hand-off. Pinned by BakeOutputTest (producer output passes the consumer's DeclineReason gate) and ShippedPrebuiltBundlesTest (adopt ⇒ NodeTypeBakeStatus.Classify == Baked; mismatched MVID ⇒ declined whole; absent node ⇒ skipped, never waited on). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Implements #1660 WS1 step 1 by turning the existing “content compiles” CI gates into artifact producers that persist the compiled NodeType assemblies as MVID-keyed bundle zips, and adds a boot-time consumer that seeds those shipped bundles into the assembly store so the dynamic-type sweep can load instead of compile.
Changes:
- Extend
mw-plugin-testto optionally persist compiled NodeTypes into per-package prebuilt bundle zips (plusframework-mvid.txt) and wire CI to upload these artifacts with stable MVID-keyed names. - Add
BundleWriter(producer) and a boot-time shipped-bundle seeder (ShippedPrebuiltBundles.SeedAll) that adopts bundles before the dynamic-type sweep runs. - Add tests that pin producer/consumer conformance against the same MVID gate (
PrebuiltAssemblySeeder.DeclineReason) and verify “pending=0 / Baked” classification after adoption.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/MeshWeaver.PluginTester/Program.cs | Adds CLI args for bake output + source SHA provenance. |
| tools/MeshWeaver.PluginTester/PluginGateRunner.cs | Hooks bake persistence into the gate run so bake failures make the run red. |
| tools/MeshWeaver.PluginTester/BakeOutput.cs | Implements the bake persistence step that writes per-package bundle zips and framework-mvid.txt. |
| test/MeshWeaver.PluginTester.Test/BakeOutputTest.cs | Pins that baked bundles round-trip via BundleReader and pass the live MVID gate. |
| test/MeshWeaver.PluginCatalog.Test/ShippedPrebuiltBundlesTest.cs | Pins boot consumer adoption behavior (match adopts; mismatch declines; absent types skipped). |
| test/MeshWeaver.PluginCatalog.Test/ModuleLandingServiceTest.cs | Doc comment fix to reference the updated DeclineReason(string?) signature. |
| src/MeshWeaver.PluginCatalog/ModuleLandingService.cs | Doc comment fix to reference the updated DeclineReason(string?) signature. |
| src/MeshWeaver.Plugin.Packaging/BundleWriter.cs | Adds bundle writer implementing the zip format consumed by BundleReader. |
| src/MeshWeaver.Hosting/ShippedPrebuiltBundles.cs | Adds boot-time seeding of shipped prebuilt/*.zip bundles through PrebuiltAssemblySeeder. |
| src/MeshWeaver.Hosting/MeshWeaver.Hosting.csproj | References MeshWeaver.Plugin.Packaging so the consumer can read bundles. |
| src/MeshWeaver.Hosting/DynamicTypePreWarmerHostedService.cs | Sequences shipped-bundle seeding after static import and before the sweep. |
| src/MeshWeaver.Graph/Configuration/PrebuiltAssemblySeeder.cs | Exposes the MVID identity accessor and adds an overload for explicit live identity in tests. |
| src/MeshWeaver.Documentation/Data/Architecture/PluginPackaging.md | Links to the new CI content bake documentation page. |
| src/MeshWeaver.Documentation/Data/Architecture/CiContentBake.md | New architecture doc describing producer/identity/image/consumer flow for WS1 step 1. |
| memex/aspire/Memex.Portal.Distributed/Memex.Portal.Distributed.csproj | Adds optional publish-time inclusion of prebuilt bundle zips into prebuilt/. |
| .github/workflows/dotnet-test.yml | Wires gates to bake + upload MVID-keyed artifacts and emits step summaries. |
Suppressed comments (1)
.github/workflows/dotnet-test.yml:1132
- This script section also runs with
set -euo pipefail, butmvid_file="$RUNNER_TEMP/bake-plugins/framework-mvid.txt"will fail fast under-uifRUNNER_TEMPis unset, despite other paths in this workflow using${RUNNER_TEMP:-/tmp}. Using the same fallback here keeps the postcondition check consistent with the gate step.
set -euo pipefail
mvid_file="$RUNNER_TEMP/bake-plugins/framework-mvid.txt"
if [ ! -s "$mvid_file" ]; then
echo "::error::the plugins gate ran green but wrote no bake identity ($mvid_file) — the bake stage regressed."
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Test Results (shard 5)1 371 tests 1 370 ✅ 4m 38s ⏱️ Results for commit 4e5da71. ♻️ This comment has been updated with latest results. |
Test Results (shard 0)919 tests 918 ✅ 10m 54s ⏱️ Results for commit 4e5da71. ♻️ This comment has been updated with latest results. |
Test Results (shard 4)1 604 tests 1 598 ✅ 7m 36s ⏱️ Results for commit 4e5da71. ♻️ This comment has been updated with latest results. |
Test Results (shard 3) 11 files 11 suites 6m 1s ⏱️ Results for commit 4e5da71. ♻️ This comment has been updated with latest results. |
Test Results (shard 1)2 148 tests 2 045 ✅ 6m 49s ⏱️ Results for commit 4e5da71. ♻️ This comment has been updated with latest results. |
Test Results (shard 2)2 438 tests 2 434 ✅ 8m 17s ⏱️ Results for commit 4e5da71. ♻️ This comment has been updated with latest results. |
Test Results 65 files 65 suites 44m 16s ⏱️ Results for commit 4e5da71. ♻️ This comment has been updated with latest results. |
…lbacks
BakeOutput now hands BundleWriter open-FileStream factories instead of
buffering every DLL/PDB into byte[] first — the bytes flow disk → zip
per entry (BundleWriter opens and disposes each stream), so a large
bake no longer holds its whole assembly set in memory at once.
The workflow's new bake paths use the ${RUNNER_TEMP:-/tmp} fallback
shape the script already uses everywhere else, so set -u can never
kill the step on an unset RUNNER_TEMP.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # src/MeshWeaver.PluginCatalog/ModuleLandingService.cs # test/MeshWeaver.PluginCatalog.Test/ModuleLandingServiceTest.cs
Implements #1660 workstream 1, step 1: the CI compile stage that already proves the image's shipped content compiles now also produces the compiled assemblies as MVID-keyed prebuilt bundles, the image can ship them, and boot loads instead of compiling.
What shipped
Producer — the content gates persist what they compile
mw-plugin-testgains--bake-output <dir>/--source-sha <sha>: after the gate's checks, every NodeType that reachedCompilationStatus.Okis persisted as one prebuilt-assembly bundle per package (<package>.zip, newBundleWriterinMeshWeaver.Plugin.Packaging— the write half of the existingBundleReadercodec), plusframework-mvid.txtnaming the identity the bundles are keyed to. This reuses the batch-bake/compile pipeline the gate already runs (NodeTypeBatchBakeunder the hood) — nothing is re-implemented; the bake is a persist step over the gate's own assembly store.Oktype with no bytes in the store faults the run (GateReport.FatalError), and a green gate that wrote no bake identity fails the CI step. Nocontinue-on-error, no input-shapedif:.CI wiring (
dotnet-test.yml)doc-gate(Doc + samples stages) bakes into$RUNNER_TEMP/bakeand uploadsbaked-assemblies-<mvid>; the step summary lists per-package assembly counts.plugin-gatebakes the vital plugin modules (Store, Essentials) it already compiles from the build-time sync of Systemorph/MeshWeaver.Plugins and uploadsbaked-plugins-<mvid>, with the synced plugins-repo commit recorded as each bundle'ssourceSha(provenance). Same checkout, same deploy key, same preflight assertion as before — the gate is upgraded from verdict-only to artifact-producing, per the maintainer's direction on NodeType assemblies are built in CI and LOADED at runtime — no runtime Roslyn, no warm-up window (maintainer-directed) #1660.Image pickup + boot consumer
Memex.Portal.Distributed.csprojaccepts-p:PrebuiltBakeDir=<dir>→ bundles land inprebuilt/in the publish output/container image. Inert when unset.ShippedPrebuiltBundles.SeedAll(MeshWeaver.Hosting), sequenced insideDynamicTypePreWarmerHostedServiceafter the static repo import settles and before the sweep probes the store: reads eachprebuilt/*.zipwithBundleReader, gates once on the manifest MVID viaPrebuiltAssemblySeeder.DeclineReason, filters to NodeTypes this mesh actually holds (one enumeration — no per-missing-path waits), and adopts viaPrebuiltAssemblySeeder.Seed. The sweep then finds those typesBaked→pending=0, zero Roslyn for covered shipped content. Best-effort and loud: anything unadoptable degrades to today's compile.Docs: new Architecture/CiContentBake (+ link from PluginPackaging).
DocumentationLinkIntegrityTestgreen.Local bake over the repo's shipped content (the required run)
Doc stage (
--allow .github/doc-gate.allow):Samples stage (
--allow .github/samples-gate.allow, staged via.github/scripts/stage-samples-gate.sh):28 assemblies, 8 bundles, one framework MVID; the 8 known-debt types are absent from the bundles by design (no usable build to ship — the consumer compiles them as today). Bundle layout verified:
meshweaver/manifest.json(nodePath → assembly, frameworkMvid, sourceSha, per-type sourceVersions) +meshweaver/assemblies/<nodePath>.dll/.pdb— byte-compatible withBundleReader/PluginBundleClient.Seeder-conformance evidence
BakeOutputTest(new, full in-process gate run): the produced bundle round-trips throughBundleReaderandPrebuiltAssemblySeeder.DeclineReason(manifest.FrameworkMvid) == nullon the producing build — the exact gate that holds at consumption. Passed!ShippedPrebuiltBundlesTest(new,MonolithMeshTestBase): matching-MVID bundle ⇒ adopted, record stamped (CompiledFrameworkVersion= live,LastCompiledVersionset), bytes on the store under the probed key, andNodeTypeBakeStatus.Classify(def, storeHasBytes: true, live) == Baked— thepending=0pin. Mismatched MVID ⇒ declined whole, record untouched. Absent-node entry ⇒ skipped without hanging. Passed! (2/2)PrebuiltAssemblySeederGateTest+ fullMeshWeaver.Graph.Test,MeshWeaver.Hosting.Test,MeshWeaver.PluginCatalog.Test,MeshWeaver.PluginTester.Testsuites green (Release, build-then---no-build).Docker-thinning state (honesty over completeness)
Deferred — and one assumption in the brief was falsified. The CI run number is a compile input (
Version→ assembly attributes), somain-cd's image legs — which re-dotnet publishfrom source under their own run number — mint a different Graph MVID than this workflow's artifact. Shipping the Build-and-Test bake into the CD-built image today would produce bundles the seeder declines wholesale: a silently-inert feature that looks shipped. So this PR deliberately does not wire main-cd to consume the artifact. The concrete follow-up plan (bake inside the portal-image leg as the near-term step; thin containerize consuming the compile stage's publish once #1660 WS3 makes the identity deterministic) is posted as a comment on #1660. All plumbing an image producer needs (-p:PrebuiltBakeDir,prebuilt/seeding, MVID gate) ships here and is inert until used.Also deferred, tracked in the same comment: widening the plugin bake beyond the vital modules (PR wall-clock budget), and test lanes actually consuming
baked-assemblies-<mvid>.Refs #1660
🤖 Generated with Claude Code