Fix Cratis meta-package + Chronicle consumption, realign per-TFM deps, and expose the MVVM observer boundary#2312
Merged
Conversation
The Directory.Packages.NET8-9/NET8/NET9.props override files exist to pin the .NET framework dependencies to the same major as the target framework. A blanket 'update stable dotnet package versions' chore had overwritten them with 10.x versions, defeating their purpose and forcing 10.x framework packages into net8 and net9 builds. Restore the framework-aligned versions: Microsoft.Extensions.* and System.* back to 9.x in the shared net8/net9 file and the dependency-injection packages back to 8.x (net8) / 9.x (net9). Additionally override Microsoft.Extensions.Logging.Debug to 9.x (the base props now pin it at 10.x for net10) and align net8 Microsoft.Extensions.DependencyModel to 9.x to match the EF Core 9.x the shared file uses. These satisfy released Chronicle 15.35.0's per-target-framework floors (net8 -> 8.0.1 / 9.0.13, net9 -> 9.0.13).
Arc 20.44.0 was compiled against a Chronicle whose IEventSequence.AppendMany signature differed from the released one consumers restore. 15.35.0 added a trailing optional 'Subject? subject' parameter to AppendMany/Append, which modifies the single method signature rather than adding an overload, so Arc compiled against the older signature bound to a method that no longer exists and threw MissingMethodException at runtime against released Chronicle. - Pin the Chronicle floor to released 15.35.0 (and bump Cratis.Fundamentals/Metrics.Roslyn and the net10 Microsoft.Extensions packages to the versions 15.35.0 requires) so a version mismatch fails restore instead of crashing at runtime. - Add a reflection guard spec pinning the AppendMany/Append overload signatures the command pipeline binds to, so a future Chronicle signature drift trips at build time instead of reaching consumers as a runtime crash.
The Cratis meta package bundles the Arc source generator into analyzers/dotnet/cs, and the individual packages (Cratis.Arc.Core, Cratis.Fundamentals) ship the same generator. A consumer referencing Cratis plus any package that transitively brings one of those (e.g. Cratis.Arc.MongoDB -> Cratis.Arc.Core) loaded the generator from two analyzer paths, ran it twice, and failed with CS0101 because the unconditional 'Cratis.Arc.Generated.GeneratedMarker' was emitted twice. - Add a buildTransitive target in Cratis.props that de-duplicates @(Analyzer) by file name before compilation, so every Cratis consumer collapses duplicate analyzer DLLs regardless of restore path (this also protects GeneratedQueryMetadata). - Make GeneratedMarker a partial type as defense-in-depth, so a duplicate emission merges instead of colliding even if an analyzer slips through de-duplication. - Cover the partial-merge defense with a unit spec that compiles two copies of the generated marker and asserts no CS0101.
Reproduce the analyzer double-load end to end: pack the meta-package closure into a local feed and build a consumer that references both Cratis and Cratis.Arc.MongoDB with a [ReadModel], asserting it builds clean with no CS0101. The read model makes the generator emit GeneratedQueryMetadata too, so the test specifically exercises the analyzer de-duplication rather than only the partial-marker defense. Run the packaging integration specs sequentially: they shell out to dotnet pack/build against the same projects and shared build outputs, so they cannot run concurrently without racing on those outputs and the NuGet restore.
ARCCHR0002 (ambiguous command event source id) only exempted commands whose Handle returns EventForEventSourceId directly, so it false-positived on commands that supply the event source explicitly through a tuple return. Do not report the diagnostic when the Handle return type is, or is a tuple containing, EventForEventSourceId / IEnumerable<EventForEventSourceId>, or is a tuple whose first element derives from EventSourceId<T> (new-stream create, where the event source is the returned/generated id rather than a command property and ICanProvideEventSourceId is neither needed nor implementable). The ambiguity warning still fires when a tuple return provides no event source.
withViewModel observes only the render it wraps, so a child that reads observable view model state directly needs its own observer boundary. Expose observer from the @cratis/arc.react.mvvm package root with documentation, so consumers wrap a leaf with it instead of importing from mobx-react/mobx-react-lite directly, and cover the export with a spec.
Flag direct imports from mobx-react and mobx-react-lite and steer consumers to import observer from @cratis/arc.react.mvvm instead, keeping the MobX binding an internal detail. Register the rule in the recommended config and cover it with rule tests.
Explain that withViewModel observes only the component it wraps, recommend passing plain props to presentational children, show leaf-only observer() for children that must read view model observables directly, and discourage blanket wrapping and direct mobx-react imports.
This was referenced Jun 21, 2026
|
NuGet packages for this PR, e.g. Cratis.Arc: |
The meta package repacked every analyzer DLL into analyzers/dotnet/cs so a single Cratis reference brought all analyzers. Because those same DLLs also ship in the individual packages, a consumer referencing Cratis plus any package that transitively brings a sub-package (e.g. Cratis.Arc.MongoDB -> Cratis.Arc.Core) loaded each generator twice and failed with CS0101. The patch for that was a per-consumer buildTransitive MSBuild task that de-duplicated analyzers by filename in every consumer's compile. Remove the bundling instead of patching around it. The meta package now just depends on the sub-packages: a Cratis-only consumer still gets full runtime and proxy generation, and the ARC*/ARCCHR* analyzers flow when any sub-package is referenced (the common case). This eliminates the double-load at its source and drops the MSBuild de-duplication task. - Revert Cratis.csproj analyzer bundling and the Cratis.props dedup task. - Drop the Cratis.Architecture.CodeAnalysis pin (only used by bundling). - Revert the GeneratedMarker partial defense-in-depth and its spec, and remove the GeneratorTestHelper.Compile helper it used. - Remove the MetaConsumerApp pack-and-build double-load regression; keep AssemblyParallelization.cs (serializes the pack/build integration specs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
NuGet packages for this PR, e.g. Cratis.Arc: |
The net8/net9 dependency overrides were realigned to their target-framework major but left at older patches. Move each pin to the latest stable release within its major so net8/net9 builds get current servicing fixes: - 9.0.13 -> 9.0.17 across EF Core, Microsoft.Extensions.* (Logging, Hosting, DependencyInjection(.Abstractions), DependencyModel, Configuration.FileExtensions), System.Text.Json, System.Reflection.MetadataLoadContext, and Mvc.Testing (net9). - net8 Microsoft.AspNetCore.Mvc.Testing 8.0.24 -> 8.0.28. Pins already at the latest in their line are unchanged: net8 DependencyInjection 8.0.1 / .Abstractions 8.0.2 (no newer 8.x), Npgsql.EntityFrameworkCore.PostgreSQL 9.0.4, Microsoft.Extensions.Telemetry/Resilience 9.10.0, Microsoft.Build.* 17.14.28. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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
Makes Arc consumable again from the
Cratismeta package alongside Arc.Chronicle, and tightens two long-standing rough edges. Arc 20.44.0 failed real consumers at both build and runtime; this restores a clean restore-build-run path against released Chronicle, fixes an analyzer false-positive, and exposes the sanctioned MobX observer boundary in the React MVVM package. It also restores the per-target-framework dependency pins (net8 → 8.x, net9 → 9.x) that a blanket version-bump chore had overwritten with 10.x, so net8/net9 builds no longer pull 10.x framework packages.Added
observerfrom@cratis/arc.react.mvvmso a child component that reads view model observables directly can be given its own observer boundary, with guidance in the React MVVM documentation.no-direct-mobx-react-importESLint rule that flags direct imports frommobx-react/mobx-react-liteand points consumers to@cratis/arc.react.mvvminstead.Changed
Cratis.Chronicleto15.35.0.Cratismeta package no longer bundles the Cratis analyzers; the ARC*/ARCCHR* analyzers now flow through the individual packages. A project referencing onlyCratisstill gets full runtime and proxy generation, but receives the analyzers only when a sub-package such asCratis.Arc.CoreorCratis.Arc.MongoDBis referenced.Microsoft.Extensions.*packages.mobx-react/mobx-react-liteimports as errors.Fixed
Cratismeta package alongside a package that transitively bringsCratis.Arc.Core(such asCratis.Arc.MongoDB) no longer fails the build withCS0101 'Cratis.Arc.Generated.GeneratedMarker'.MissingMethodExceptionat runtime against released Chronicle. The Chronicle floor now matches theAppendMany/Appendoverloads Arc actually calls, so an incompatible Chronicle version failsdotnet restoreinstead of crashing at runtime.ARCCHR0002analyzer no longer reports an ambiguous command event source id when the command'sHandlesupplies the event source explicitly through a tuple return — including new-stream-create commands that return a generated id deriving fromEventSourceId<T>.