RIDDL 2.0.0 Released
RIDDL 2.0.0 is now available.
RIDDL 2.0.0 is now available. This release of the RIDDL compiler and language tooling includes the following changes:
What’s New
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 ayields/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, andvalidate --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. Onlyyield/reply,error/requireandforwardsettle a path — asendortellno longer does. - A cross-context connector must terminate on the context’s own portlet, and
a
tell/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.