Skip to content

WolverineFx.Bobcat takes the Critter Stack vocabulary, Bobcat's samples and the spec writer (#5082) - #5083

Merged
jeremydmiller merged 5 commits into
mainfrom
bobcat-vocabulary-5082
Oct 19, 2026
Merged

jeremydmiller merged 5 commits into
mainfrom
bobcat-vocabulary-5082

Conversation

@jeremydmiller

@jeremydmiller jeremydmiller commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Bobcat 1.0.0-rc.2 (JasperFx/bobcat#755) drops every JasperFx, JasperFx.Events and Wolverine dependency from Bobcat core. The pieces that need them move here, as the design of record (design/jasperfx-decoupling.md in Bobcat) decided.

Refs #5082, JasperFx/bobcat#755

What moves, and where it lands

The Critter Stack vocabulary → WolverineFx.Bobcat (src/Bobcat/Wolverine.Bobcat), namespaces unchanged (Bobcat.CritterStack, Bobcat), so a consumer's source does not change:

  • CritterStack/: CritterStackScenario, CritterStackSpec, CritterStackActOutcome, EventStores, DocumentStores, StoreRouting, EventStoreAuthoring, CritterStackHostExtensions, ProjectionsBehind and the two timeout exceptions. RecordBuilding stays in Bobcat core.
  • EventModel/: GeneratedEventModel, SpecIdentityAudit.
  • Preflight.AddContainerChecks is now an extension method over Bobcat's Preflight (PreflightContainerChecks), still delegating to JasperFx's EnvironmentChecker.
  • WolverineFx.Bobcat's own code: [EventModelTrigger(TriggerKind.Http, …)] → ActTrigger.Http. Nothing here used JasperFx.Testing.DispositionKind or the recovery-hint attributes, and nothing used EnvironmentCheckResults.

Tests

  • Bobcat.CritterStackScenario.Tests → src/Bobcat/Bobcat.CritterStackScenario.Tests, now net9.0 and net10.0. Marten runs on this repo's docker-compose Postgres through Servers, Fisher on a temp SQLite file, and [PostgresFact] still never skips when CI=true. It is in wolverine.slnx and CIBobcat.
  • SpecIdentityAuditTests (the pure one and the end-to-end one with SliceIdentitySpecs) and CritterStackSpecRoleTests → Wolverine.Bobcat.Tests. The role specs used to be compiled in a generator harness. Here they are this assembly's own projected specs ([Fact(Explicit = true)]), read through GeneratedEventModel.
  • EventTagQueryDescriptionTests → Bobcat.CritterStackScenario.Tests, beside EventStores.Describe.

The samples → src/Samples/Bobcat. All 13, plus README, sample-projects.md, docker-compose.yml, init/ and LICENSE.

  • They take this repo's Wolverine projects and WolverineFx.Bobcat as ProjectReferences, the way src/Samples does.
  • They take Bobcat, Bobcat.Xunit and Bobcat.Generators as packages, so those packages' buildTransitive props apply.
  • Their Directory.Packages.props imports the root one, so the Critter Stack versions are this repo's.
  • Wolverine.CritterWatch moves from a local-feed 1.0.2-vehicle.1 to the published 1.1.0-rc.1.
  • build/BobcatSamples.cs has CompileBobcatSamples, TestBobcatSamples (one Postgres database per sample, on 5433) and TestBobcatSamplesOnFisher. .github/workflows/bobcat-samples.yml runs ./build.sh BobcatSamples.
  • The names carry a Bobcat prefix because TestSamples already exists here.

The spec writer → new package WolverineFx.Bobcat.EventModel (src/Bobcat/Wolverine.Bobcat.EventModel), with the write-specs JasperFx command next to scaffold.

  • It contains EmlangSpecWriter, SpecProjectWriter, the spec arm of ImportMerge (SpecMerge) and the spec arm of ImportFileWriter.WriteSpecs (SpecFileWriter).
  • An application that references the package runs dotnet run -- write-specs board.json (checked: it appears in help in BankAccountES).
  • The flags and callout codes are the old ones (--specs-out, --specs-namespace, --app-project, --store, --bobcat-version, --wolverine-version), plus the import flags the writer needs. It merges into the stubs only the members the specs need.
  • A generated spec project pins WolverineFx.Bobcat to this package's own version, and Bobcat to the version stamped from Directory.Packages.props.
  • Tests are in Wolverine.Bobcat.EventModel.Tests.

Findings worth a look

  • A fix in the moved code. Running the store-only tests on net9.0 (they were net10.0-only in Bobcat) changed the test order. That exposed a tenant's first read in a fresh schema failing with Marten's immediate "No event activity was detected". It is the Harden the dead letter queue posting to the DB #497 empty-store case, which had been fixed for the untenanted wait only. The tenant wait now returns at once when the tenant's own view holds no events on a single-database store.
  • wolverine#4975, turned red as intended. BankAccountES pinned the Query-vs-View disagreement on its GET slices so that the fix would turn it red, and against this repo's Wolverine it did. The specs now assert a Query that reads its response type, with no disagreement.
  • No reflection into Bobcat. Bobcat rc.2 made the members the moved code needs public (RecordedStep.Failure's setter, CuratedScenario.Source/SourceChapter, CuratedFieldTypes, EmlangReader.Text), so they are called directly.

Merge order

Bobcat 1.0.0-rc.2 is published and this branch restores it from nuget.org (the temporary NuGet.config is gone). Against rc.1 the vocabulary types would be declared twice (CS0433), so this needs rc.2 or later.

  1. Merge, then ship WolverineFx.Bobcat (+ .EventModel). WolverineFx.Bobcat 6.51.1-alpha.1 does not run on rc.2 (bobcat's compatibility page says so).
  2. WolverineFx.Bobcat: scheduled messages are explicit (#5077); ThenHandled, scheduled grammars, body-less HTTP acts, GivenReceived (#5076) #5091 (WolverineFx.Bobcat vocabulary gaps from bobcat#683: ThenHandled, ThenMessageScheduled values, body-less HTTP acts, a received-message Given #5076/WolverineFx.Bobcat: WhenReceived of a TimeoutMessage only reschedules it, so a spec of the timeout handler passes with nothing run #5077) adds [EventModelTrigger(TriggerKind.Http, …)] attributes; whichever merges second renames those to ActTrigger.Http.

On rc.2 from nuget.org, run per framework (both frameworks at once share Marten schemas locally and collide; CI runs one framework per target): Wolverine.Bobcat.Tests 223 passed / 8 skipped on each of net9.0 and net10.0, Bobcat.CritterStackScenario.Tests 208/208 on each, Wolverine.Bobcat.Http.Tests 43/43, Wolverine.Bobcat.EventModel.Tests 156 passed / 2 skipped.

Verified locally

  • dotnet build wolverine.slnx -c Release -f net9.0 and the unpinned build: 0 errors.
  • ./build.sh CIBobcat --framework net9.0 with CI=true:
    • Wolverine.Bobcat.Tests: 223 passed, 8 skipped
    • Wolverine.Bobcat.Http.Tests: 43 passed
    • Bobcat.CritterStackScenario.Tests: 207 passed (208 with EventTagQueryDescriptionTests, run directly on net9.0 and net10.0)
    • Wolverine.Bobcat.EventModel.Tests: 78 passed, 1 skipped
    • IncidentService.Tests: 8 passed
    • ProcessManagerViaHandlers.Tests: 29 passed
  • The same suites also pass directly on net10.0.
  • ./build.sh BobcatSamples: every sample project compiles, every runnable sample's specs pass, and BankAccountES passes on Fisher (24/24).

🤖 Generated with Claude Code

https://claude.ai/code/session_01YDKQhnBVcmVLCTkJbVeADk

jeremydmiller and others added 4 commits October 18, 2026 12:32
GH-5082)

Bobcat 1.0.0-rc.2 (JasperFx/bobcat#755) drops every JasperFx, JasperFx.Events and Wolverine dependency
from core, so the store-agnostic vocabulary moves here, keeping its namespaces:

- src/Bobcat/Wolverine.Bobcat/CritterStack: CritterStackScenario, CritterStackSpec, CritterStackActOutcome,
  EventStores, DocumentStores, StoreRouting, EventStoreAuthoring, CritterStackHostExtensions,
  ProjectionsBehind and the two timeout exceptions (Bobcat.CritterStack). RecordBuilding stays in core.
- EventModel/GeneratedEventModel and SpecIdentityAudit (Bobcat).
- Preflight.AddContainerChecks, now an extension method over Bobcat's Preflight (JasperFx's
  EnvironmentChecker), so a call site reads as before.
- [EventModelTrigger(TriggerKind.Http, ...)] is [EventModelTrigger(ActTrigger.Http, ...)].

One core internal is reached by reflection until Bobcat exposes it: RecordedStep.Failure has an
internal setter, and recordTimedOutAct (bobcat#673) sets it on the open When step.

Tests: Bobcat.CritterStackScenario.Tests moves beside Wolverine.Bobcat.Tests (Marten on this repo's
Postgres through Servers, Fisher on temp SQLite, [PostgresFact] never skipping with CI=true), now on
net9.0 and net10.0 and in CIBobcat. The SpecIdentityAudit tests and CritterStackSpecRoleTests move into
Wolverine.Bobcat.Tests, the role specs compiled as this assembly's own projected specs.

Running them on net9.0 exposed an order-dependent bug the net10.0-only Bobcat run never hit: a
tenant's first read in a fresh schema threw Marten's immediate "No event activity was detected"
(the #497 empty-store case, fixed then for the untenanted wait only). The tenant wait now returns
at once when the tenant's own view holds no events on a single-database store.

Bobcat comes from a local preview feed (NuGet.config, 1.0.0-rc.2.local.1) until rc.2 publishes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDKQhnBVcmVLCTkJbVeADk
…em (GH-5082)

All 13 samples from the Bobcat repository (JasperFx/bobcat#755) land in src/Samples/Bobcat with their
README, sample-projects.md, docker-compose.yml, init scripts and LICENSE. They are written against the
Critter Stack vocabulary that now ships in WolverineFx.Bobcat.

- They reference this repository's Wolverine projects (and WolverineFx.Bobcat) by ProjectReference, the
  way src/Samples consumes Wolverine, and Bobcat, Bobcat.Xunit and Bobcat.Generators as packages, so the
  packages' buildTransitive props apply as for any consumer. Directory.Build.props adds those four to
  every Tests project.
- Directory.Packages.props imports the repository's own, so the samples run on exactly its Marten, Fisher,
  JasperFx.Events, xUnit and Alba. Only the Aspire packages and Wolverine.CritterWatch are pinned there;
  CritterWatch moves from a local-feed 1.0.2-vehicle.1 to the published 1.1.0-rc.1, whose WolverineFx
  6.51.0 dependency the ProjectReference satisfies, and BankAccountES's NuGet.config goes.
- build/BobcatSamples.cs carries Bobcat's Samples targets as CompileBobcatSamples, TestBobcatSamples
  (one Postgres database per sample, on this repo's docker-compose Postgres, 5433) and
  TestBobcatSamplesOnFisher (BankAccountES on a SQLite file). bobcat-samples.yml runs BobcatSamples.
- BankAccountES's TriggerKind becomes ActTrigger. Its EventModel specs pinned wolverine#4975's
  Query-vs-View disagreement so that the fix would turn them red, and against this repository's
  Wolverine it did: the GET slices now assert a Query reading its response type, with no disagreement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDKQhnBVcmVLCTkJbVeADk
…l --specs (GH-5082)

Bobcat keeps importing an eventmodelers.ai board as stubs and EventModelDefinitions; writing WolverineFx.Bobcat
specifications for the board's examples moved here with the vocabulary they are written against
(JasperFx/bobcat#755). New package WolverineFx.Bobcat.EventModel (src/Bobcat/Wolverine.Bobcat.EventModel):

- EmlangSpecWriter and SpecProjectWriter from Bobcat.EventModel, the specification arm of its additive merge
  (SpecMerge: MergeSpecs, MergeGlobalUsings) and of ImportFileWriter (SpecFileWriter.WriteSpecs), with the
  same callout codes.
- The write-specs JasperFx command, next to scaffold: an application referencing the package runs
  `dotnet run -- write-specs board.json`. It keeps the flags --specs-out, --specs-namespace, --app-project,
  --store, --bobcat-version and --wolverine-version, plus the import's --model, --namespace, --out,
  --chapter, --aggregate, --starts-stream, --strong-typed-ids, --single-definition, --json and --force,
  and merges into the stubs only what the specifications need.
- A generated spec project pins WolverineFx.Bobcat to this package's own version and Bobcat to the version
  it was built against (stamped from Directory.Packages.props), instead of constants moved by hand.

Bobcat.EventModel 1.0.0-rc.2 keeps three members internal that the writer used. EventModelInternals reaches
CuratedScenario.Source/SourceChapter and CuratedFieldTypes.TryInfer by reflection, and copies the trivial
EmlangReader.Text, until Bobcat makes them public; it is the one file to delete then.

Tests (Wolverine.Bobcat.EventModel.Tests): EmlangSpecWriterTests, SpecProjectWriterTests and
DeclaredIdentityTests, the specification halves #755 cut from the import tests, the spec-merge arm of
ImportMergeTests plus the idempotency and --force cases for specifications, and write-specs end to end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDKQhnBVcmVLCTkJbVeADk
The test JasperFx/bobcat#755 deleted from Bobcat's TagsAndStrongTypedIdsTests, beside the store-only
vocabulary's other tests, with its own tag types.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDKQhnBVcmVLCTkJbVeADk
…tion bridges

Bobcat rc.2 made RecordedStep.Failure, CuratedScenario.Source/SourceChapter, CuratedFieldTypes and
EmlangReader.Text public (JasperFx/bobcat#755), so EventModelInternals and failStep go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDKQhnBVcmVLCTkJbVeADk
@jeremydmiller
jeremydmiller marked this pull request as ready for review October 19, 2026 20:07
@jeremydmiller
jeremydmiller merged commit 2c7d1b9 into main Oct 19, 2026
46 checks passed
jeremydmiller added a commit that referenced this pull request Oct 19, 2026
…ead spec starts with no Appointments

The body-less HTTP acts' [EventModelTrigger] take Bobcat's ActTrigger since #5083 moved to Bobcat 1.0.0-rc.2.
modeled_read_models.a_single_read_model_needs_a_stream_to_be_about passed only when the collection's other
specs left zero or several Appointments behind; it now clears them first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDKQhnBVcmVLCTkJbVeADk
This was referenced Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant