This file provides specific guidance for working with the RIDDL project. For general ossuminc organization patterns, see ../CLAUDE.md (parent directory).
RIDDL documentation has moved to ossum.tech/riddl
The Hugo-based documentation site at riddl.tech has been consolidated into the ossum.tech MkDocs site. Key documentation:
- Language Reference: https://ossum.tech/riddl/references/language-reference/
- EBNF Grammar: https://ossum.tech/riddl/references/ebnf-grammar/
- Tutorials: https://ossum.tech/riddl/tutorials/
- Tools (riddlc): https://ossum.tech/riddl/tools/riddlc/
The doc/ directory in this repository contains legacy Hugo content that
redirects to ossum.tech. Do not add new documentation here.
RIDDL (Reactive Interface to Domain Definition Language) is a specification language for designing distributed, reactive, cloud-native systems using DDD principles. It's a monorepo containing multiple cross-platform Scala modules.
RIDDL is a heavily used library both by Ossum Inc. and external consumers. Never make incompatible changes to public APIs without following this process:
- No removal of public API — Do not remove public methods, classes,
traits, or extension methods. If functionality must be retired, add
@deprecatedannotations with a migration message and the target major version for removal (e.g.,@deprecated("Use flatten() instead", "2.0.0")). - No breaking signature changes — Do not change parameter types, return types, or add required parameters to existing public methods. New parameters must have defaults.
- Deprecation warnings until next major release — Deprecated APIs must remain functional through the current major version (1.x). They may only be removed in the next major release (2.0.0).
- Additive changes only — New methods, extension methods, classes, and traits are always safe. Prefer adding new APIs alongside old ones rather than modifying existing ones.
When in doubt, add, don't change.
- Scala 3.7.4 (not Scala 2!) — overrides sbt-ossuminc's 3.3.7 LTS
default due to a compiler infinite loop bug in 3.3.x with opaque
types and intersection types (see
build.sbtheader comment) - ALWAYS use Scala 3 syntax:
while i < end do ... end while(NOTwhile (i < end) { ... })- No
nullchecks - useOption(x)instead - New control flow syntax with
do/then/end
Current version: 1.3.0 (updated Feb 2026)
With.Javascript(...)→With.ScalaJS(...)orWith.scalajs(lowercase for default)With.Native()→With.Native(...)(now requires parameter list, not just())With.BuildInfo.withKeys(...)→With.BuildInfo.withKeys(...)(project)(curried function)
// Scala 3.7.4 (overridden from sbt-ossuminc's 3.3.7 LTS default)
.configure(With.scala3) // Sets scalaVersion to 3.3.7 LTS
// Then override: scalaVersion := "3.7.4"
// Scala.js configuration
.jsConfigure(With.ScalaJS(
header = "RIDDL: module-name",
hasMain = false,
forProd = true,
withCommonJSModule = true
))
// Scala Native configuration
.nativeConfigure(With.Native(
mode = "fast", // "debug", "fast", "full", "size", "release"
buildTarget = "static", // or "application"
gc = "none",
lto = "none"
))
// BuildInfo with custom keys
.jvmConfigure(With.BuildInfo.withKeys(
"key1" -> value1,
"key2" -> value2
))utils → language → passes → commands → riddlc
↓
testkit
Note: The diagrams and hugo modules have been moved to the riddl-gen repository.
Purpose: Binary AST serialization for fast module imports
- Location:
language/shared/src/main/scala/com/ossuminc/riddl/language/bast/(inside language module) - Package:
com.ossuminc.riddl.language.bast - Cross-platform: JVM, JS, Native
- Status: Core functionality complete (as of Jan 2026)
Note: The legacy standalone bast/ directory has been removed (Jan 16, 2026). All BAST code lives in the language module's bast package.
Key files (all in language/shared/src/main/scala/com/ossuminc/riddl/language/bast/):
package.scala- Constants and node type tags (NODE_, TYPE_, STREAMLET_*, etc.)BASTWriter.scala- Serialization pass (extends HierarchyPass)BASTReader.scala- DeserializationBASTLoader.scala- Import loading utilityBASTUtils.scala- Shared utilitiesStringTable.scala- String interning for compression
The riddlLib module exports a TypeScript-friendly API via RiddlAPI object.
Key features:
- All method names preserved (not minified) via
@JSExport - JavaScript-friendly return types:
{ succeeded: boolean, value?: object, errors?: Array<object> } - All Scala types converted to plain JS:
List→Array- Case classes → Plain objects
Either→{ succeeded, value, errors }
Building npm packages (via sbt-ossuminc 1.3.0 helpers):
sbt riddlLibJS/npmPrepare # Assemble package (pure sbt)
sbt riddlLibJS/npmPack # Create .tgz tarball
sbt riddlLibJS/npmPublishGithub # Publish to GH Packages
sbt riddlLibJS/npmPublishNpmjs # Publish to npmjs.comCI Workflow: .github/workflows/npm-publish.yml triggers on
release or manual dispatch, uses sbt tasks directly.
Module format: ESModule ("type": "module" in package.json).
Consumers use import { RiddlAPI } from '@ossuminc/riddl-lib'.
Documentation:
NPM_PACKAGING.md- npm build and installation guideTYPESCRIPT_API.md- Complete TypeScript API reference
Published: @ossuminc/riddl-lib on GitHub Packages npm registry
CRITICAL DISTINCTION:
- Can appear anywhere in hierarchy
- Parser rules determined by enclosing container
include "entities.riddl"in a Context → must contain Context-valid content- Already implemented
- Loads BAST-serialized content into RIDDL models
- Full import:
import "file.bast"— loads all Nebula contents - Selective import:
import domain X from "file.bast" - Aliased import:
import type T from "file.bast" as MyT - Allowed locations: Root level, inside domains, inside contexts
- 14 definition kinds supported (domain, context, entity, type, etc.)
- Key files:
CommonParser.scala—bastImport(),selectiveBastImport()TopLevelParser.scala—loadBASTImports()post-parse loadingBASTLoader.scala— BAST file reading and content populationAST.scala—BASTImportcase class
- Tests: 4 passing in
BASTLoaderTest.scala - Validation: Integrated into
ValidationPass
- Wraps
ArrayBuffer[CV]for efficient modification - Extension methods:
.toSeq,.isEmpty,.nonEmpty - Do NOT use:
.toList,.iteratordirectly (not available) - Pattern:
contents.toSeq.map { ... }.toJSArrayfor JS conversion
- Scala 3 enum, not case classes
- Get type name:
token.getClass.getSimpleName.replace("$", "") - Extract text:
token.loc.source.data.substring(token.loc.offset, token.loc.endOffset)
- Fields:
line,col,offset,endOffset,source - Always 1-based (not 0-based)
- Delta encoding for BAST: compress by storing differences
Prefer HierarchyPass for maintaining parent context:
class MyPass extends HierarchyPass {
override def process(value: RiddlValue, parents: ParentStack): Unit = {
value match {
case d: Domain => processDomain(d, parents)
case c: Context => processContext(c, parents)
// ... pattern match all node types
}
}
override def result: MyPassOutput = MyPassOutput(...)
}BAST Writer Pattern:
BASTWriterPass(in passes module) extendsHierarchyPass- Uses
BASTWriterutilities (in language module) for byte writing - Sacrifice write speed for read speed
- String interning for deduplication
Updated: Jan 2026 for improved reliability and performance
- Triggers:
main,developmentbranches - Parallelized: JVM/Native/JS builds using matrix strategy
- Timeout: 60 minutes
- Dependency scanning with SARIF upload
- Auto-triggers on PRs and pushes (not manual-only)
- Timeout: 45 minutes
- Fixed artifact paths (was broken in earlier versions)
- Triggers only on Hugo/doc changes (NOT all .scala files)
- ScalaDoc caching for faster builds
- Timeouts: 30min build, 10min deploy
All workflows use JDK 25 (standardized)
When the Scala LTS version changes (either directly in build.sbt or when sbt-ossuminc updates its default), the following files MUST be updated:
GitHub Workflows (.github/workflows/):
-
scala.yml:
RIDDLC_PATHenv var:riddlc/native/target/scala-X.Y.Z/riddlc- Cache paths:
**/target/scala-X.Y.Z - Native artifact path:
riddlc/native/target/scala-X.Y.Z/riddlc - Native artifact path:
riddlLib/native/target/scala-X.Y.Z/libriddl-lib.a - JS artifact path:
riddlLib/js/target/scala-X.Y.Z/riddl-lib-opt/main.js
-
coverage.yml:
- Coverage report paths:
**/target/scala-X.Y.Z/scoverage-report/
- Coverage report paths:
sbt-ossuminc Version Policy:
- sbt-ossuminc always defaults to the latest Scala LTS version
- When sbt-ossuminc is updated, check if its default Scala version changed
- Current LTS: 3.3.7 (3.3.x series), but riddl uses 3.7.4 due to compiler bug (see build.sbt)
- Next LTS expected: 3.9.x (Q2 2026)
Quick Search to Find All References:
grep -r "scala-3\." .github/workflows/Example Fix (3.7.4 → 3.9.0):
# In each workflow file, replace all occurrences:
sed -i 's/scala-3.7.4/scala-3.9.0/g' .github/workflows/*.ymlAny change to the fastparse parser MUST have a corresponding change to the EBNF grammar.
The EBNF grammar at language/shared/src/main/resources/riddl/grammar/ebnf-grammar.ebnf
is the canonical specification of RIDDL syntax. It is validated by a TatSu-based parser
that runs in CI on all **/input/**/*.riddl test files.
When modifying the fastparse parser:
- Update the corresponding rule(s) in
ebnf-grammar.ebnf - Run the EBNF validator locally:
cd language/jvm/src/test/python pip install -r requirements.txt # first time only python ebnf_tatsu_validator.py
- Ensure both parsers accept the same inputs
- CI will fail if the EBNF parser cannot parse test files that fastparse accepts
This ensures the documented grammar stays in sync with the actual implementation.
When implementing new code:
- Write the code
- ALWAYS run
sbt "project <module>" compile - Fix Scala 3 syntax errors immediately
- Then proceed to next step
- Input test files:
language/input/<category>/<file>.riddl - Examples:
language/input/import/import.riddl
Cause: Using Scala 2 syntax
Fix: Use Scala 3 syntax with do/end
Cause: Token is an enum
Fix: Use token.getClass.getSimpleName
Cause: Contents is opaque type with limited extensions
Fix: Use .toSeq extension method
Cause: sbt-ossuminc 1.0.0 API change
Fix: Use With.ScalaJS instead
Cause: Scala 3.7.4 limitation — default parameter values in a case
class's first parameter list cannot resolve given instances from a
subsequent using clause in the generated companion apply method.
Fix: Remove the default value. May be fixed in 3.9.x LTS.
Example:
// This fails in 3.7.4:
case class Foo(x: Bar = Bar())(using PlatformContext)
// Fix: remove default (or provide explicit given)
case class Foo(x: Bar)(using PlatformContext)Cause: @JSExportTopLevel on a case class with (using PlatformContext) in a second parameter list. The JS export sees the
context parameter as a non-default parameter after defaulted params.
Fix: Remove @JSExportTopLevel from internal data structures that
don't need to be constructed from JS code.
Cause: System.lineSeparator() returns \0 in Scala.js
Fix: Use PlatformContext.newline instead. Never use
System.lineSeparator() in shared code. The FileBuilder trait
and its entire hierarchy use (using PlatformContext) for this.
- Create directory:
<moduleName>/shared/src/{main,test}/scala/... - Add to
build.sbtusingCrossModule - Add variants to root aggregation
- Dependencies go in both directions of
cpDep()
- Shared code:
<module>/shared/src/ - Platform-specific:
<module>/{jvm,js,native}/src/ - Avoid platform-specific APIs in shared code
- Use
PlatformContextfor platform abstraction
- sbt-dynver generates versions from git tags
- Format:
MAJOR.MINOR.PATCH-commits-hash-YYYYMMDD-HHMM - Clean tag:
git tag -a 1.0.0 -m "Release 1.0.0"(novprefix - it interferes with sbt-dynver) - Always run
sbt publishLocalafter tagging to make the new version available locally
Short description (imperative mood)
Detailed explanation of what changed and why.
Focus on "why" rather than "what".
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- main: Production releases
- development: Active development (current work)
- Always push to
developmentunless releasing
README.md- Project overviewNPM_PACKAGING.md- npm build guideTYPESCRIPT_API.md- TypeScript API reference.github/workflows/*.yml- CI/CD configurationCLAUDE.md(this file) - AI assistant guidance
- Keep npm packaging docs in sync with actual build
- Update version numbers when releasing
- Add examples for new features
- Link related documents
# Compile specific module
sbt "project bast" compile
# Run tests for module
sbt "project language" test
# Build npm package
./scripts/pack-npm-modules.sh riddlLib
# Format code
sbt scalafmt
# Check all platforms compile
sbt cJVM cJS cNative
# Run all tests
sbt tJVM tJS tNative
# Package riddlc executable
sbt riddlc/stage
# Result: riddlc/jvm/target/universal/stage/bin/riddlcAI-friendly validation pass for MCP server integration. Design complete in NOTEBOOK.md.
- Provides proactive guidance (Tips) rather than just errors/warnings
- Works on incomplete models (no ResolutionPass dependency)
- Designed for iterative AI-driven model building
BAST serialization is fully implemented with 60 tests passing and 6-10x speedup.
riddlc bastify <input.riddl>- Convert RIDDL to BASTriddlc unbastify <input.bast>- Convert BAST back to RIDDL (pending)
- Speed: 6-10x faster than parsing RIDDL source
- Size: 63-67% of source file size for non-trivial inputs
- Cross-platform: JVM, JS, Native all supported
Language module (language/shared/.../bast/):
BASTWriter.scala,BASTReader.scala- Serialization/deserializationBASTLoader.scala,BASTUtils.scala- Loading utilitiesStringTable.scala,PathTable.scala- Interning tables
Passes module: BASTWriterPass.scala - Pass wrapper for AST traversal
BAST format specification and API documentation should be added to ossum.tech/riddl (not this repository). See the Documentation section above.
readNode() vs readTypeExpression() - These handle DISJOINT tag sets:
readNode()→ NODE_* tags (definitions)readTypeExpression()→ TYPE_* tags (type expressions)- Mixing them causes byte misalignment ("Invalid string table index" errors)
Problem: This repository often has multiple work streams happening concurrently (BAST development, API fixes, documentation, etc.). Files from different work streams may be staged simultaneously.
Rule: ONLY commit files related to the specific task you're working on.
How to commit selectively:
# Check what's staged
git status
# Unstage files not related to current task
git restore --staged path/to/file1 path/to/file2
# Stage only what you need
git add path/to/related/file
# Commit
git commit -m "Focused commit message"If you accidentally commit too many files:
# Undo the commit but keep changes staged
git reset --soft HEAD~1
# Unstage the unrelated files
git restore --staged unrelated/files
# Recommit with only the relevant files
git commit -m "Corrected commit message"
# Re-stage the other work for future commits
git add unrelated/filesFiles that often appear staged but may not be related to your work:
CLAUDE.md- Documentation updatesbuild.sbt.bak- Backup files (should be in .gitignore)examples/- Example codelanguage/.../Keywords.scala- Keyword updatespasses/.../ValidationPass.scala- Validation rule updates
Always add backup files and temporary files to .gitignore:
*.bak- Backup files*.tmp- Temporary filestarget/- Build artifacts (already present).metals/- Metals IDE files (already present)
Core parsing/validation logic lives in RiddlLib (shared trait +
companion object) at riddlLib/shared/.../RiddlLib.scala. This is
usable on JVM, JS, and Native. The JS-only RiddlAPI.scala is a
thin facade that delegates to RiddlLib and converts results to
plain JavaScript objects.
- Cross-platform code: Use
RiddlLib.parseString(...)etc. with agiven PlatformContextin scope (provided by each platform'scom.ossuminc.riddl.utils.pc) - JS facade:
RiddlAPIadds@JSExportmethods,getDomains,inspectRoot, and JS-only helpers likeformatErrorArray
CRITICAL: All methods that accept an origin parameter use
RiddlLib.originToURL() to convert strings to URLs.
def originToURL(origin: String): URL =
if origin.startsWith("/") then
URL.fromFullPath(origin)
else
URL(URL.fileScheme, "", "", origin)
end ifWrong (Scala 2 style):
lines.foreach(pc.log.info) // Error: type mismatchCorrect (Scala 3):
lines.foreach(line => pc.log.info(line))Reason: Scala 3 doesn't automatically convert by-name parameters (=> String) to function parameters (String => Unit).
When code needs to be shared between JVM (riddlc commands) and JS (RiddlAPI), put it in utils/shared/:
Example: InfoFormatter is used by both:
commands/InfoCommand.scala(JVM)riddlLib/RiddlAPI.scala(JS via@JSExport)
// utils/shared/src/main/scala/com/ossuminc/riddl/utils/InfoFormatter.scala
object InfoFormatter {
def formatInfo: String = {
// Build info formatting logic
}
}After staging (sbt riddlc/stage), the riddlc executable provides:
riddlc help # Show all available commands
riddlc version # Version information
riddlc info # Build information
riddlc parse <file> # Parse RIDDL file
riddlc validate <file> # Validate RIDDL fileCommands can load options from HOCON config files.
Executable location: riddlc/jvm/target/universal/stage/bin/riddlc
lazy val mymodule_cp = CrossModule("mymodule", "riddl-mymodule")(JVM, JS, Native)
.dependsOn(cpDep(utils_cp), cpDep(language_cp))
.configure(With.typical, With.GithubPublishing)
.settings(
description := "Description here"
)
.jvmConfigure(With.coverage(50))
.jsConfigure(With.ScalaJS("RIDDL: mymodule", withCommonJSModule = true))
.nativeConfigure(With.Native(mode = "fast"))
lazy val mymodule = mymodule_cp.jvm
lazy val mymoduleJS = mymodule_cp.js
lazy val mymoduleNative = mymodule_cp.nativeThen add to root aggregation: .aggregate(..., mymodule, mymoduleJS, mymoduleNative)
Note: Use With.ScalaJS(...) for sbt-ossuminc 1.0.0+, not With.Javascript(...)
- Extend
Pass,DepthFirstPass, orHierarchyPass - Implement
process()method for each AST node type - Declare dependencies via
def requires(): Seq[Pass] = Seq(...) - Override
result()to return yourPassOutputsubclass - Add to standard passes or invoke explicitly
- Define options:
case class MyOptions(...) extends CommandOptions - Define command:
class MyCommand extends Command[MyOptions] - Implement:
def name: Stringdef getOptionsParser: OptionParser[MyOptions]def run(options: MyOptions, context: PlatformContext): Either[Messages, PassesResult]
- Register with
CommandLoaderif using plugin system
- Always check sbt-ossuminc version - API may have changed
- BAST version is single integer -
VERSION: Int = 1, stays at 1 until schema finalized for users - BAST Import is fully implemented - Full, selective, and aliased imports all work (Issue #72 resolved)
- npm packages are versioned by git - Rebuild after commits to get new version
- Workflow improvements made - Check .github/workflows/ for latest patterns
- ONLY commit files related to your current task - Multiple work streams may be active
- Check git status carefully before committing - Unstage unrelated files
- RiddlAPI origin parameters must be URLs - Use
originToURL()helper for String origins - Scala 3 lambda syntax required -
foreach(line => func(line))notforeach(func) - Share code via utils/shared/ - For code used by both JVM and JS variants
- BAST code lives in language module -
language/shared/.../bast/, NOT standalone directory - BAST readNode() vs readTypeExpression() - Disjoint tag sets; mixing causes byte misalignment
- Scala version is 3.7.4 - Each module overrides
scalaVersion := "3.7.4"(3.3.x has compiler bug with opaque types) - BAST location comparisons use offsets - Compare offset/endOffset, not line/col
- Scala version changes require workflow updates - Update
scala-X.Y.Zpaths in workflows - All RIDDL documentation goes to ossum.tech - Don't add docs to this repo's
doc/directory - Never use System.lineSeparator() in shared code - Use
PlatformContext.newlineinstead; returns\0in Scala.js - FileBuilder requires PlatformContext -
trait FileBuilder(using PlatformContext)— all subclasses must propagate the using clause - Scala 3.7.4 default param limitation - Case class defaults can't resolve givens from a subsequent using clause in generated apply; remove defaults or provide explicit givens
- @JSExportTopLevel incompatible with using clauses - Don't use on case classes that have
(using PlatformContext)in a second parameter list - npm packaging uses sbt-ossuminc helpers -
With.Packaging.npm()assembles package,With.Publishing.npm()publishes. Tasks:npmPrepare,npmPack,npmPublishGithub - npmTypesDir fixed in sbt-ossuminc 1.3.0 - Earlier versions had a convention mismatch (JS variant
baseDir/js/types/doubled tomodule/js/js/types/). No override needed with 1.3.0+ - npm requires --tag for prerelease versions - sbt-dynver versions like
1.2.3-1-hashare prerelease per npm semver. Must pass--tag devwhen publishing - riddlLib JS is ESModule - Changed from CommonJS (
withCommonJSModule = trueremoved). Package.json has"type": "module". Consumers useimport { RiddlAPI } from '@ossuminc/riddl-lib' - gh auth needs write:packages for npm - Run
gh auth refresh -s write:packagesif publishing to GH Packages npm registry - OutlinePass and TreePass - Lightweight HierarchyPass subclasses in
passes/shared/.../passes/. OutlinePass produces flatSeq[OutlineEntry]; TreePass produces recursiveSeq[TreeNode]. Exposed viaRiddlAPI.getOutline()andRiddlAPI.getTree()with TypeScript declarations - riddlLibJS tests now work - Fixed by overriding
Test / scalaJSLinkerConfigtoCommonJSModuleinbuild.sbt. Production bundle stays ESModule. 8 sharedRiddlLibTesttests run on both JVM and JS - NEVER put
import ',import ", orimport(in shared string literals - ESM shim plugins scan the JS bundle and rewrite these patterns. Use string concatenation ("im" + "port") or rephrase the message.ESMSafetyTestinriddlLib/jvm/src/test/enforces this by scanning the fullLinkJS bundle - Container.flatten() extension - Recursively removes Include/BASTImport wrappers in-place. Lives in
language/shared/.../Contents.scala. FlattenPass delegates toroot.flatten(). Use basePassnotDepthFirstPass— mutating contents during traversal corrupts ArrayBuffer iteration - release.yml automates multi-platform builds - Triggered by
gh release create. Builds native riddlc on macOS ARM64, macOS x86_64, Linux x86_64, plus JVM. Auto-updates homebrew-tap formula with SHA256 hashes - Homebrew formula supports native + JVM fallback - macOS ARM64 gets native binary (no JDK). Other platforms get JVM version with openjdk@21 dependency. Formula at
../homebrew-tap/Formula/riddlc.rb - RiddlLib shared trait -
riddlLib/shared/.../RiddlLib.scalaprovides cross-platform API (parseString, flattenAST, validateString, getOutline, getTree).object RiddlLib extends RiddlLibhas default implementations. RiddlAPI.scala is now a thin JS facade delegating to RiddlLib - parseString returns opaque Root in JS - As of 1.5.0,
RiddlAPI.parseString()returns the actual ScalaRootobject (opaque to JS). UsegetDomains(root)orinspectRoot(root)to access data. TypeScript type is brandedRootAST - Schema is in NonDefinitionValues - Schema extends Leaf (Definition) but is also in the
NonDefinitionValuesunion type. Its match case inValidationPass.process()must appear BEFOREcase _: NonDefinitionValues. Similarly, Relationship extends Leaf and must be matched beforecase _: Definition - CheckMessagesTest .check file format - Lines starting with space are continuation lines appended to the previous entry (with
\n). Non-space-starting lines begin new entries. Multi-line messages (like overloaded warnings) must not have new entries inserted mid-continuation - Streamlet shape validation guards on nonEmpty - Empty streamlets (
{ ??? }) should not be checked for inlet/outlet counts since they're placeholders. Usestreamlet.nonEmptyguard before shape checks - Publishing workflow - Tag with
git tag -a X.Y.Z -m "Release X.Y.Z", push tag, thensbt clean test publish. All modules publish to GitHub Packages across JVM, JS, and Native platforms. Version is derived from the git tag by sbt-dynver - Analysis passes in passes/shared/.../analysis/ - MessageFlowPass, EntityLifecyclePass, DependencyAnalysisPass. Each extends
CollectingPass, requires ResolutionPass. Run viaPass.standardPasses :+ XxxPass.creator()thenresult.outputs.outputOf[XxxOutput](XxxPass.name) - RecognizedOptions registry -
DefinitionValidation.RecognizedOptions.registry: Map[String, OptionSpec]validates option names, argument counts, and parent definition types. Unrecognized options produce StyleWarning (not Error) to keep extensible - RiddlLib analysis API methods -
getHandlerCompleteness(),getMessageFlow(),getEntityLifecycles()on shared RiddlLib trait + JSRiddlAPIfacade. Each runs standard passes plus the relevant analysis pass - HandlerCompleteness in ValidationOutput -
ValidationOutput.handlerCompleteness: Seq[HandlerCompleteness]populated inValidationPass.postProcess(). Categories:BehaviorCategory.Executable,PromptOnly,Empty - Downstream integration plans - Each downstream project (riddlsim, riddl-gen, riddl-mcp-server, synapify) has a
RIDDL-INTEGRATION-PLAN.mddescribing how to consume new library features. Designed for separate Claude instances working in those projects