Skip to content

v0.23.0

@jeremydmiller jeremydmiller tagged this 16 Sep 20:33
Every one of these came out of re-deriving CritterStackSamples' CritterCrush from its raw board
export (CritterStackSamples#21, #22) and then REVIEWING the rendered output rather than trusting a
green run. Four were silent — the model said one thing, the code or the console said another, and
nothing anywhere reported it.

#318 — AN UNRECOGNIZED DECLARED FIELD TYPE IS REPORTED. `fields: { statuses: Dictionary<Guid,
string> }` emitted `public string Statuses`. Ten scalars were known and everything else fell
through quietly. A declaration names a TYPE while a scenario value is a SAMPLE, so only
declarations are checked, and the finding is a WARNING rather than a problem because models relying
on the fallback must keep loading. The type vocabulary moved to CuratedFieldTypes so the reader and
the scaffolder cannot hold different opinions — and known types are canonicalised now, so
`fields: { id: GUID }` no longer emits `public GUID Id`, which never compiled.

#319 — A SLICE THAT MINTS ITS OWN STREAM ID CAN ASSERT WHAT IT EMITTED. With no stream bracketed,
"the events the act appended" was the empty list, so `Then {event} is emitted` said "the emitted
events were: []" while the store held a complete stream. That reads as "the handler did nothing",
and it forced a handler to derive its stream id from the act's payload just so a scenario could
name it — the test harness dictating a design decision. Now: a sequence floor before, queried
after. Note `Then no events are emitted` in a scenario that arranges no stream used to be vacuously
true and now means what it says.

#320 — A CURATED `given:` CAN NAME ANOTHER AGGREGATE. The shipped Gherkin always could; the curated
format could not, so a rule spanning two aggregates was unsayable. Worse than unsayable: every
arranged event landed on the acting slice's own aggregate, which for a multi-stream projection
WORKS, so the model said something untrue and the specs passed. Stream ids append rather than
re-shape, so a model naming no aggregate re-scaffolds unchanged.

#321 — A CONSUMING SLICE SEES ITS TRIGGER'S SHAPE. `elements:` resolved per slice only, so the
moment a trigger became an in-model emission the consumer's field list went empty and a StartStream
automation scaffolded as a [WriteModel] bind that fails at dispatch. Nothing about the trigger had
changed; only where it was written down.

#322 — A REPEATED STEP IS ITS OWN ROW, AND FINISHES. StepId is a step TEMPLATE id, not an identity,
and both step shapes the grammar most recently encouraged repeat. Measured on a real 37-scenario
suite: 33 of 182 steps stuck at "running" across 21 scenarios, every one of them a CleanPass. The
console collapsed them on top of that, keeping the last occurrence's text with the first one's
duration. Fixed without a protocol change — starts key on stepNumber, finishes pair with the first
occurrence still running.

#323 — `@pattern:` LETS A SPEC STATE ITS PATTERN INSTEAD OF GUESSING IT. Every Automation slice
carried "Pattern: Declared claims Automation; Declared claims Command" — a hotspot no change to the
model or the code could clear, because `When X is received` is the same sentence for a bus command
and an automation's trigger. Abstaining on bus acts was rejected: this repo's own saga lane receives
real commands. A tag can say what a sentence cannot.

⚠️ SCAFFOLDED FEATURES NOW CARRY A `@pattern:` TAG, so a sample claiming byte-for-byte regeneration
needs re-scaffolding. CritterCrush also gains the ability to express the two-aggregate guard it
currently records as a hotspot.

Also: docs/versions.md was five bumps stale while calling itself the canonical dependency set, and
its Test stack row still described the pre-MTP world. Re-derived from the real nuspecs — the set is
now aligned EXACTLY rather than merely compatibly, with every floor at the pin.

NOT IN THIS RELEASE, deliberately. #324 (a unit-tested slice reachable from the Event Model) is a
four-part feature with its design decided and recorded, not a defect. jasperfx#859 owns the other
half of #323: a disagreement between two sources on the same rung names neither of them. And the
scaffolder still emits a `Given no events for …` for a slice it knows creates the stream, which
wants its own change because it interacts with `{streamId}`.

17 assemblies / 1475 tests green throughout; console front end 127 vitest and vue-tsc clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Assets 2
Loading