Note
This document serves as the primary guidance for AI Agents working on the Morphir Rust ecosystem.
AI agents working on this project are expected to be well-versed in Morphir concepts, the Morphir IR, and related tools, designs, specifications, and schemas.
- Official Documentation: https://morphir.finos.org - Comprehensive Morphir documentation, concepts, and guides
- LLM Context File: https://morphir.finos.org/llms.txt - AI agent-optimized context about Morphir
- Reference Implementation: finos/morphir-elm - The canonical Elm implementation of Morphir
- GitHub Organization: github.com/finos - Browse other Morphir ecosystem projects:
morphir-elm- Reference implementation and IR specificationmorphir-jvm- JVM/Scala implementationmorphir-dotnet- .NET implementationmorphir-examples- Example Morphir models and use cases- Additional language bindings and tooling
Agents should understand:
- Morphir IR: The intermediate representation structure, versioning, and evolution
- Morphir Concepts: Functional domain modeling, type systems, and distribution patterns
- IR Schemas: JSON schemas, serialization formats, and compatibility requirements
- Tooling Ecosystem: How different Morphir implementations and tools interact
When uncertain about Morphir-specific design decisions, consult the reference implementation and official documentation.
- Language: Rust (Edition 2021)
- Build System: Cargo
- Async Runtime:
tokio
- CLI Framework:
starbase(application structure),clap(command parsing) - TUI:
ratatui(terminal UI widgets),crossterm(cross-platform terminal manipulation),tuirealm(component framework) - Serialization:
serde&serde_json(JSON handling),schemars(JSON Schema generation) - Logging:
tracing(structured logging),tracing-subscriber(log subscribers),tracing-appender(file logging) - Error Handling:
thiserror(library errors),anyhow(application/CLI errors)
- Public APIs: All public types, functions, and modules must have documentation comments (
//!for modules,///for items). - Examples Required: Components providing significant user behavior (such as loaders, visitors, and complex converters/transformers) must include doc tests/examples demonstrating usage.
- TDD & BDD: We prioritize robust testing and reliability.
- Test-Driven Development (TDD): Write tests before implementation to ensure correctness and design quality.
- Behavior-Driven Development (BDD): For complex workflows and behaviors (like IR migration or CLI commands), use
cucumberwith Gherkin feature files (.feature) to describe compliance and behavior in plain English.
- Coverage: Ensure high test coverage for core logic.
- Object Mother Pattern: Use the Object Mother pattern to create well-named factory functions that produce test data for specific scenarios:
- Create functions like
a_simple_package(),a_package_with_dependencies(),an_invalid_ir_definition()that return pre-configured test data - Place these in a dedicated
test_dataormothersmodule within your tests - Makes tests more readable and reduces duplication of complex setup code
- Facilitates testing edge cases and specific scenarios consistently
- Create functions like
- BDD Test Drivers: All BDD/Cucumber tests must have a corresponding TestDriver:
- Create a
TestDriverstruct that encapsulates the system under test and its dependencies - The driver provides domain-specific methods that map to Gherkin steps (e.g.,
when_migrating_ir(),then_output_should_contain()) - Keeps step definitions clean and delegates complex setup/assertions to the driver
- Example:
tests/drivers/migration_driver.rsfor IR migration BDD tests
- Create a
- Whitebox & Blackbox Testing: Use both testing approaches to validate proper encapsulation:
- Blackbox Tests: Test public APIs without knowledge of internal implementation. Validates that the public interface works correctly and is stable. Place these in integration tests or BDD scenarios.
- Whitebox Tests: Test internal implementation details, edge cases, and private functions. Use
#[cfg(test)]modules within source files for unit tests that need access to private items. - Balance: Prefer blackbox tests for API stability; use whitebox tests to ensure correctness of internal logic and edge cases that are hard to trigger through the public API.
- Encapsulation Validation: If internal implementation details leak into blackbox tests, this indicates poor encapsulation that should be refactored.
Reflecting Morphir's functional domain-driven design nature, strictly adhere to:
- Algebraic Data Types (ADTs): Design domain models using ADTs to precisely capture the problem domain:
- Product Types: Use structs to combine multiple values (e.g.,
struct Package { name: PackageName, modules: Vec<Module> }) - Sum Types: Use enums to represent alternatives and variants (e.g.,
enum Value { Literal(Literal), Constructor(FQName), Apply { function: Box<Value>, argument: Box<Value> } }) - Pattern Matching: Leverage exhaustive pattern matching to ensure all cases are handled
- Type-Driven Design: Let the type system guide implementation and catch errors at compile time
- Product Types: Use structs to combine multiple values (e.g.,
- Immutability: Prefer immutable data structures.
- Illegal States Unrepresentable: Design types such that invalid states cannot be constructed (e.g., use Enums/Sum Types over boolean flags + optional fields).
- No Primitive Obsession: Wrap primitives in domain types (e.g.,
PackageNamestruct instead ofString). - Composition: Build complex logic by composing small, pure functions.
- Strong Cohesion & Loose Coupling: Keep related logic together; minimize dependencies between distinct domains.
- Avoid Mega Files: Do not create large, monolithic source files. Split code into focused, cohesive modules.
- Module-per-Directory Pattern: When a module grows beyond ~300-500 lines or contains multiple related types:
- Create a directory with the module name
- Use
mod.rsto expose the public API - Split implementation into separate files by logical grouping (e.g.,
types.rs,parser.rs,visitor.rs)
- File Size Guidelines:
- Single-purpose files: ~200-400 lines is ideal
- Complex implementations: Consider splitting at 500+ lines
- If a file has multiple
implblocks for different concerns, split by concern
- Logical Grouping: Organize files by:
- Domain concepts: Group related types and their implementations together
- Layers: Separate concerns like parsing, transformation, validation, serialization
- Visibility: Private helpers can live in submodules; public API should be clear in
mod.rs
- Example Structure:
src/ ir/ mod.rs // Public API, re-exports types.rs // Core type definitions parser.rs // Parsing logic visitor.rs // Visitor implementations transform/ // Nested module for transformations mod.rs migration.rs optimization.rs
- Commit Upon Task Completion: Create a commit as soon as a logical unit of work is complete (e.g., after implementing a feature, fixing a bug, or completing a refactoring).
- Avoid Large Worktrees: Do not accumulate large amounts of uncommitted changes. Break work into smaller, committable units.
- Atomic Commits: Each commit should represent a single, coherent change that:
- Compiles successfully (if applicable to the change)
- Passes existing tests (or updates tests appropriately)
- Can be understood independently
- Commit Message Quality: Write clear, descriptive commit messages:
- First line: Brief summary (50-72 characters) in imperative mood
- Body: Explain why the change was made, not just what changed
- Reference related issues/beads when applicable
- AI Agent Authorship - CLA Restriction: NEVER add AI agents (e.g., Claude, ChatGPT, etc.) as the author or co-author in git commits. Due to FINOS Contributor License Agreement (CLA) requirements, only human contributors with signed CLAs may be listed as authors or co-authors. Commits must be attributed solely to the human developer directing the work.
- Use Git for History: Rely on git history to track changes and evolution—don't keep commented-out code or version suffixes in filenames.
- No Repository Littering: Do not create temporary output files in the repository root or source directories.
- Gitignored Locations: Direct all temporary outputs (including redirected command output, test artifacts, and working files) to gitignored directories:
- Preferred:
.agents/out/(for agent-generated temporary files) - Alternative:
.morphir/out/(for Morphir-specific outputs)
- Preferred:
- Redirect Output: When running commands that generate output files, explicitly redirect them to the gitignored locations above rather than letting them write to the working directory.
- Clean Working Directory: Keep the git working directory clean to avoid accidental commits of temporary files.
- stdout is Reserved for Output: Never write logs to stdout. stdout is exclusively for actual program output (command results, generated content, data). This ensures:
- CLI output can be piped to other commands
- JSON/structured output remains parseable
- Scripts can capture actual results without log noise
- stderr for Console Logging: All console-level logging (warnings, errors, progress messages) must go to stderr.
- File-Based Logging: Log files should be written to:
- Workspace-local:
.morphir/logs/when inside a morphir workspace (a directory containingmorphir.tomlor.morphir/) - Global fallback:
~/.morphir/logs/when not in a workspace
- Workspace-local:
- Log Levels: Use appropriate log levels:
error: Unrecoverable failureswarn: Recoverable issues or deprecation noticesinfo: High-level progress indicators (default for CLI)debug: Detailed operational informationtrace: Highly verbose debugging output
- Structured Logging: Prefer structured log formats (JSON lines) for file-based logs to enable analysis and aggregation.
- Rotation: File logs should support rotation to prevent unbounded growth.
- Configuration: Log destinations and levels should be configurable via:
- Environment variables:
MORPHIR_LOG_LEVEL,MORPHIR_LOG_DIR - Config file:
[logging]section inmorphir.toml
- Environment variables:
The CLI reference documentation is auto-generated from the Rust CLI source code via clap's usage output.
- Source of Truth:
crates/morphir/src/main.rs- Rust source with clap derive macros - Intermediate:
docs/morphir.usage.kdl- Generated frommorphir usagecommand - Generated Output:
docs/cli/directory - Markdown files generated from the KDL spec - Task:
.mise/tasks/docs/markdown- Orchestrates generation and post-processing
mise run docs:markdown # Regenerate CLI docs
mise run docs:generate # Regenerate all docs (CLI + release notes)The task automatically:
- Builds the morphir binary
- Generates
docs/morphir.usage.kdlfrommorphir usage - Generates markdown with
usage generate markdown - Adds Jekyll front matter for just-the-docs theme
DO NOT add examples directly to generated markdown files or the KDL file - they will be lost on regeneration.
Instead, add a long_about attribute in the Rust source code (crates/morphir/src/main.rs):
#[derive(Clone, Subcommand)]
enum IrAction {
/// Migrate IR between versions
#[command(long_about = "Migrate IR between versions
Converts Morphir IR between Classic (V1-V3) and V4 formats.
**Examples:**
```bash
morphir ir migrate ./morphir-ir.json -o ./morphir-ir-v4.jsonSee the IR Migration Guide for details.")] Migrate { // fields... } }
For detailed real-world examples (like step-by-step walkthroughs), add them to separate guide pages in `docs/` that are NOT auto-generated (e.g., `docs/ir-migrate.md`).
### Clap Attributes for Documentation
- Doc comment (`///`) - Short description shown in command listing
- `#[command(long_about = "...")]` - Extended help with examples (supports markdown)
- `#[command(hide = true)]` - Hide command from default help (experimental commands)
- `#[arg(short, long)]` - Short and long flag names
- `#[arg(required = true)]` - Mark argument as required
### Markdown Console Rendering
The CLI includes `termimad` for rendering markdown to the terminal with proper syntax highlighting. Use `help::print_markdown()` in `crates/morphir/src/help.rs` to render markdown text.
## Release Management
### Overview
morphir-rust uses a comprehensive release management system:
- **CHANGELOG.md**: Follows [keepachangelog](https://keepachangelog.com) format
- **Mise tasks**: `mise run release:*` for release workflow automation
- **Claude skill**: `/release-manager` for AI-assisted release operations
### Changelog Guidelines
- All notable changes must be documented in `CHANGELOG.md`
- Add entries to `[Unreleased]` as changes are made
- Use categories: Added, Changed, Deprecated, Removed, Fixed, Security
- Reference PRs/issues where applicable
### Release Tasks
| Task | Description |
|------|-------------|
| `mise run release:check` | Run all pre-release quality checks |
| `mise run release:version-bump <v>` | Bump workspace version |
| `mise run release:changelog-validate` | Validate CHANGELOG.md format |
| `mise run release:changelog-entry <cat> <msg>` | Add changelog entry |
| `mise run release:pre-release <v>` | Full pre-release workflow |
| `mise run release:tag-create <v> [--push]` | Create release tag |
| `mise run release:post-release <v>` | Post-release validation |
### Release Process Quick Reference
```bash
# 1. Run pre-release checks
mise run release:pre-release 0.2.0
# 2. Bump version and update changelog
mise run release:version-bump 0.2.0
# Edit CHANGELOG.md manually
# 3. Commit and tag
git add Cargo.toml Cargo.lock CHANGELOG.md
git commit -m "chore: prepare release v0.2.0"
mise run release:tag-create 0.2.0 --push
# 4. Monitor CI and publish release on GitHub
Use the /release-manager skill for guided release workflows:
/release-manager prepare <version>- Pre-release preparation/release-manager analyze- Analyze commits for changelog/release-manager retrospective <version>- Post-release review/release-manager whats-new <version>- Generate release summaries
When ending a work session, you MUST complete ALL steps below. Work is NOT complete until git push succeeds.
MANDATORY WORKFLOW:
- File issues for remaining work - Create issues for anything that needs follow-up
- Run quality gates (if code changed) - Tests, linters, builds
- Update issue status - Close finished work, update in-progress items
- PUSH TO REMOTE - This is MANDATORY:
git pull --rebase bd sync git push git status # MUST show "up to date with origin" - Clean up - Clear stashes, prune remote branches
- Verify - All changes committed AND pushed
- Hand off - Provide context for next session
CRITICAL RULES:
- Work is NOT complete until
git pushsucceeds - NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds