This is the central engineering notebook for the RIDDL project. It tracks current status, work completed, design decisions, and next steps across all modules.
Last Updated: February 8, 2026
Scala Version: 3.7.4 (overrides sbt-ossuminc's 3.3.7 LTS default due to
compiler infinite loop bug with opaque types/intersection types in 3.3.x).
All workflow paths updated to scala-3.7.4.
1.6.0 Enhancements (uncommitted): Comprehensive library enhancements for simulator and generator support. 5 phases completed across 2 sessions:
- Phase 1: Handler completeness classification (A1), behavioral statistics (A2)
- Phase 2: MessageFlowPass (B1), EntityLifecyclePass (B2)
- Phase 3: DiagramsPass extensions (A3), DependencyAnalysisPass (B3)
- Phase 4: Recognized options vocabulary and validation (C1-C5)
- Phase 5: RiddlLib/RiddlAPI extensions (D1) Full clean build: 734 tests, 0 failures.
Release 1.6.0 Published: ValidationPass bug fixes and new validations. Fixed SagaStep undo check, SagaStep shape check, duplicate checkMetadata. Added validation for Schema, Relationship, Streamlet shape/handler, Adaptor/Repository handler requirements, Projector repo ref, Epic/UseCase user ref, Function input/output types. 1,526 tests pass (0 failures). Published to GitHub Packages.
Release 1.5.0 Published: Extracted cross-platform RiddlLib
trait from JS-only RiddlAPI. RiddlAPI is now a thin JS facade
delegating to RiddlLib. parseString returns opaque Root handle;
use getDomains()/inspectRoot() accessors. Fixed riddlLibJS/test
ESModule crash. 1,527 tests pass (0 failures). Published to GitHub
Packages (Maven + npm). BREAKING: TS consumers must update
parseString usage.
Release 1.4.0 Published: Container.flatten() extension, rewritten
FlattenPass, multi-platform release workflow. Native macOS ARM64
binary distributed via Homebrew.
npm Package Published: @ossuminc/riddl-lib published to GitHub
Packages npm registry via CI and locally. ESModule format with TypeScript
declarations. CI workflow (npm-publish.yml) uses sbt-ossuminc 1.3.0
tasks (npmPublishGithub/npmPublishNpmjs). Consumable via
npm install @ossuminc/riddl-lib with
@ossuminc:registry=https://npm.pkg.github.com in .npmrc.
Packaging Infrastructure: Docker, npm, and TypeScript support added:
Dockerfile— Multi-stage build with custom JRE via jlink (~80-100MB image)docker-publish.yml— CI workflow for Docker image publishing to ghcr.ionpm-publish.yml— CI workflow for npm package publishingriddlLib/js/types/index.d.ts— TypeScript type definitionspackage.json.template— Enhanced with TypeScript and ES module supportpack-npm-modules.sh— Updated with TypeScript definitions integration
Homebrew Tap Updated: ossuminc/homebrew-tap updated to 1.4.0.
macOS ARM64 gets native binary (no JDK dependency). Other platforms
fall back to JVM version with openjdk@21.
Branch Cleanup Complete: All stale feature/bugfix branches deleted. Only main
and development branches remain.
EBNF Validation Complete (Feb 1, 2026):
- Internal test files: 59/77 passed, 13 include fragments skipped, 5 expected failures (all have bugs that fastparse also rejects)
- External test files (riddl-examples): 8/9 passed, 1 expected failure (Trello needs AI regeneration)
- Key fixes made: comment regex tokens (avoid whitespace-skipping issues), statement rule (added morph/become), pseudo_code_contents rule, interactions rule (added comment support)
- CI updated to validate against riddl-examples repository
- Tasks #7-10 created for fastparse fixes to match EBNF (hex escapes, cardinality mutual exclusivity, metadata with-block requirement)
The RIDDL project is a mature compiler and toolchain for the Reactive Interface to Domain Definition Language. BAST serialization is complete (60 tests, 6-10x speedup). Hugo and diagrams modules moved to another repository.
Documentation: ossum.tech/riddl - all new docs go there, not this repo.
Status: Handed off — task document written at
../riddl-models/TASK-fix-validation-errors.md
45 of 186 riddl-models files fail riddlc validate. Error categories:
ambiguous path references (23), briefly outside with {} (4),
unresolved EmailAddress type (7), unresolved Year type (5),
decimal fractional part (2), complex multi-error (4).
After all files pass, add riddl-models EBNF validation to CI
(.github/workflows/scala.yml, mirroring existing riddl-examples
pattern).
Status: Pending
parseString now returns opaque Root. Downstream projects that
access result.value.domains directly must switch to
RiddlAPI.getDomains(result.value) or
RiddlAPI.inspectRoot(result.value).domains.
Projects to update: synapify, riddl-mcp-server, ossum.ai.
Expand riddlLib/js/types/index.d.ts to cover the full AST and Pass
hierarchies so TypeScript consumers can access the rich capabilities
of the RIDDL compiler — not just the RiddlAPI facade.
Current state: The existing index.d.ts (~390 lines) only
declares types for the RiddlAPI object's 12 exported methods and
their return types. The underlying AST node types (Domain, Context,
Entity, Handler, State, Type, etc.), pass outputs, and message types
are represented as opaque object or simplified interfaces.
Goal: Provide complete TypeScript type declarations for:
- AST hierarchy — All
RiddlValuesubtypes:Domain,Context,Entity,Handler,State,Adaptor,Saga,Projector,Repository,Streamlet,Epic,Function,Type, statements, expressions, etc. - Type expressions —
PredefinedType,AggregateTypeExpression,EntityReferenceTypeExpression,AliasedTypeExpression, etc. - Pass outputs —
PassesResult,SymbolsOutput,ResolutionOutput,ValidationOutput, message collections - Messages —
KindOfMessagesubtypes (Error,Warning,Info,MissingWarning,StyleWarning,UsageWarning) - Location —
Atwithline,col,offset,endOffset - Contents — Typed container contents for each definition kind
Approach: Generate declarations from the Scala AST source files
in language/shared/src/main/scala/com/ossuminc/riddl/language/AST.scala
and related files. Consider whether declarations should be
hand-maintained or auto-generated via a build step.
File: riddlLib/js/types/index.d.ts
Status: Requested by Synapify team (February 5, 2026)
Add a convenience function to RiddlLib (the JS-exported API object)
that converts a parsed AST to BAST bytes in a single call.
Motivation: Synapify needs to parse RIDDL in Electron's main
process (Node.js, for filesystem access/include resolution) and
transport the parsed AST to the renderer process via IPC. BAST binary
format is the natural transport format since BASTWriter/BASTReader are
already in the language/shared module (Scala.js compatible).
Proposed API:
// In RiddlLib or RiddlAPI:
@JSExport
def ast2bast(root: Root): js.typedarray.Int8Array = {
val input = PassInput(root)
val outputs = PassesOutput()
val pass = BASTWriterPass(input, outputs)
val result = Pass.runPass[BASTOutput](input, outputs, pass)
js.typedarray.Int8Array.from(result.bytes)
}Details:
- Uses BASTWriterPass via the Pass framework for correct traversal
- Returns
Int8Arrayfor efficient IPC transport (structured clone) - Synapify is prototyping this locally first, then will swap to the RiddlLib version when available
- BASTReader.read(bytes) already provides the inverse operation
- Consider also adding
bast2ast(bytes: Int8Array): js.Dynamicfor symmetry
AI-friendly validation pass for MCP server integration. See design section below.
(none)
| Date | Task | Notes |
|---|---|---|
| March 1, 2026 | Review and remove doc/src/main/hugo/content/ |
Hugo content migrated to ossum.tech/riddl. Redirect site is in doc/redirect-site/. After confirming redirects have been working for ~1 month, the Hugo content directory can be deleted. Keep doc/redirect-site/ for ongoing redirects. |
| November 2026 | Upgrade CodeQL Action v3 → v4 | GitHub deprecating CodeQL Action v3 in December 2026. Update .github/workflows/scala.yml line 182: github/codeql-action/upload-sarif@v3 → @v4. See changelog. |
Status: Fully implemented (January 30, 2026)
BAST import is fully functional with three syntax variants:
- Full import:
import "file.bast" - Selective import:
import domain X from "file.bast" - Aliased import:
import type T from "file.bast" as MyT
Allowed at root level, inside domains, and inside contexts. 14
definition kinds supported. 4 tests passing in BASTLoaderTest.scala.
Integrated into ValidationPass.
Key files: CommonParser.scala (parsing), TopLevelParser.scala
(post-parse loading), BASTLoader.scala (BAST reading), AST.scala
(BASTImport case class).
Note: The old doImport() stub in ParsingContext.scala:82-90
is superseded but still present. Could be cleaned up.
Status: Fully implemented and integrated (60 tests, all passing)
| Metric | Result |
|---|---|
| Speed | 6-10x faster than parsing RIDDL |
| Size | 63-67% of source file size |
| Platforms | JVM, JS, Native |
| CLI | riddlc bastify <file> |
Code locations:
- Utilities:
language/shared/.../bast/ - Pass:
passes/shared/.../BASTWriterPass.scala
Documentation: BAST format specification goes to ossum.tech/riddl (not here).
Key technical note: readNode() and readTypeExpression() handle DISJOINT tag
sets - mixing them causes byte misalignment. See CLAUDE.md for details.
| Decision | Rationale | Alternatives | Date |
|---|---|---|---|
| No namespace syntax for imports | RIDDL uses nested domains for namespacing | import "x.bast" as ns |
2026-01-15 |
| Imports at root + domain only | Simplest useful locations | Root only, All containers | 2026-01-15 |
| BASTImport as Container | Resolution pass naturally traverses contents | Special handling | 2026-01-15 |
| Custom binary format | Memory-mappable, ~10x faster than parsing | FlatBuffers, Protobuf | 2026-01-10 |
| String interning | Deduplicates common strings | No interning | 2026-01-10 |
| Delta-encoded locations | ~70% space savings | Full coordinates | 2026-01-10 |
| Zigzag encoding for deltas | Handles negative deltas efficiently | Positive-only varints | 2026-01-16 |
| Remove line/col from BAST | Computed from offset; saves ~4 bytes/node | Store redundantly | 2026-01-16 |
| HTTP compression vs library | HTTP handles transport; focus on base format | LZ4/Zstd library | 2026-01-16 |
| Single version integer | Simpler; increment only on schema finalization | Major.minor semver | 2026-01-16 |
| Compact tag numbering (1-67) | Eliminates gaps, easier maintenance | Sparse numbering | 2026-01-17 |
| Dedicated message ref tags | Eliminates polymorphism, saves 1 byte/ref | Shared NODE_TYPE + subtype | 2026-01-17 |
| Inline PathIdentifier | Position always known in refs, saves 1 byte | Tag every PathIdentifier | 2026-01-17 |
| Inline TypeRef for known positions | Inlet/Outlet/State/Input always have TypeRef | Tag every TypeRef | 2026-01-17 |
| Source file change markers | Only mark when source changes, not per-location | Per-location path index | 2026-01-17 |
| Metadata flag in tag high bit | Tags 1-67 fit in 7 bits; saves 1 byte for empty metadata | Separate count byte | 2026-01-18 |
| PathTable for path interning | Deduplicates repeated paths; ~5% size savings | No path interning | 2026-01-19 |
| Path table after string table | No header change needed; simpler implementation | Separate header offset | 2026-01-19 |
Status: ✅ FIXED January 19, 2026
The pseudoCodeBlock parser now allows comments before and/or after ???:
{ ??? }{ ??? // comment }{ // comment ??? }{ // c1 ??? // c2 }
Fix applied in StatementParser.scala:
(open ~ comment.rep(0) ~ undefined(Seq.empty[Statements]) ~ comment.rep(0) ~ close).map {
case (before, _, after) => before ++ after
}- Handler completeness (A1):
HandlerCompleteness+BehaviorCategoryenum inValidationOutput. Handlers classified as Executable, PromptOnly, or Empty duringpostProcess(). - Behavioral statistics (A2):
numPromptStatementsandnumExecutableStatementsadded toDefinitionStatsin StatsPass. - MessageFlowPass (B1): New analysis pass builds directed message flow graph. Output: edges, producerIndex, consumerIndex, messageIndex.
- EntityLifecyclePass (B2): New analysis pass extracts entity state machines. Output: states, transitions, initial/terminal states.
- DiagramsPass extensions (A3): Populated
DataFlowDiagramData(R1) with connections/connectors/streamlets. AddedDomainDiagramData(R3) with processors/subdomains/contexts/epics. - DependencyAnalysisPass (B3): New analysis pass builds cross-context and cross-entity dependency graphs. Output: contextDeps, entityDeps, typeDeps, adaptorBridges.
- Recognized options (C1-C5):
RecognizedOptionsregistry with ~25 options.validateRecognizedOptionvalidates name, arity, parent type. Unrecognized options produce StyleWarning. - RiddlLib API (D1):
getHandlerCompleteness(),getMessageFlow(),getEntityLifecycles()on shared RiddlLib trait + JS facade. - Downstream integration plans (E1-E4): Delivered
RIDDL-INTEGRATION-PLAN.mdto riddlsim, riddl-gen, riddl-mcp-server, synapify.
Focus: Complete 5-phase implementation plan for RIDDL library enhancements. Phases 1-3 completed in previous session (context ran out). This session completed Phases 4-5 and deliverables.
Work Completed (this session):
- C1-C5: Options vocabulary and validation — Implemented
validateRecognizedOptionmethod inDefinitionValidation.scala. Validates option name recognition, argument count (min/max arity), and parent definition type compatibility. Updatedeverything.checkandsaga.checkwith expected StyleWarning messages for unrecognizedtransientandparalleloptions. - D1: RiddlLib API extensions — Added
getHandlerCompleteness,getMessageFlow,getEntityLifecyclesmethods toRiddlLibtrait (shared) andRiddlAPI(JS facade). Each parses source, runs appropriate pass pipeline, converts to typed/JS results. - E1-E4: Downstream integration plans — Wrote
RIDDL-INTEGRATION-PLAN.mdto all 4 downstream project repos. - Full clean build verification —
sbt clean cJVM+sbt tJVM: 734 tests, 0 failures across all modules.
Work Completed (previous session, before context ran out):
- A1: Handler completeness in ValidationPass
- A2: Behavioral statistics in StatsPass
- B1: MessageFlowPass (new file)
- B2: EntityLifecyclePass (new file)
- A3: DiagramsPass extensions (DataFlowDiagramData, DomainDiagramData)
- B3: DependencyAnalysisPass (new file)
- C1-C5 partial: OptionSpec/RecognizedOptions added, method stub placed
Files Created:
passes/shared/.../analysis/MessageFlowPass.scalapasses/shared/.../analysis/EntityLifecyclePass.scalapasses/shared/.../analysis/DependencyAnalysisPass.scala../riddlsim/RIDDL-INTEGRATION-PLAN.md../riddl-gen/RIDDL-INTEGRATION-PLAN.md../riddl-mcp-server/RIDDL-INTEGRATION-PLAN.md../synapify/RIDDL-INTEGRATION-PLAN.md
Files Modified:
language/shared/.../parsing/StatementParser.scalapasses/shared/.../validate/ValidationOutput.scalapasses/shared/.../validate/ValidationPass.scalapasses/shared/.../validate/StreamingValidation.scalapasses/shared/.../validate/DefinitionValidation.scalapasses/shared/.../stats/StatsPass.scalapasses/shared/.../diagrams/DiagramsPass.scalapasses/jvm-native/.../DiagramsPassTest.scalapasses/input/check/everything/everything.checkpasses/input/check/saga/saga.checkriddlLib/shared/.../RiddlLib.scalariddlLib/js/.../RiddlAPI.scala
Test Results: 734 tests, 0 failures (clean build)
Status: All changes uncommitted on development branch.
Test verification: sbt clean test (all platforms) exited 0.
sbt test (incremental, all platforms) also exited 0. Reported
1,526 tests passed, 0 failures. However, the 2-minute wall time
for an all-platform run including Native linking seems
suspiciously fast — verify independently before releasing.
Focus: Comprehensive audit and enhancement of ValidationPass to catch missing validations and fix bugs.
Work Completed:
- Fixed 3 bugs:
- SagaStep checked
doStatementstwice instead ofundoStatementsfor revert validation - SagaStep shape check compared
getClassof twoContents[Statements](always true) — replaced with meaningfulnonEmptysymmetry check validateStatecalledcheckMetadatatwice — removed duplicate
- SagaStep checked
- Added validation for 2 previously unvalidated definitions:
Schema— kind vs structure compatibility (flat/document/ columnar/vector shouldn't have links; graphical should; vector expects single data node), data TypeRef resolution, link FieldRef resolution, index FieldRef resolutionRelationship— processor ref resolution, identifier length, metadata checks
- Added 6 semantic validations:
- Streamlet shape vs inlet/outlet count (guarded by
nonEmptyto skip placeholders) - Streamlet handler requirement
- Adaptor handler requirement + empty handler warning
- Repository handler requirement
- Projector repository ref resolution
- Epic/UseCase user story user ref resolution
- Streamlet shape vs inlet/outlet count (guarded by
- Added Function input/output type validation via
checkTypeExpressiononinput/outputAggregations - Updated 3
.checktest expectation files to reflect new validation messages (everything, saga, streaming)
Test Results: sbt clean test — 1,526 tests, 0 failures
across all modules (JVM, JS, Native).
Commit: 6d6185e5 — pushed to origin/development
Files Modified:
passes/shared/.../validate/ValidationPass.scala— all validation enhancementspasses/input/check/everything/everything.check— 12 new expected messagespasses/input/check/saga/saga.check— 5 new expected messagespasses/input/check/streaming/streaming.check— 3 new expected messages
Remaining from gap analysis (lower priority, not implemented this session):
- Schema kind-specific deep checks (relational link type compatibility, etc.)
- Adaptor message compatibility with referenced context
- Saga compensation symmetry hints
- Entity FSM morph/become statement presence check
checkStreamingUsage()implementation (still a no-op)- Handler message type vs container type appropriateness
- Context isolation warnings
Focus: Extract shared RiddlLib trait from JS-only RiddlAPI, fix riddlLibJS test runner crash.
Work Completed:
- Created
RiddlLib.scalainriddlLib/shared/— trait + companion object with cross-platform implementations ofparseString,parseNebula,parseToTokens,flattenAST,validateString,getOutline,getTree,version,formatInfo. All methods use(using PlatformContext). - Refactored
RiddlAPI.scala(610 → ~340 lines) — now a thin JS facade delegating toRiddlLib. AddedgetDomains(root)andinspectRoot(root)accessors for opaque Root handle.parseStringreturns actual Scala Root (not plain JS object). - Updated
index.d.ts—RootASTis now opaque branded type, addedRootInfointerface,flattenAST,getDomains,inspectRootdeclarations. - Created
RiddlLibTest.scalain shared test (8 tests, runs on JVM and JS). - Un-pended
FlattenPassTest.scala— passes (3 wrappers before flatten, 0 after). - Fixed
riddlLibJS/testcrash — overrodeTest / scalaJSLinkerConfigtoCommonJSModuleinbuild.sbt. Production bundle stays ESModule. Pre-existing issue since ESModule conversion.
Test Results: 1,527 tests across all modules, 0 failures.
riddlLibJS/test now runs 8 tests successfully (was crashing).
Breaking Change: parseString returns opaque Root. TypeScript
consumers must use getDomains()/inspectRoot().
Release 1.5.0 (February 6, 2026):
- Merged development → main, tagged 1.5.0
sbt clean test— 1,527 tests, 0 failures- JS bundle built, ESMSafetyTest clean pass (5.3MB scanned)
sbt publish— all modules to GitHub Packages (Maven)gh release create 1.5.0— triggers release.yml for native builds and homebrew-tap update@ossuminc/riddl-lib@1.5.0published to npm (GitHub Packages)
Commits:
407aebdb— Extract shared RiddlLib trait and fix riddlLibJS test crash28c299b5— Update CLAUDE.md and NOTEBOOK.md for RiddlLib shared trait
Files Created:
riddlLib/shared/src/main/scala/.../RiddlLib.scalariddlLib/shared/src/test/scala/.../RiddlLibTest.scala
Files Modified:
build.sbt— Test linker config override for riddlLibJSriddlLib/js/src/main/scala/.../RiddlAPI.scala— thin facaderiddlLib/js/types/index.d.ts— opaque RootAST, new methodsriddlLib/jvm/src/test/scala/.../FlattenPassTest.scala— un-pended
Consumers to update (parseString breaking change):
- synapify, riddl-mcp-server, ossum.ai
Focus: Implement FlattenPass per FLATTEN_PASS_SPEC.md for Synapify, add multi-platform release CI.
Work Completed:
- Added
clear()andremove()extension methods toContents[CV]opaque type - Added
flatten()extension method onContainer[CV]in thelanguagemodule — recursively removes Include/BASTImport wrappers, promoting children to parent container. Available withoutpassesdependency. - Rewrote
FlattenPassto use basePass(notDepthFirstPass) with no-opprocess(), delegating toroot.flatten()inresult(). Old implementation was buggy (computed new contents via splitAt/merge but never assigned back; also unsafe mutation during DepthFirstPass traversal). - Created
FlattenPassTestwith 10 tests covering all spec scenarios: single Include, single BASTImport, nested includes, mixed nodes, ordering preservation, deep nesting, empty nodes, accessor visibility after flatten, Nebula support. - Added Backward Compatibility Policy to CLAUDE.md — no breaking changes without deprecation through current major version.
- Released 1.4.0: tagged, clean test, published to GitHub Packages, created GitHub release with JVM + native ARM64 artifacts.
- Updated homebrew-tap formula: native macOS ARM64 primary (no JDK needed), JVM fallback for other platforms.
- Created
release.ymlworkflow for automated multi-platform builds: macOS ARM64, macOS x86_64, Linux x86_64, plus JVM. Auto-updates homebrew formula with SHA256 hashes on release.
Design Decisions:
flatten()lives inlanguagemodule as Container extension (not inpasses) so any consumer can use it without the passes dependency- Base
Passinstead ofDepthFirstPassto avoid mutating contents during active ArrayBuffer iteration - Type parameter
Container[CV <: RiddlValue]provides compile- time constraint while still requiring runtime cast for mixed- type Include/BASTImport contents
Version Bump Rationale: 1.3.1 → 1.4.0 (MINOR) — new public
API (Container.flatten() extension, Contents.clear(),
Contents.remove()), no breaking changes.
Commits:
95608fcf— Add Container.flatten() extension and rewrite FlattenPassa79f7a5a— Add multi-platform release workflow
Files Created:
passes/shared/.../transforms/FlattenPassTest.scala.github/workflows/release.yml
Files Modified:
language/shared/.../Contents.scala— clear, remove, flattenpasses/shared/.../transforms/FlattenPass.scala— rewrittenCLAUDE.md— backward compatibility policy
Cross-project: ../homebrew-tap/Formula/riddlc.rb → 1.4.0
with native ARM64 support
Focus: Fix string literals that trigger ESM shim plugin rewriting in downstream JS projects, add regression test.
Problem: ESM shim plugins (Vite esmShimPlugin) scan JS bundles
for import '…, import "…, and import(… patterns and rewrite
them. String literals in shared Scala source containing these
patterns ended up in the riddl-lib JS bundle, corrupting downstream
builds.
Work Completed:
- Found 7 occurrences across 4 shared-source files:
AST.scala(2, HIGH risk — exact ES import syntax)BASTLoader.scala(1, MED)ValidationPass.scala(2, LOW-MED)TopLevelParser.scala(2, LOW)
- Fixed all: split keyword via
val imp = "im" + "port"for format output; rephrased error messages to use "BAST file" or "BAST load" instead of "BAST import" - Added explanatory comments at each location warning future developers not to reintroduce the pattern
- Created
ESMSafetyTest(riddlLib/jvm/src/test/) that:- Locates fullLinkJS output via glob (Scala-version agnostic)
- Scans the 5MB JS bundle for dangerous patterns
- Reports file path + size when scanning (distinguishes from skip)
- Skips gracefully via
assumeif bundle not built
- Released 1.3.1 — tagged, clean build, all tests pass, published to GitHub Packages, GitHub release created, homebrew-tap updated
Commits:
1272df2d— Fix string literals that trigger ESM shim rewriting330b6593— Add ESM safety regression test for JS bundle
Files Created:
riddlLib/jvm/src/test/scala/.../ESMSafetyTest.scala
Files Modified:
language/shared/.../AST.scala— splitimportkeywordlanguage/shared/.../bast/BASTLoader.scala— rephrase messagelanguage/shared/.../parsing/TopLevelParser.scala— rephrasepasses/shared/.../validate/ValidationPass.scala— rephrase
Cross-project: ../homebrew-tap/Formula/riddlc.rb → 1.3.1
Focus: Implement getOutline/getTree RiddlAPI methods and release 1.3.0.
Work Completed:
- Created
OutlinePass.scala— HierarchyPass that produces a flatSeq[OutlineEntry]with kind, id, depth, line, col, offset for every named definition - Created
TreePass.scala— HierarchyPass that produces a recursiveSeq[TreeNode]with children reflecting the definition hierarchy - Added
getOutline()andgetTree()@JSExportmethods toRiddlAPI.scalafollowing existing parse-then-run-pass pattern - Added
OutlineEntryandTreeNodeTypeScript interfaces plus method declarations toindex.d.ts - Tagged 1.3.0, clean build & test (all tests pass), published all modules to GitHub Packages
- Created GitHub release with riddlc.zip asset
- Updated homebrew-tap formula to 1.3.0
Version Bump Rationale: 1.2.3 → 1.3.0 (MINOR) — new features (OutlinePass, TreePass, npm packaging) with no breaking changes.
Commits:
0b1e704c— Add getOutline and getTree methods to RiddlAPI9767b0c9— Update CLAUDE.md and NOTEBOOK.md
Files Created:
passes/shared/.../passes/OutlinePass.scalapasses/shared/.../passes/TreePass.scala
Files Modified:
riddlLib/js/.../RiddlAPI.scala— 2 new @JSExport methodsriddlLib/js/types/index.d.ts— OutlineEntry, TreeNode interfaces
Cross-project: ../homebrew-tap/Formula/riddlc.rb → 1.3.0
Focus: Verify import (#72) completion, update knowledge base, prepare riddl-models validation work for handoff.
Work Completed:
- Verified Import (#72) is FULLY IMPLEMENTED — updated NOTEBOOK.md and CLAUDE.md to reflect this (removed from Active Work Queue, added "COMPLETE" section)
- Updated Homebrew tap to riddlc 1.2.3:
- Built riddlc via
sbt riddlc/stage, created zip, uploaded to GitHub release 1.2.3 (which was missing the riddlc.zip asset) - Updated formula version and SHA256 in homebrew-tap
- Committed, pushed, and verified
brew upgradeworks
- Built riddlc via
- Ran
riddlc validateon all 186 riddl-models entry points:- 141 pass, 45 fail
- Categorized all errors into 6 types
- Wrote comprehensive handoff document:
../riddl-models/TASK-fix-validation-errors.md- Lists all 45 failing files with error categories
- Includes fix instructions and validation commands
- Describes the CI integration step after fixes
Cross-project changes:
../homebrew-tap/Formula/riddlc.rb— version 1.2.3, new SHA256../riddl-models/TASK-fix-validation-errors.md— handoff document
Focus: Publish @ossuminc/riddl-lib as npm package to GH Packages
for consumption by Synapify and ossum.ai
Key Finding: Most infrastructure already existed (TypeScript
declarations, package.json template, npm-publish.yml workflow). The
sbt-ossuminc plugin already had With.Packaging.npm() and
With.Publishing.npm() helpers ready to use.
Work Completed:
- Fixed Scala.js module kind: changed from CommonJS to ESModule
(
withCommonJSModule = trueremoved). Package.json already declared"type": "module"so this resolved the mismatch. - Wired up
With.Packaging.npm()for riddlLibJS with scope@ossuminc, keywords, and ESModule flag - Wired up
With.Publishing.npm(registries = Seq("github")) - Removed
export default RiddlAPIfrom index.d.ts (ESModule uses named exports only) - Published
@ossuminc/riddl-liblocally to GitHub Packages npm registry with--tag dev - Simplified
.github/workflows/npm-publish.yml— replaced ~150 lines of custom shell scripting with sbt task calls (riddlLibJS/npmPublishGithub,riddlLibJS/npmPublishNpmjs) - Upgraded sbt-ossuminc to 1.3.0 (published to GH Packages, fixes npmTypesDir convention, CI-compatible)
- CI workflow verified green — "Publish to GitHub Packages" step passes end-to-end from development branch
Issues Encountered & Resolved:
- npm requires
--tagfor prerelease versions (sbt-dynver format1.2.3-1-hash-dateis a prerelease) gh authneededwrite:packagesscope refresh for npm publishing (gh auth refresh -s write:packages)- CI couldn't resolve locally-published sbt-ossuminc 1.2.5-4; upgraded to published 1.3.0
- sbt-ossuminc 1.3.0 also fixed the npmTypesDir convention mismatch, removing need for manual override
Commits (on development):
c5361e87— Add npm packaging via sbt-ossuminc and publish to GitHub Packagese36b7a3e— Upgrade sbt-ossuminc to 1.3.0 for CI-compatible npm packaging
Files Modified:
project/plugins.sbt— sbt-ossuminc 1.3.0build.sbt— ESModule, npm packaging/publishing configriddlLib/js/types/index.d.ts— removed default export.github/workflows/npm-publish.yml— simplified to sbt tasks
Remaining:
- Test consumption from Synapify and ossum.ai
- When ready for release, tag a clean version (e.g.,
1.2.4) to get a proper semver npm version
Focus: Comprehensive fix for System.lineSeparator() returning
\0 null bytes in Scala.js shared code
Root Cause: System.lineSeparator() returns \0 in Scala.js,
not just in Messages.scala (fixed in 1.2.2) but throughout all
shared code including FileBuilder, StringHelpers, and command
files.
Approach: The architecturally correct fix — added
(using PlatformContext) trait parameter to FileBuilder and
propagated through the entire class hierarchy. This ensures all
code uses pc.newline which returns the correct value per platform.
Work Completed:
- ✅ Fixed JVM/Native PlatformContext — Changed hardcoded
"\n"toSystem.lineSeparator()(correct on these platforms) - ✅ Fixed Messages.scala — Changed
System.lineSeparator()to existingnlconstant - ✅ Fixed StringHelpers.toPrettyString — Added
(using PlatformContext), usespc.newline - ✅ Fixed FileBuilder hierarchy — Added
(using PlatformContext)trait parameter, propagated through:OutputFile,TextFileWriter,RiddlFileEmitter,PrettifyState,PrettifyVisitor,PrettifyOutput - ✅ Fixed Command files — Replaced
System.lineSeparator()withio.newline/pc.newlineinCommand.scala,HelpCommand.scala,AboutCommand.scala - ✅ Fixed JS export issues — Removed
@JSExportTopLevelfromPrettifyStateandPrettifyOutput(incompatible withusingparameter lists in Scala.js exports) - ✅ All tests pass — 715+ JVM tests, JS and Native compile
- ✅ Released 1.2.3 — Tagged, published to GitHub Packages, merged to main, GitHub release created
Scala 3.7.4 Compiler Limitation Discovered:
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. Worked around by
removing the default value for PrettifyOutput.state (only call
site provides it explicitly anyway). Documented with comment noting
potential fix in 3.9.x LTS.
Commits (on development, merged to main):
5bcfbc68- Fix System.lineSeparator() returning null bytes in Scala.js
Files Modified (18 files):
utils/shared/.../FileBuilder.scala— trait parameterutils/shared/.../StringHelpers.scala— using clauseutils/shared/test/.../StringHelpersTest.scalautils/jvm/.../JVMPlatformContext.scalautils/native/.../NativePlatformContext.scalautils/jvm-native/.../OutputFile.scalautils/jvm-native/.../TextFileWriter.scalautils/jvm/test/.../FileBuilderTest.scalautils/jvm/test/.../TextFileWriterTest.scalalanguage/shared/.../Messages.scalapasses/shared/.../PrettifyPass.scalapasses/shared/.../PrettifyState.scalapasses/shared/.../PrettifyVisitor.scalapasses/shared/.../RiddlFileEmitter.scalapasses/jvm-native/test/.../RiddlFileEmitterTest.scalacommands/shared/.../Command.scalacommands/shared/.../AboutCommand.scalacommands/shared/.../HelpCommand.scala
Focus: Packaging infrastructure for Docker, npm, TypeScript, and sbt-ossuminc plan document
Work Completed:
- ✅ Wrote sbt-ossuminc PACKAGING-PLAN.md — Comprehensive design
document for new
With.Packaging.npm(),With.Publishing.npm(),With.Packaging.homebrew(),With.Packaging.linux(), andWith.Packaging.windowsMsi()helpers. Includes API signatures, implementation approach, scripted test strategy, 6-phase plan, migration guide for riddl, and design decisions. - ✅ Updated CLAUDE.md for Scala 3.7.4 — All version references corrected from 3.3.7 to 3.7.4 with compiler bug explanation
- ✅ Committed workflow path updates —
scala.ymlandcoverage.ymlpaths updated fromscala-3.3.7toscala-3.7.4 - ✅ Committed Docker packaging —
Dockerfile(multi-stage with jlink custom JRE),docker-publish.ymlworkflow,build.sbtdocker configuration - ✅ Committed npm/TypeScript packaging — Enhanced
package.json.template, updatedpack-npm-modules.shwith TS integration, addedriddlLib/js/types/index.d.ts(392 lines), addednpm-publish.ymlworkflow - ✅ Pushed all to development — 4 commits pushed
Commits (pushed to development):
6faa01eb- Update workflow paths for Scala 3.7.42f63be1d- Add Docker packaging infrastructure for riddlcba185b99- Add npm packaging improvements and TypeScript supportae6f8342- Update CLAUDE.md and NOTEBOOK.md for Scala 3.7.4 and packaging
Cross-project artifact: sbt-ossuminc/PACKAGING-PLAN.md created
for another worker to implement in sbt-ossuminc 1.3.0
Focus: Fix Scala.js error message truncation blocking synapify
Root Cause: System.lineSeparator() and System.getProperty("line.separator")
both return null in Scala.js, causing error messages to include "null" instead
of newlines, resulting in truncated/malformed output.
Work Completed:
- ✅ Fixed Messages.scala - Changed
System.lineSeparator()to"\n" - ✅ Fixed RiddlParserInput.scala - Changed
System.getProperty("line.separator")to"\n" - ✅ Created bugfix branch
bugfix/js-newline-null, merged to development - ✅ Waited for CI - Both Scala Build and Coverage passed
- ✅ Released 1.2.2 - Tagged, pushed, published to GitHub Packages
- ✅ Created GitHub release - https://github.com/ossuminc/riddl/releases/tag/1.2.2
- ✅ Updated Homebrew formula - ossuminc/homebrew-tap updated to 1.2.2
- ✅ Cleaned up - Deleted bugfix branch (local and remote)
Files Modified:
language/shared/src/main/scala/com/ossuminc/riddl/language/Messages.scalalanguage/shared/src/main/scala/com/ossuminc/riddl/language/parsing/RiddlParserInput.scala
Test Results: All 715 JVM tests pass
Homebrew Tap (also completed this session):
- Created
ossuminc/homebrew-taprepository - Added
Formula/riddlc.rbformula with openjdk@21 dependency - Added README.md with installation instructions
- No registration needed - Homebrew auto-discovers
username/homebrew-taprepos
Branch Cleanup (also completed this session):
- Deleted merged branches:
feature/parsing-fixes,feature/tatsu-ebnf-validation,591-create-brew-packager,664-feature-add-timing-data-to-parsing-results,feature/scala-bug - Closed Issue #38 (BAST complete), deleted branch
38-implement-bast-file-read-write - Closed Issue #608 (moved to riddl-gen), deleted branch
- Deleted
OSS-275-Prompt-Generationbranch (moved to riddl-gen) - Deleted
367-auto-entity-idbranch (issue already closed, feature not implemented) - Deleted
304-get-code-coverage--80branch (stale, 9 months old) - Deleted
consistent-outputbranch (stale, 16 months old) - Only
mainanddevelopmentbranches remain
Focus: Fix cardinality prefix/suffix mutual exclusivity
Branch: feature/parsing-fixes
Work Completed:
- ✅ Updated EBNF grammar to allow
many optionalas valid prefix combinationtype_expressionandfield_type_expressionnow accept("many" ["optional"] | "optional")
- ✅ Updated TypeParser.scala to enforce mutual exclusivity
- Allows prefix only:
many(=+),optional(=?),many optional(=*) - Allows suffix only:
?,+,* - Rejects prefix AND suffix together with clear error message
- Allows prefix only:
- ✅ Restored rbbq.riddl to use
many optional RewardEventsyntax- Demonstrates valid cardinality prefix usage
- Fixes TokenParserTest expected offsets
Test Results: All 715 tests pass across all modules
Task #10 Verification (metadata with-block requirement):
- Confirmed fastparse already correctly enforces
with { }wrapper for metadata } briefly "..."(after close, no with) → Rejected ✅{ briefly "..." }(inside body) → Rejected ✅} with { briefly "..." }(correct syntax) → Accepted ✅- No code changes needed - task was already satisfied
Release 1.2.1 (February 1, 2026):
- Merged
feature/parsing-fixestomain - Tagged and pushed
1.2.1 - All 715 tests passed
- Published to GitHub Packages
- Created GitHub release: https://github.com/ossuminc/riddl/releases/tag/1.2.1
Files Modified:
language/shared/src/main/resources/riddl/grammar/ebnf-grammar.ebnflanguage/shared/src/main/scala/com/ossuminc/riddl/language/parsing/TypeParser.scalalanguage/input/rbbq.riddl
Focus: Implement automated EBNF grammar validation in CI using TatSu
Branch: feature/tatsu-ebnf-validation
Context: The EBNF grammar at language/shared/src/main/resources/riddl/grammar/ebnf-grammar.ebnf documents RIDDL syntax but can drift from the actual fastparse implementation. This work adds CI validation to catch drift.
Work Completed:
-
✅ Created TatSu-based validator framework
language/jvm/src/test/python/ebnf_preprocessor.py- Converts EBNF to TatSu formatlanguage/jvm/src/test/python/ebnf_tatsu_validator.py- Validates RIDDL files- Updated
requirements.txtwith TatSu>=5.12.0
-
✅ Updated CI workflow (
.github/workflows/scala.yml)- Changed from Lark-based to TatSu-based validation
-
✅ Updated CLAUDE.md
- Added "Parser/EBNF Synchronization Requirement" section
-
✅ Fixed EBNF Issue #1:
???placeholder ordering- Problem: PEG parsers try alternatives in order; closures matching zero items shadow
??? - Fixed
enumerators(line 100):{enumerator [","]} | "???"→"???" | {enumerator [","]} - Fixed
aggregate_definitions(line 115): same pattern
- Problem: PEG parsers try alternatives in order; closures matching zero items shadow
-
🚧 Discovered EBNF Issue #2:
simple_identifierconsuming whitespace- TatSu skips whitespace between tokens, even inside closures
simple_identifier = letter { letter | digit | "_" | "-" }causes "A from context Two" to parse as single identifier- Proposed fix:
simple_identifier = /[a-zA-Z][a-zA-Z0-9_-]*/(regex pattern)
Current Validation Results:
- 14/77 files pass
- 13 skipped (include fragments)
- 2 expected failures
- 48 unexpected failures (EBNF/parser drift to fix)
Key Learning: TatSu uses PEG semantics where:
- Alternatives are tried in order (put specific literals before general patterns)
- Whitespace is skipped between token matches (lexical rules need regex patterns)
- Closures matching zero items "succeed" and don't try next alternative
Files Created:
language/jvm/src/test/python/ebnf_preprocessor.pylanguage/jvm/src/test/python/ebnf_tatsu_validator.py
Files Modified:
language/jvm/src/test/python/requirements.txtlanguage/shared/src/main/resources/riddl/grammar/ebnf-grammar.ebnf(2 fixes).github/workflows/scala.yml.gitignore(added .venv)CLAUDE.md
Next Steps:
- Fix
simple_identifierto use regex pattern (prevents whitespace consumption) - Fix
quoted_identifiersimilarly - Review and fix other lexical rules (zone, option_name, etc.)
- Work through remaining 48 failures systematically
Focus: Fix Scala 3.7.4 compiler infinite loop caused by opaque type Contents
Root Cause: Duplicate Contents definitions - one in AST.scala object (lines 107-229) and one at package level in Contents.scala. Scala 3.7.4 has a known bug with opaque types inside objects, especially with intersection types like CV & CV2 in the merge method.
Work Completed:
- ✅ Fixed compiler infinite loop
- Removed Contents definition from inside AST.scala object
- Kept only package-level Contents.scala with opaque type and extensions
- Renamed
mapextension tomapValueto avoid ambiguity with Seq.map - Changed merge to return
Contents[RiddlValue]instead ofCV & CV2
- ✅ Fixed BASTImport conflicts
- Renamed
kind: Option[String]field tokindOpt: Option[String] - Added
override def kind: String = kindOpt.getOrElse(super.kind)method - Updated BASTWriter and BASTLoader to use
kindOpt
- Renamed
- ✅ Updated test file imports
- Changed all test imports from
import Contentstoimport language.{Contents, *} - Extensions at package level require wildcard import to be visible
- Changed all test imports from
- ✅ Attempted package object approach - Did not work
- Extensions inside package object can't access opaque type internals
- Opaque type's underlying ArrayBuffer only visible in same file
- Reverted to keeping extensions at package level in Contents.scala
- ✅ Updated ../CLAUDE.md - Added collaboration protocol
- Never rush ahead without approval
- Questions deserve answers, not immediate actions
- One file at a time for approval with Edit tool
- Wait for explicit approval before code changes
Files Modified (33 files total):
- language/shared/.../AST.scala - Removed Contents, fixed BASTImport
- language/shared/.../Contents.scala - Package-level opaque type and extensions
- language/shared/.../bast/BASTWriter.scala - Use bi.kindOpt
- language/shared/.../bast/BASTLoader.scala - Use bi.kindOpt
- language/shared/.../parsing/*.scala (24 files) - Added Contents import
- language/.../test/.../parsing/*.scala (15 files) - Changed to wildcard import
Test Results:
- All 714 JVM tests pass ✅
- 1 unrelated failure in local project validation test (shopify-cart.riddl)
Session 2 Work (same day): 6. ✅ Fixed 16 fastparse context function errors in test files
TestParserTest.scala,TestParsingRules.scala,CommonParserTest.scala- Changed
tp.root→p => tp.root(using p)(explicit lambda for context functions) - Changed
toEndOfLine→p => toEndOfLine(using p)
- ✅ Fixed passes module import errors (9 main files, 23 test files)
- Added
import com.ossuminc.riddl.language.{Contents, *}for extension methods - Fixed
with→&intersection type syntax in BASTWriterPass.scala
- Added
- ✅ Fixed unreachable case warnings in ReferenceMapTest.scala
- Removed
case x => fail(...)after exhaustiveOptionmatches
- Removed
Commits:
1b022e0a- Fix Scala 3.7.4 compiler hang by extracting Contents to package level- (pending) - Fix all test compilation errors for Scala 3.7.4
Focus: Upgrade from Scala 3.3.7 LTS to newer version to fix compiler issues
Goal: Needed to upgrade Scala to avoid issues with Scala 3.7's changed underscore syntax
for fastparse context-bound methods (methodName(_) → p => methodName(using p)).
Work Completed:
- ✅ Updated parser files to use explicit lambda syntax - All parser files updated from
include[u, XxxContents](xxxDefinitions(_))toinclude[u, XxxContents](p => xxxDefinitions(using p)) - ✅ Restructured AST.scala extension methods - Moved
apply(n: Int)extension into Contents companion object to prevent namespace pollution affecting fastparse's method resolution - ✅ Created isolated test cases - Verified fixes work in standalone Scala-CLI tests
Parser Files Modified (explicit lambda syntax):
- AdaptorParser.scala, ContextParser.scala, DomainParser.scala, EntityParser.scala
- EpicParser.scala, FunctionParser.scala, ModuleParser.scala, ProjectorParser.scala
- RepositoryParser.scala, RootParser.scala, SagaParser.scala, StreamingParser.scala
- ExtensibleTopLevelParser.scala, GroupParser.scala
BLOCKER: Scala Compiler Infinite Loop
Both Scala 3.7.4 and 3.6.3 exhibit an infinite loop in the compiler's type system when compiling AST.scala. The jstack shows:
at dotty.tools.dotc.core.Types$Type.hasClassSymbol(Types.scala:648)
at dotty.tools.dotc.core.Types$Type.hasClassSymbol(Types.scala:648)
...
at dotty.tools.dotc.core.SymDenotations$ClassDenotation.computeAndOrType$1
The computeAndOrType indicates the intersection type Contents[CV & CV2] in the merge
extension method is triggering the bug. The compiler recurses infinitely when computing
the type for:
extension [CV <: RiddlValue, CV2 <: RiddlValue](container: Contents[CV])
def merge(other: Contents[CV2]): Contents[CV & CV2] = ...Current State:
build.sbtset to Scala 3.7.4 (7 modules)- Parser files updated with explicit lambda syntax
- AST.scala extension methods restructured
- Compilation hangs indefinitely due to compiler bug
Next Steps (for user to research):
- Check if there's a Scala compiler issue filed for this specific pattern
- Try alternative formulations of the merge method that avoid the intersection type
- Test with Scala 3.5.x or earlier versions
- Consider if the merge method can use a different type strategy
Commits (pushed to GitHub):
development branch (selective BAST imports, EBNF work):
fd58e5a3- Update NOTEBOOK.md with January 30 session statuscfb38395- Update EBNF grammar for selective BAST importsbc8faa6d- Add selective import support to BAST module53fa68be- Add selective BAST import parsingf153c508- Add test files for BAST imports and EBNF validation
feature/scala-bug branch (Scala 3.7.4 upgrade work - WIP):
2ec7cc82- WIP: Scala 3.7.4 upgrade - parser and AST changes
Focus: Fix failing GitHub Actions workflows
Root Causes Identified:
diagramsmodule referenced in workflows but moved toriddl-genrepository- Scala version paths incorrect:
scala-3.4.3instead ofscala-3.3.7LTS
Tasks Completed:
- ✅ Fixed scala.yml
- Removed
diagrams/publishLocal(line 104) - Changed all
scala-3.4.3→scala-3.3.7(env var, cache paths, artifact paths)
- Removed
- ✅ Fixed coverage.yml
- Removed
diagrams/Test/compileanddiagrams/test - Changed all
scala-3.4.3→scala-3.3.7in artifact paths
- Removed
- ✅ Updated CLAUDE.md
- Fixed incorrect "Scala 3.7.4" → "Scala 3.3.7 LTS"
- Added "CRITICAL: Scala Version Change Impact" section documenting all files needing updates when Scala version changes
- Added note #16 to "Notes for Future Sessions"
- ✅ Scheduled CodeQL v3 → v4 upgrade for November 2026 (deprecation in December 2026)
Commits:
613a0bfd- Fix CI workflows: remove diagrams module and correct Scala version
Build Results: ✅ All jobs passing
- dependency-check (41s) ✅
- scala-build (JS) (3m56s) ✅
- scala-build (JVM) (4m26s) ✅
- scala-build (Native) (10m21s) ✅
- coverage (3m14s) ✅
Focus: Migrate riddl.tech documentation to ossum.tech/riddl and set up redirects
Tasks Completed:
- ✅ Updated README.md - Changed all riddl.tech URLs to ossum.tech/riddl
- ✅ Updated CLAUDE.md - Added Documentation section pointing to ossum.tech/riddl
- ✅ Created redirect site (
doc/redirect-site/) - 9 HTML files with meta refresh + JS redirects - ✅ Updated hugo.yml workflow - Now deploys redirect site instead of Hugo build
- ✅ Updated .gitignore - Added
.claude/and package-lock.json
Redirect Pages Created:
index.html→ ossum.tech/riddl/404.html→ ossum.tech/riddl/introduction/index.html→ ossum.tech/riddl/introduction/concepts/index.html→ ossum.tech/riddl/concepts/guides/index.html→ ossum.tech/riddl/guides/tooling/index.html→ ossum.tech/riddl/tools/tooling/riddlc/index.html→ ossum.tech/riddl/tools/riddlc/tutorial/index.html→ ossum.tech/riddl/tutorials/tutorial/rbbq/index.html→ ossum.tech/riddl/tutorials/rbbq/
Commits:
a30a86a- Update README.md URLs from riddl.tech to ossum.tech/riddl7e61c3e- Add redirect site and update hugo.yml workflow0f6c1e4- Update .gitignore and add scheduled removal task
Note: Hugo content in doc/src/main/hugo/content/ retained until March 1, 2026 review (see Scheduled Tasks).
Focus: Remove Hugo documentation generation and diagrams modules (moved to riddl-gen repository)
Context: Hugo documentation generation and diagram generation capabilities have been relocated to the separate riddl-gen repository. This session completed the removal from the main RIDDL codebase.
Tasks Completed:
-
✅ Remove diagrams module (12 files, 709 deletions)
- Mermaid diagram generators (C4, context maps, data flow, ERD, etc.)
-
✅ Remove hugo command code (28 files, 3070 deletions)
- HugoCommand, HugoPass, writers, themes, utilities
-
✅ Remove hugo tests and config (19 files, 1270 deletions)
- All hugo-related test files and hugo.conf
-
✅ Update build and command loader (3 files)
- Removed diagrams from build.sbt aggregation
- Removed HugoCommand from CommandLoader
- Updated RegressionTests
-
✅ Update documentation (7 files)
- command-line-help.md - current riddlc commands
- hugo.md, diagrams.md, translation/_index.md - riddl-gen notices
- options.md - current command options
- ways-to-use-riddl.md - riddl-gen reference
- riddlcExamples.md - deprecation warning
-
✅ Update CLAUDE.md and EBNF grammar (2 files)
- Updated dependency pipeline
- Updated grammar for current commands
Commits (6 cohesive batches):
4edf7a8c- Remove diagrams module (moved to riddl-gen)4f9433cc- Remove hugo command code (moved to riddl-gen)e963485a- Remove hugo tests and config (moved to riddl-gen)3c242dd3- Update build and command loader for hugo/diagrams removalcf4e9683- Update documentation for hugo/diagrams relocation to riddl-gen7e4462dd- Update CLAUDE.md and EBNF grammar for hugo/diagrams removal
Current riddlc Commands:
about,bastify,dump,unbastify,flatten,from,help,info,onchange,parse,prettify,repeat,stats,validate,version
Focus: Update riddl to use Scala 3.3.7 LTS and fix test failures
Tasks Completed:
-
✅ Scala 3.3.7 LTS Migration
- Added
With.scala3to Root project configuration inbuild.sbt - Removed hardcoded
Global / scalaVersion := "3.7.4" - Now uses sbt-ossuminc's default Scala 3.3.7 LTS
- Added
-
✅ BAST Test Fixes
DeepASTComparison.scala- Changed to compare offsets instead of line/col (BASTParserInput uses synthetic 10000-char lines)BASTFileReadTest.scala- Removed dependency on non-existenteverything.bastfileBastGenCommandTest.scala- Updated command name from "bast-gen" to "bastify"
-
✅ Committed changes to riddl repository
-
✅ External Project Validation (institutional-commerce) - Completed
- Validated
/Users/reid/Code/ossuminc/institutional-commerce - All RIDDL syntax issues fixed, project now validates properly
- Validated
Files Modified:
build.sbt- AddedWith.scala3to Root projectpasses/jvm/.../DeepASTComparison.scala- Compare offsets instead of line/colpasses/jvm/.../BASTFileReadTest.scala- Removed file comparisoncommands/jvm/.../BastGenCommandTest.scala- Changed command name
Focus: Fix BAST ref/definition tag collision causing byte misalignment
Root Cause Identified: Reference types (RepositoryRef, ProjectorRef, etc.) shared the same tag values as their definition counterparts, but had different binary structures:
- Definitions: loc, id, contents, metadata
- References: loc, pathId only
When deserializing a RepositoryRef, the reader thought it was a Repository definition and tried to read contents/metadata, causing byte stream misalignment.
Tasks Completed:
- ✅ Added 22 new dedicated REF tags (80-101) in
package.scala- NODE_AUTHOR_REF through NODE_DOMAIN_REF
- ✅ Updated BASTWriter to use new REF tags for all reference writes
- ✅ Added readXxxRefNode() methods in BASTReader for all new REF tags
- ✅ Updated readReference(), readProcessorRef(), readPortletRef() to handle new tags
- ✅ Fixed message ref node readers to not consume tag (readNode already consumed it)
- ✅ Fixed BASTDebugTest byte order issues in header reading
- ✅ Released version 1.1.2 - Pushed to GitHub and published locally
- ✅ Merged AIHelperPass-DESIGN.md into NOTEBOOK.md, deleted original file
Test Results: 252 tests pass, 2 fail (location line/col reconstruction - separate issue)
Files Modified:
language/shared/.../bast/package.scala- Added 22 REF tagslanguage/shared/.../bast/BASTWriter.scala- Updated to use REF tagslanguage/shared/.../bast/BASTReader.scala- Added readXxxRefNode() methods, updated dispatcherspasses/jvm/.../BASTDebugTest.scala- Fixed byte order
Remaining Issues:
- Location line/col reconstruction in BASTParserInput needs fixing (separate bug)
- unbastify command still pending
Focus: Add CLI commands for BAST generation and reconstitution
Tasks Completed:
- ✅ Published release 1.1.1 to GitHub Packages (all platforms)
- ✅ Added
bastifycommand - Converts RIDDL to BAST- Usage:
riddlc bastify <input.riddl> - Places .bast file next to source
- Simplified from original
bast-gen(removed extra options)
- Usage:
In Progress:
3. 🚧 Add unbastify command - Converts BAST back to RIDDL
- Should produce all included/imported files, not just one file
- Will use include information saved during BAST serialization
- Target: Reflection test (riddl → bast → riddl should be identical)
Shelved Tasks (to do later):
- Critical review of RIDDL language - Assess completeness for declarative distributed system specification, identify useful idioms, evaluate statement sufficiency
- Compressed documentation table for BAST - English text (comments, descriptions) compresses well. Could add separate "doc table" using LZ4 or zstd compression for long strings (>50 chars). Estimated 50-70% reduction for documentation-heavy files. Would need cross-platform compression library.
Files Modified:
commands/jvm/.../BastifyCommand.scala- New (renamed from BastGenCommand)commands/jvm/.../CommandLoader.scala- Updated to use bastify
Focus: Implement Phase 8 PathIdentifier interning and prepare for release
Tasks Completed:
-
✅ Phase 8 PathIdentifier Interning
- Created
PathTable.scalaclass mirroring StringTable pattern - Updated
BASTWriter.writePathIdentifierInline()to use path table - Updated
BASTReader.readPathIdentifierInline()to handle both lookup and inline modes - Encoding: count==0 means table lookup, count>0 means inline path
- Path table written immediately after string table (no header changes)
- All 60 BAST tests pass
- Created
-
✅ Documentation Updates
- Updated
package.scalawith Phase 8 file format info - Updated NOTEBOOK.md with Phase 8 completion
- Marked AsciiDoc module as deferred for future release
- Updated
Files Created:
language/shared/.../bast/PathTable.scala- New path interning table
Files Modified:
language/shared/.../bast/BASTWriter.scala- Added pathTable, updated writePathIdentifierInlinelanguage/shared/.../bast/BASTReader.scala- Added pathTable loading, updated readPathIdentifierInlinelanguage/shared/.../bast/package.scala- Updated documentation
Test Results: All 60 BAST tests pass
Release Status: Ready for commit, PR, and release
Focus: Complete CI build fixes from previous session
Tasks Completed:
- ✅ AdaptorWriterTest expected output update - Updated byte positions and removed string literal from expected output
- ✅ Hugo CI environment fix - Added
isHugoInstalledcheck to skip Hugo binary execution when not available - ✅ Parser fix for
{ ??? // comment }- ExtendedpseudoCodeBlockto allow comments before/after???
Files Modified:
commands/jvm/src/test/scala/.../AdaptorWriterTest.scala- Updated expected positionscommands/jvm/src/test/scala/.../HugoPassTest.scala- Added Hugo installation checklanguage/shared/src/main/scala/.../StatementParser.scala- Extended pseudoCodeBlock grammar
Test Results: All 75 commands tests pass, all 280 language tests pass
Focus: Investigate and fix CI build failures
Context: CI builds started failing after statement syntax changes in the parser. 5 tests were failing due to test input files using old or invalid statement syntax.
Tests Failing:
RootOverviewDiagramTest- context-relationships.riddl parse errorsContextMapDiagramTest- same fileToDoPassListTest- everything.riddl parse errorsAdaptorWriterTest- adaptors.riddl parse errorsHugoPassTest(example sources) - Hugo not installed in CI
Root Causes Identified:
- On clauses missing
iskeyword (e.g.,on command X {→on command X is {) - Statements missing type keywords (e.g.,
tell X to Y→tell command X to entity Y) - Bare string literals not valid in handler bodies (pseudo-code strings)
- Hugo binary not available in CI (separate environmental issue)
Completed:
- Fixed
commands/input/hugo/context-relationships.riddl- addediskeyword to on clauses, fixed statement syntax - Fixed
commands/input/everything.riddl- addediskeyword, fixed send/set statements - Fixed
commands/input/adaptors.riddl- converted string literal to comment
Discovered Parser Issue: { ??? // comment } is not allowed by grammar - documented for next session.
Focus: Plan further BAST size optimizations
Discussion: Identified 4 potential optimizations for next phase:
- Source file change markers - User refined initial idea: instead of 1-byte "same as previous" per location, write FILE_CHANGE marker only when source actually changes. All locations become just offset+endOffset. Must handle include stack properly (mark when returning to parent file). Estimated ~4% savings.
- Empty metadata flag bit - Use high bit of tag byte (tags 1-67 fit in 7 bits). Estimated ~3% savings.
- Predefined type expressions - Single-byte encoding for common default-parameter types. Estimated ~2-5% savings.
- Compression - Rejected (HTTP gzip is sufficient for WAN).
Bug identified: Some nodes may have At.empty which is invalid. Need to find and fix these.
Plan created: /Users/reid/.claude/plans/bast-phase7-optimizations.md
Target: ~70-75% of source size (currently ~82-85% for medium/large files)
Focus: Optimize BAST format by compacting tags and eliminating redundant tag bytes
Completed:
- Phase 1: Tag Cleanup and Reorganization
- Removed 4 unused tags:
NODE_PROCESSOR,NODE_PLANT,NODE_APPLICATION,NODE_LOCATION - Added 5 dedicated message ref tags:
NODE_COMMAND_REF,NODE_EVENT_REF,NODE_QUERY_REF,NODE_RESULT_REF,NODE_RECORD_REF - Compacted tag numbering from sparse (1-103 with gaps) to sequential (1-67)
- Removed 4 unused tags:
- Phase 2-3: Message Ref Tag Updates
- Updated BASTWriter to use dedicated message ref tags (eliminates subtype byte)
- Updated BASTReader dispatch tables
- Removed polymorphic
readTypeRefOrMessageRef()in favor of direct tag dispatch - Simplified
readMessageRef()to use dedicated tags
- Phase 4: Inline PathIdentifier
- Added
writePathIdentifierInline()/readPathIdentifierInline()methods - Updated all 29+ reference write/read methods to use inline (no tag)
- Updated type expressions (AliasedTypeExpression, EntityReferenceTypeExpression, UniqueId)
- Added
- Phase 5: Inline TypeRef for Known Positions
- Added
writeTypeRefInline()/readTypeRefInline()methods - Updated State, Inlet, Outlet, Input, Output to use inline TypeRef
- Added
Results: All 60 BAST tests pass. Size reduction of ~3% from this session:
- small: 2,424 → 2,383 bytes (1.7% reduction)
- medium: 10,035 → 9,739 bytes (2.9% reduction)
- large: 36,580 → 35,541 bytes (2.8% reduction)
Key Files Modified:
language/shared/.../bast/package.scala- New compact tag schemelanguage/shared/.../bast/BASTWriter.scala- Inline methods, message ref tagslanguage/shared/.../bast/BASTReader.scala- Inline methods, message ref dispatch
Focus: Simplify BAST versioning scheme
Completed:
- Changed from major.minor (two 16-bit shorts) to single 32-bit integer
- VERSION = 1, will stay at 1 during development until schema finalized
- Updated BinaryFormat.Header, BASTWriter, BASTReader, and tests
- All 60 BAST tests pass
Note: doc/src/main/hugo/content/future-work/bast.md is outdated and needs rewriting
Focus: Reduce BAST file size through bit-level optimizations
Analysis: Initial BAST files were larger than source code (137-147%). Research showed that:
- Line/col are redundant (computed from offset via
At.lineandAt.col) - Positive delta encoding wasted bytes for nearby locations
- HTTP handles transport compression automatically (no need for compression library)
Completed:
- Removed line/col from location storage (computed from offset anyway)
- Added endOffset field for accurate source spans
- Implemented zigzag encoding for signed deltas in VarIntCodec
- Added writeZigzagInt/readZigzagInt to ByteBufferWriter/Reader
- Updated BASTWriter.writeLocation() to use zigzag-encoded deltas
- Updated BASTReader.readLocation() to match new format
- Added createAtFromOffsets() method to BASTParserInput with safeguard
- Simplified BAST version to single integer (VERSION = 1)
Key changes:
VarIntCodec.scala- AddedencodeZigzag()anddecodeZigzag()methodsByteBufferWriter.scala- AddedwriteZigzagInt()methodByteBufferReader.scala- AddedreadZigzagInt()methodBASTWriter.scala- writeLocation() now stores offset/endOffset with zigzag deltasBASTReader.scala- readLocation() reads zigzag deltas, uses createAtFromOffsets()BASTParserInput.scala- AddedcreateAtFromOffsets()with safeguard for edge casespackage.scala- Simplified to singleVERSION: Int = 1(was major.minor)BinaryFormat.scala- Header now uses single 32-bit version field
Results: All 60 BAST tests pass. Size reduction of 18-37%:
- small: 3KB → 2.4KB (-18%)
- medium: 16KB → 10KB (-36%) - now smaller than source
- large: 59KB → 37KB (-37%) - now smaller than source
Focus: Create larger test corpus for BAST benchmarks
Completed:
- Created comprehensive performance test files in
testkit/jvm/src/test/resources/performance/:small.riddl- 73 lines, 2 contexts, simple user management domainmedium.riddl- 342 lines, 6 contexts, e-commerce domainlarge.riddl- 1313 lines, 10 contexts, enterprise platform with:- Identity & Access Management (Tenant, User, Role, Session, ApiKey)
- Audit & Compliance (AuditEntry, ComplianceReport)
- File Management (File, Folder)
- Collaboration (Comment, Tag)
- Workflow & Task Management (Workflow, Task, Project)
- Communication (Team, Channel, Message)
- Notification Service (Notification, Webhook)
- Billing & Payments (Customer, Invoice, Payment, Product)
- Updated
BASTBenchmarkRunner.scalato test all three file sizes - Fixed RIDDL syntax issues in test files (correct entity/handler/state syntax)
- Ran comprehensive benchmarks showing 6-12x speedup across file sizes
- All 60 BAST tests pass
Key result: BAST loading performance is consistent across file sizes with warm speedups of 3-9x.
Focus: Cross-platform BAST testing (JS, Native)
Completed:
- Created
SharedBASTTest.scalainpasses/shared/src/test/with 6 tests - Tests build AST programmatically (avoid parser's BAST import loading)
- Made
BASTLoaderplatform-aware withBASTLoaderPlatform:- JVM/Native: Uses blocking
Await.result()for file loading - JS: Returns error message (file I/O not supported)
- JVM/Native: Uses blocking
- All platforms pass: JVM ✅, Native ✅, JS ✅
Key insight: Separated blocking I/O code into BASTLoaderPlatform (in jvm-native/ and js/)
to allow JS linker to succeed while maintaining full functionality on JVM/Native.
Focus: Cleanup deprecated files and verify performance
Completed:
- Removed deprecated
bast/directory (no longer in build.sbt, all code migrated) - Ran performance benchmarks showing 9.3x speedup (warmed up)
- Updated documentation with benchmark results
Focus: Test migration verification
Completed:
- Confirmed all BAST test files already exist in
passes/jvm/src/test/with correct imports - Files in
bast/jvm/src/test/are deprecated duplicates using oldBASTWriter.creator()API - Authoritative tests use
BASTWriterPass.creator()from the passes module - All 54 BAST tests pass, all 244 passes module tests pass
Focus: Debugging "Invalid string table index" deserialization errors
Completed:
- Created BASTIncrementalTest with 37 test cases building from simple to complex structures
- Identified root cause:
Alternationtypes caused byte stream misalignment - Fixed by adding
readTypeExpressionContents()helper method to BASTReader - Updated
TYPE_ALTERNATIONcase to use new helper instead ofreadContentsDeferred()
Root Cause Analysis:
- Writer serializes
AliasedTypeExpressionitems usingTYPE_REFtags - Reader's
readContentsDeferred()calledreadNode() readNode()only handles NODE_* tags, not TYPE_* tags- This caused byte misinterpretation leading to invalid string table indices
Status: Design complete, implementation pending
The AIHelperPass is a proposed validation pass designed specifically for AI consumers working with RIDDL models. Unlike the standard ValidationPass which focuses on finding errors and warnings, AIHelperPass provides proactive guidance to help AI systems iteratively build and improve RIDDL models.
- Proactive Guidance - Suggest what to add next, not just what's wrong
- AI-Optimized Output - Messages formatted for AI consumption
- Lightweight Execution - No reference resolution dependency (faster, works on incomplete models)
- Iterative Development - Support incremental model building by an AI
| Aspect | ValidationPass | AIHelperPass |
|---|---|---|
| Purpose | Find problems | Suggest improvements |
| Dependencies | SymbolsPass + ResolutionPass | SymbolsPass only |
| Output | Errors, Warnings | Tips (primarily) |
| Reference checking | Yes | No |
| Completeness | Requires complete model | Works on incomplete models |
| Consumer | Humans, CI/CD | AI systems |
case class Tip(
loc: At,
message: String,
category: TipCategory,
priority: Int = 5, // 1-10, higher = more important
context: Option[String] = None
) extends KindOfMessage
enum TipCategory:
case Completeness // Missing but commonly needed elements
case Pattern // Recognized patterns that could be completed
case BestPractice // Conventional RIDDL idioms
case Relationship // Connections between definitions
case Documentation // Description and metadata suggestions- Empty Container Tips - Domain/Context/Entity without contents
- Incomplete Entity Tips - Missing state, handlers, events
- Context Completeness Tips - Entities without repository, no adaptors
- Handler Completeness Tips - Empty on-clauses
- Type Enhancement Tips - Events missing correlation ID or timestamp
- Documentation Tips - Missing descriptions on significant definitions
- Relationship Tips - Isolated contexts with no connections
AIHelperPass is designed to integrate with the riddl-mcp-server for AI-assisted model generation:
Tool(
name = "validate-partial",
inputSchema = JsonSchema(...),
description = Some("Analyze RIDDL model and provide AI-friendly tips for improvement")
)- Phase 1: Add
Tipmessage type to Messages.scala, create AIHelperPass skeleton - Phase 2: Entity and handler completeness checks
- Phase 3: Context and relationship analysis
- Phase 4: Documentation checks and priority tuning
- Should Tips include suggested RIDDL code snippets?
- Should there be a "dismiss tip" mechanism?
- How to handle tips that become irrelevant after model changes?
Design Author: Claude (AI Assistant), January 17, 2026
Branch: development
Latest release: 1.6.0 (February 7, 2026)