Marten#4472: Modular composite configuration smoke + behavior tests across satellite assemblies#4495
Merged
Merged
Conversation
Pre-9.0 audit gap: monolithic services.AddMarten(opts => { ... }) is
well-tested, but the multi-assembly modular case — projection types
declared in satellite assemblies (each carrying [assembly: JasperFxAssembly]),
each contributing its own IConfigureMarten that references its local
projections, composed into a main host via DI — was never explicitly
validated against the post-#276 (SG-only dispatch) + post-#4461
(zero-runtime-codegen) contracts. This PR is the regression gate.
Adds three new top-level test projects (deliberately isolated from
CoreTests / DaemonTests / EventSourcingTests):
- src/ModularConfigTests.SatelliteA/
Order aggregate (Guid Id), OrderPlaced / OrderShipped events,
partial OrderProjection : SingleStreamProjection<Order, Guid>,
OrdersConfig : IConfigureMarten. [assembly: JasperFxAssembly].
JasperFx.Events.SourceGenerator wired as analyzer-only
PackageReference so [GeneratedEvolver] emits at compile time.
- src/ModularConfigTests.SatelliteB/
Daily aggregate (string Id), DailyOpened / DailyClosed events,
partial DailyProjection : MultiStreamProjection<Daily, string>
with date-keyed identity, ReportingConfig : IAsyncConfigureMarten.
Same JasperFxAssembly + SG analyzer wiring as SatelliteA.
- src/ModularConfigTests/
SmokeTest: end-to-end host composition exercising both
satellites' projections through inline dispatch. Pins:
1. OrderingTests — registration-order = invocation-order;
last-registered IConfigureMarten wins on scalar setter.
2. LastWinsTests — duplicate event-type registration is
idempotent; last-registered scalar setter wins.
3. AddMartenTimingTests — IConfigureMarten registered AFTER
services.AddMarten still applies (order-independent).
4. AsyncComposeTests — IConfigureMarten + IAsyncConfigureMarten
compose; both contributions land on final StoreOptions.
Critical finding surfaced during the work: a bare
services.AddSingleton<IAsyncConfigureMarten, T>() registration adds
the implementation to DI but never invokes its Configure() method.
The hosted service that drains async configs
(AsyncConfigureMartenApplication) is only registered by
services.ConfigureMartenWithServices<T>(). The chip's smoke-test
snippet used the bare AddSingleton; the fixture uses the
correct extension and the docs page calls the asymmetry out
explicitly. Filed upstream as a UX gap for follow-up.
Wired:
- src/Marten.slnx registers the three new projects under /Testing/.
- build/build.cs gains a TestModularConfig Nuke target, added to
the Test aggregate's .DependsOn(...) chain after TestEventSourcing.
- All four main CI workflows
(pg15-jsonnet[-eventsourcing], pgLatest-systemtextjson[-eventsourcing])
get a test-modular-config step adjacent to the existing
test-event-sourcing / test-document-db steps.
- docs/configuration/composite-configuration.md documents the pattern,
the four locked-in design contracts, the
[assembly: JasperFxAssembly] marker rationale, the SG-analyzer
satellite requirement, and the IConfigureMarten vs
IAsyncConfigureMarten registration asymmetry. Page added to the
VitePress sidebar.
- .nuke/build.schema.json regenerated to include the new target
(also drops the stale TestCodeGen entry that was orphaned from
build.cs — auto-regenerated cleanup).
Verified: smoke + 4 pin tests pass 5/5 on both net9.0 and net10.0.
./build.sh test-modular-config succeeds.
Closes #4472.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jeremydmiller
added a commit
that referenced
this pull request
May 19, 2026
Master merged #4495 (ModularConfigTests) after I branched, so the agent pass didn't see this fixture's `opts.GeneratedCodeMode = TypeLoadMode.Auto;` line. Build failed on CI for both NET10 + NET9 matrix legs. Removing the now-deleted property reference + the matching `using JasperFx.CodeGeneration` import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jeremydmiller
added a commit
that referenced
this pull request
May 19, 2026
Master merged #4495 (ModularConfigTests) after I branched, so the agent pass didn't see this fixture's `opts.GeneratedCodeMode = TypeLoadMode.Auto;` line. Build failed on CI for both NET10 + NET9 matrix legs. Removing the now-deleted property reference + the matching `using JasperFx.CodeGeneration` import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jeremydmiller
added a commit
that referenced
this pull request
May 19, 2026
* Eliminate all remaining JasperFx.CodeGeneration usages from Marten PR #4461 removed the JasperFx.RuntimeCompiler PackageReference and retired the major codegen surfaces. This is the long-tail cleanup of the leftover `using JasperFx.CodeGeneration` imports, the now-orphaned `GenerateCode(...)` interface methods + override bodies, the vestigial ICodeFileCollection plumbing, the [Obsolete] StoreOptions codegen-config properties, and the dead MartenSnapshot fingerprint feature that existed to invalidate codegen artifacts that no longer exist. What changed (112 files, +60 / -3387): - 8 stale `using JasperFx.CodeGeneration` imports dropped (no actual type usage post-#4461). - 3 dead interface methods removed: `IIdGeneration.GenerateCode`, `ISelectableColumn.GenerateCode`, plus the `UpsertArgument.GenerateCodeTo*` / `GenerateBulkWriterCodeAsync` virtuals (+ `MetadataColumn.setMemberFromReader`). Cascade-deleted the override bodies across 8 identity strategies, 9 upsert-argument derivatives, 13 metadata columns, plus DataColumn / IdColumn / Events/Schema/VersionColumn. Each type itself is preserved as a data carrier / config marker. - 5 whole-file deletions: `Internal/CodeGeneration/MartenSnapshot.cs`, `MartenSnapshotInputs.cs`, `FrameCollectionExtensions.cs`, `StoreOptions.GeneratesCode.cs`, `Events/EventGraph.GeneratesCode.cs`. Removed the `MartenSnapshot.VerifyAtBoot` / `PersistFingerprint` calls from `DocumentStore.cs` and the two CoreTests covering them. - ICodeFileCollection / ICodeFile implementations dropped from `DocumentStore`, `EventGraph`, and `StoreOptions` (carried implicitly through deleting the GeneratesCode partials). Removed 2 `services.AddSingleton<ICodeFileCollection>(...)` DI registrations from `MartenServiceCollectionExtensions.cs`. - StoreOptions scrubbed: `GeneratedCodeMode`, `SourceCodeWritingEnabled`, `GeneratedCodeOutputPath`, `AllowRuntimeCodeGeneration`, `SetApplicationProject`, `CreateGenerationRules`, `BuildFiles`, `_generatedCodeMode`, `PreferJasperFxMessage`. Test-side cleanup deleted dozens of `opts.GeneratedCodeMode = TypeLoadMode.X` lines. `ApplicationAssembly` kept — `AutoRegister` and `TryUseSourceGeneratedDiscovery` legitimately need it as a scan hint. - `Directory.Packages.props` orphan `JasperFx.RuntimeCompiler` PackageVersion line removed. Comments referencing RuntimeCompiler scrubbed in `SecondaryStoreProxyFactory.cs`, `CompiledQueryHandlerRegistry.cs`, `DocumentStore.CompiledQueryCollection.cs`, `Internal/ProviderGraph.cs`. Verification: - `src/Marten/` is now free of `JasperFx.CodeGeneration` imports — `grep -rn "JasperFx\.CodeGeneration" src/Marten/` returns empty. - Full solution builds clean (0 errors). - CoreTests 411/412 (1 pre-existing skip), DocumentDbTests 987/988 (1 pre-existing skip), LinqTests 1257/1258 (1 pre-existing skip). Doc updates and follow-up issues for the recovered Docker content (now relevant to Wolverine / JasperFx rather than Marten) ship in follow-up commits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Docs: codegen removal in Marten 9.0 + recover Docker content for sibling repos The companion to the code cleanup. Codegen-removal-related doc pages are slimmed to a brief "removed in 9.0, delete your Internal/Generated folder, codegen write no longer needed for Marten" note; the rest of the touched files are snippet refreshes from `mdsnippets` propagating the source-side scrubs. - docs/devops/devops.md — the application-Dockerfile section dropped the `RUN dotnet run -- codegen write` step + added a Marten-9.0 tip pointing at the new reality. The recovered Docker content (the pre-9.0 multi-stage Dockerfile that ran codegen-write in the build stage) is captured in the follow-up issues filed against the Wolverine and JasperFx repos for re-publication in their docs. - docs/configuration/cli.md — the Codegen warning block reframed: the `codegen` family is still exposed by the shared JasperFx CLI (for tools like Wolverine that ship their own codegen), but `dotnet run -- codegen write` is no longer necessary for Marten. - docs/configuration/aot-publishing.md — corrected the "kept as [Obsolete] no-ops" claim to "deleted entirely" for the codegen- config properties. - docs/configuration/optimized_artifact_workflow.md — same correction; the `GeneratedCodeMode` line was dropped from the sample bootstrapping. Snippet wrappers replaced with inline code blocks since the two snippets (`sample_simplest_possible_setup`, `sample_using_optimized_artifact_workflow`) no longer have a source region. - docs/migration-guide.md — the Runtime code generation removed section says the codegen-config knobs are deleted (not [Obsolete]); the Obsolete API sweep section drops the `GeneratedCodeMode` guidance. - docs/schema/index.md — pulled the stale warning about `opts.GeneratedCodeMode = TypeLoadMode.Auto` requiring a manual `Internal/` delete; not applicable post-9.0. - All other docs/**.md changes are `mdsnippets` refreshes propagated from the source-side scrubs in the previous commit (Startup.cs samples, hostbuilder snippets, ValueType / EventSourcing samples, etc.). Verified: `mdsnippets` clean, `markdownlint --disable MD009` clean across changed files, `cspell --config ./docs/cSpell.json` clean, `vitepress build docs` exits 0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix ModularConfigTests after rebase: drop opts.GeneratedCodeMode Master merged #4495 (ModularConfigTests) after I branched, so the agent pass didn't see this fixture's `opts.GeneratedCodeMode = TypeLoadMode.Auto;` line. Build failed on CI for both NET10 + NET9 matrix legs. Removing the now-deleted property reference + the matching `using JasperFx.CodeGeneration` import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #4472.
Pre-9.0 audit gap: the monolithic
services.AddMarten(opts => { ... })case is well-tested, but the multi-assembly modular case — projection types declared in satellite assemblies (each carrying[assembly: JasperFxAssembly]), each contributing its ownIConfigureMartenthat references its local projections, composed into a main host via DI — was never explicitly validated against the post-#276 (SG-only dispatch) + post-#4461 (zero-runtime-codegen) contracts.This PR adds the regression-gate fixture, the supporting Nuke + CI wiring, and a new docs page.
Three new isolated test projects
src/ModularConfigTests.SatelliteA/Orderaggregate (Guid Id),OrderPlaced/OrderShippedevents,partial OrderProjection : SingleStreamProjection<Order, Guid>,OrdersConfig : IConfigureMarten.[assembly: JasperFxAssembly]. SG analyzer wired locally.src/ModularConfigTests.SatelliteB/Dailyaggregate (string Id),DailyOpened/DailyClosedevents,partial DailyProjection : MultiStreamProjection<Daily, string>with date-keyed identity,ReportingConfig : IAsyncConfigureMarten. Same JasperFxAssembly + SG analyzer wiring.src/ModularConfigTests/The satellites are deliberately isolated from
CoreTests/DaemonTests/EventSourcingTests. Each test fixture builds a fresh host with a unique per-test schema name.Locked-in design contracts (pinned by the four tests)
OrderingTests— registration-order = invocation-order; last-registeredIConfigureMartenwins on scalar setter contention.LastWinsTests— duplicate event-type registration is idempotent (no throw); last-registered scalar setter wins.AddMartenTimingTests—IConfigureMartenregistered AFTERservices.AddMarten(...)still applies. TheStoreOptionsfactory resolvesIEnumerable<IConfigureMarten>at store-build time from the final DI snapshot.AsyncComposeTests—IConfigureMarten(sync) +IAsyncConfigureMarten(async) compose; both contributions land on the finalStoreOptions.Critical finding surfaced during the work — filed upstream as #4494
A bare
services.AddSingleton<IAsyncConfigureMarten, MyAsyncConfig>()registration adds the implementation to DI but never invokes itsConfigure()method. The hosted service that drains async configs (AsyncConfigureMartenApplication) is only registered as a side effect ofservices.ConfigureMartenWithServices<T>(). The chip's smoke-test snippet used the bareAddSingleton; the fixture uses the correct extension API and the docs page calls the asymmetry out explicitly. UX gap tracked at #4494.Wired into the build / CI
src/Marten.slnxregisters the three new projects under/Testing/.build/build.csgains aTestModularConfigNuke target, added to theTestaggregate's.DependsOn(...)chain afterTestEventSourcing.test-modular-configstep:on-push-do-ci-build-pg15-jsonnet-eventsourcing.yml(aftertest-event-sourcing)on-push-do-ci-build-pgLatest-systemtextjson-eventsourcing.yml(aftertest-event-sourcing)on-push-do-ci-build-pg15-jsonnet.yml(aftertest-document-db)on-push-do-ci-build-pgLatest-systemtextjson.yml(aftertest-document-db).nuke/build.schema.jsonregenerated to include the new target. Also drops the staleTestCodeGenentry that was orphaned frombuild.cs— auto-regenerated cleanup unrelated to this PR's scope but cheaper to include than to leave drifted.Docs
docs/configuration/composite-configuration.md(new) covers:[assembly: JasperFxAssembly]marker rationale (it's NOT a filter forDiscoverGeneratedEvolvers; the marker is for forward-compat with other Critter Stack scanning surfaces)partialprojection classes + analyzer-onlyJasperFx.Events.SourceGeneratorPackageReference)IConfigureMartenvsIAsyncConfigureMartenregistration asymmetry (AddSingleton<IAsyncConfigureMarten>() silently never runs — hosted service only wired via ConfigureMartenWithServices<T>() #4494)Page added to the VitePress sidebar under Configuration.
Test plan
./build.sh test-modular-configsucceeds locallydotnet build src/Marten.slnx -c Releasecleanmarkdownlintclean on the new docs pageOut of scope
.nupkgconsumed by downstream) — explicit follow-up if the smoke test surfaces concerns.TypeLoadMode.Staticvariant of the smoke test — the chip's smoke test usesTypeLoadMode.Auto; static-mode could be a follow-up.IConfigureMarten/IAsyncConfigureMartenthemselves.🤖 Generated with Claude Code