Skip to content

Fix Cratis meta-package + Chronicle consumption, realign per-TFM deps, and expose the MVVM observer boundary#2312

Merged
woksin merged 10 commits into
mainfrom
fix/cratis-meta-package-consumption-and-mvvm
Jun 21, 2026
Merged

Fix Cratis meta-package + Chronicle consumption, realign per-TFM deps, and expose the MVVM observer boundary#2312
woksin merged 10 commits into
mainfrom
fix/cratis-meta-package-consumption-and-mvvm

Conversation

@woksin

@woksin woksin commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Makes Arc consumable again from the Cratis meta 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

  • Exported observer from @cratis/arc.react.mvvm so a child component that reads view model observables directly can be given its own observer boundary, with guidance in the React MVVM documentation.
  • Added a no-direct-mobx-react-import ESLint rule that flags direct imports from mobx-react / mobx-react-lite and points consumers to @cratis/arc.react.mvvm instead.

Changed

  • Raised the minimum required Cratis.Chronicle to 15.35.0.
  • The Cratis meta package no longer bundles the Cratis analyzers; the ARC*/ARCCHR* analyzers now flow through the individual packages. A project referencing only Cratis still gets full runtime and proxy generation, but receives the analyzers only when a sub-package such as Cratis.Arc.Core or Cratis.Arc.MongoDB is referenced.
  • Realigned the net8/net9 framework dependency versions to their target-framework major (8.x / 9.x), so building for net8 or net9 no longer resolves 10.x Microsoft.Extensions.* packages.
  • The recommended Cratis Arc ESLint config now reports direct mobx-react / mobx-react-lite imports as errors.

Fixed

  • Referencing the Cratis meta package alongside a package that transitively brings Cratis.Arc.Core (such as Cratis.Arc.MongoDB) no longer fails the build with CS0101 'Cratis.Arc.Generated.GeneratedMarker'.
  • Arc no longer throws MissingMethodException at runtime against released Chronicle. The Chronicle floor now matches the AppendMany/Append overloads Arc actually calls, so an incompatible Chronicle version fails dotnet restore instead of crashing at runtime.
  • The ARCCHR0002 analyzer no longer reports an ambiguous command event source id when the command's Handle supplies the event source explicitly through a tuple return — including new-stream-create commands that return a generated id deriving from EventSourceId<T>.

woksin added 8 commits June 21, 2026 11:16
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.
@github-actions

Copy link
Copy Markdown

NuGet packages for this PR, e.g. Cratis.Arc:
https://github.com/cratis/arc/packages/1655206?version=20.44.2

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>
@github-actions

Copy link
Copy Markdown

NuGet packages for this PR, e.g. Cratis.Arc:
https://github.com/cratis/arc/packages/1655206?version=20.45.1

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>
@woksin woksin merged commit 7184dd0 into main Jun 21, 2026
47 checks passed
@woksin woksin deleted the fix/cratis-meta-package-consumption-and-mvvm branch June 21, 2026 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant