Release 2.0.0
RIDDL 2.0.0 is the first major release since 1.x and the largest single body of
work in the project's history: 833 commits over six weeks, adding roughly
110,000 lines and nearly doubling the codebase. It is a language release —
2.0 exists to make RIDDL models precise enough that correct code can be
generated from them.
This release contains breaking changes. Deprecated 1.x spellings still
parse and now emit deprecations; several previously-silent model defects are
now errors. See Breaking Changes below.
What's New
The language
- Unified processor model — every
Processor(context, entity, projector,
repository, adaptor, and the new genericprocessor) can declare inlets and
outlets. Shape is either ascribed (context C as sink is {…}) or derived from
port arity. The oldsource/sink/flow/merge/split/routerkeywords
become deprecated aliases. - Intentions are grammar, not options — 14 keywords across three families,
written before the definition: entity role and consistency and persistence
(aggregate,consistent/available,event-sourced/persistent/transient),
context intent (application/external/gateway/service), and connector
durability and delivery (persistent,at-least-once/at-most-once/exactly-once).
They were options in 1.x, but the Computational Model calls options advisory,
and a hard error keyed off advisory metadata is a category error. - Processor instance identity —
Id(P)names an instance of any processor,
selfis the instance a handler is executing as,initiatecreates one and
yields its id, andterminateends one. Before this, RIDDL could describe
processors but not instances of them, soId(P)was a type with no producer.
tellmay now address an instance, not only a named processor. forward— delegation, and the only statement that discharges a
yields/repliesobligation by passing it on. Legal only where there is an
obligation to delegate: you cannot forward an event or a result.- Typed holes —
prompt("…") as Tascribes a checkable type to an
AI-computed value: the seam between RIDDL's deterministic tier and its AI tier.
The ascription restates the position's type and never overrides it. - Correlations in projectors — keyed accumulation of several events into one
command, with a mandatory timeout block. - Numeric literals —
count > 5now parses. Literal text is stored exactly
as written, so1.50and007survive a round trip. empty(and its synonymnone) — the minimum-cardinality inhabitant of a
type, legal wherever minimum cardinality is zero.- On-clause message binding —
on foo: command Foo { … }binds the handled
message to a local name;on other as xbinds the residual message's envelope. notand!are synonymous everywhere, building the identical AST node.- Multi-line
doandprompt—do { "a" "b" }, using the block spelling
RIDDL already had for prose. - A predefined
Riddlstandard module, available with no import:
BottomlessPitandForeverEmptyterminators,Envelope(CloudEvents v1.0
context attributes), andGeneratorError.
riddlc
riddlc find— a query engine over your model with 42 predicates and
actions, plus-exec,-replaceand-deletefor model-wide edits. Nothing
is written until every script has run, no two spans overlap, and the result has
been re-parsed and re-validated; a model that stops parsing or merely gains
errors is fully restored.- Every diagnostic names its rule — 375 rule ids in rustc's style:
[error] [msg-target-crosses-boundary] file(12:5). Suppress with
--no-msg-ids. validate --jsonemits one object per diagnostic for tooling, and
validate --fix/--fix-rule <id>applies the codemod a rule carries,
through the same verify-or-restore gate asfind -replace.- JSON is now a fully reflective surface alongside parse, prettify and the
binary AST —root → JSON → rootrecovers the exact AST, including the order
of definitions within their parent.
Validation
Many classes of model defect that used to pass silently are now reported,
including: a message sent to a portlet whose declared type cannot carry it;
duplicate field names; a message delivered where nothing can receive it; a
cross-context connector or send reaching past a context boundary into its
contents; an entity's own inlet and outlet obligations; a queried repository
with no index; and unreachable statements after error or terminate.
Breaking Changes
- What discharges a
yields/repliesobligation narrowed. Only
yield/reply,error/requireandforwardsettle a path — asendor
tellno longer does. - A cross-context connector must terminate on the context's own portlet, and
atell/send/forwardfrom outside a context must address the context
itself, never something it contains. There is no adaptor exemption. setandget from staterequire something that owns state — legal in an
entity or projector, an error elsewhere.- Every field of a constructor must be supplied, and constructor arguments
are now type-checked. errorandterminateare terminal in their statement block.- Deprecated but still parsing: the shape keywords,
option-spelled intentions,
type X is command {…},state X is <record>, quoted numeric constants,
andAbstract(nowAnything).
Internal
- Scala 3.9.0, sbt 2.0.6 and sbt-ossuminc 3.0.3, on a
projectMatrixbuild
with a flat cross-platform source layout. JDK 25. - Binary AST format advanced 17 revisions (6 → 23).
- Test suite grew from 173 to 427 files; 2.0.0 was certified from a genuinely
cold cache across all 19 module legs on three platforms — JVM 3,083 / JS
1,005 / Native 3,041 tests, zero failures — and validated against a
191-model, 1,017-file corpus. - A Scala.js performance fix took
Definition.hashCodefrom 384,016ns to 217ns
(1,770×), bringing browser-side validation to parity with the JVM.