Designs AST node structures, implements IR-to-code transformations, applies functional programming patterns, and architects code generation backends for morphir-dotnet. Use when building or extending Morphir IR types, working with Classic/Modern IR conversions, implementing visitor patterns, or structuring code generation pipelines. Triggers include "Morphir architecture", "Morphir design patterns", "AST node", "IR transformation", "morphir-dotnet structure", "code generation backend".
76%
Does it follow best practices?
Impact
100%
1.07x1 of 3 eval scenarios. Add 2 more for a full score.
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./.claude/skills/morphir-architect/SKILL.mdDesigns and reviews morphir-dotnet architecture: AST/IR type structures, functional programming patterns, visitor implementations, and code generation backends. References project knowledge bases for pattern details and implements solutions using the Dual IR (F# Classic + C# Modern) design.
When asked about AST/CST design, type systems, or tree structures:
Example:
// F# Classic IR: Generic Attributes Pattern
type Type<'attributes> =
| Variable of 'attributes * Name
| Reference of 'attributes * FQName * Type<'attributes> list
| Function of 'attributes * Type<'attributes> * Type<'attributes>
// Allows flexible attribute annotation:
type Type<unit> = ... // Untyped AST
type Type<SourceSpan> = ... // With source locations
type Type<TypeInfo> = ... // Fully typed ASTKey Principles:
When designing AST traversals or transformations:
Checklist:
When implementing FP patterns or error handling:
Process:
Identify Pattern Need
↓
Select Appropriate Pattern (monad, functor, etc.)
↓
Choose Language (F# native vs C# encoding)
↓
Implement Using KB Examples
↓
Validate Against Laws (if applicable)
↓
Test with Morphir IR TypesCommon Patterns:
option, C# nullable reference types → Avoid nullsaddressLens >>> cityLensWhen building DSLs or simplifying tree construction:
Example (Proposed Morphir Type Builder):
type TypeBuilder() =
member _.Yield(()) = Type.Unit(())
[<CustomOperation("variable")>]
member _.Variable(_, name) = Type.Variable((), Name.fromString name)
[<CustomOperation("func")>]
member _.Function(_, param, ret) = Type.Function((), param, ret)
let typeExpr = typeBuilder {
func (variable "a") (variable "b")
}
// Produces: Function((), Variable("a"), Variable("b"))When implementing code generation or analysis:
Decision Tree:
Need to generate code?
YES → What language?
├─ F# → Use Myriad (MSBuild-integrated)
├─ C# → Use Source Generators (incremental pipeline)
└─ Both → Separate generators for each
NO → Need to analyze code?
├─ F# code → Use F# Compiler Service
├─ C# code → Use Roslyn
└─ Morphir IR → Direct pattern matching (fastest)Dual IR Design:
src/Morphir.Models/IR/Classic/ - Discriminated unions, functional operationssrc/Morphir.Core/IR/ - Sealed records, C# consumptionKey Areas:
Type<'attributes> for extensible AST annotationsrc/
├── Morphir.Core/IR/ # C# Modern IR (sealed records)
│ ├── Type.cs # Type expressions
│ ├── Value.cs # Value expressions
│ └── Module.cs # Module definitions
├── Morphir.Models/IR/Classic/ # F# Classic IR (discriminated unions)
│ ├── Type.fs # Type<'attributes>
│ ├── Value.fs # Value definitions
│ └── Distribution.fs # Top-level IR
├── Morphir.SDK/ # F# standard library
│ ├── List.fs # List operations
│ ├── Maybe.fs # Option type
│ └── Result.fs # Result type (planned)
├── Morphir.Live/ # Interactive documentation
│ └── TryMorphir.fs # Fun.Blazor usage example
└── Morphir.Internal.CodeGeneration/ # Code generators
└── Generators/
├── VisitorGenerator.fs # Myriad visitor (stub)
└── LensGenerator.fs # Myriad lens (stub)
.agents/
├── kbs/ # Knowledge bases
│ ├── ecosystem-knowledge-base.md
│ ├── language-design-patterns.md
│ ├── visitor-pattern-implementations.md
│ ├── computation-expressions-for-ast.md
│ ├── functional-programming-patterns.md
│ └── compiler-services-metaprogramming.md
└── decisionlogs/
└── architectural-decisions.md # 25 ADRs# Build and test
./build.sh # Default build
./build.sh --target Test # Run tests
dotnet format # Format code (required pre-commit)
# Code generation (planned)
# Generate visitors for Classic IR
dotnet build -t:MyriadGenerate src/Morphir.Models/Morphir.Models.fsproj
# Generate visitors for Modern IR (future)
# Automatically generated via C# Source Generators during buildNeed to work with Morphir IR?
YES → What language is the consumer?
├─ F# → Use Classic IR (src/Morphir.Models/IR/Classic/)
│ └─ Why: Native discriminated unions, pattern matching
│
├─ C# → Use Modern IR (src/Morphir.Core/IR/)
│ └─ Why: Sealed records, better IDE support
│
└─ Both → Use conversion functions
├─ classicToCSharp: Classic IR → Modern IR
└─ csharpToClassic: Modern IR → Classic IR
NO → Working with different AST?
└─ Apply same patterns (ADTs, immutability, etc.)Need to traverse/transform AST?
├─ Language: F# + Discriminated Unions
│ ├─ Simple traversal → Functional Pattern Matching
│ ├─ Composable operations → Record Visitor
│ └─ Complex transformation → Catamorphism (fold)
│
├─ Language: C# + Sealed Records
│ ├─ Multiple operations → Classic OO Visitor (ITypeVisitor<TResult>)
│ ├─ Single transformation → Pattern Matching (switch expressions)
│ └─ Accumulation → Accumulating Visitor
│
└─ Special Requirements
├─ Deep recursion → Trampolined Visitor (stack safety)
├─ Async operations → Async Visitor
└─ Scoped context → Context-Passing VisitorNeed to handle errors?
├─ User-facing validation (collect all errors)
│ └─ Use Applicative Validation
│ → Accumulate errors: map2, map3, apply
│ → Better UX (show all errors at once)
│
├─ Internal pipeline (stop on first error)
│ └─ Use Railway-Oriented Programming
│ → Result monad with bind (>>=)
│ → Short-circuit on failure
│ → Explicit error propagation
│
└─ Optional values (no error information)
└─ Use Option/Maybe
→ Some/None (F#)
→ Nullable reference types (C#)When to use: Creating new AST node types or extending Morphir IR
Prerequisites:
Steps:
Phase 1: Design ADT Structure
Define Type Variants
Add Generic Attributes (if F#)
type MyType<'attributes> =
| Case1 of 'attributes * Field1 * Field2
| Case2 of 'attributes * OtherFieldsApply Immutability
record with init-only propertiesPhase 2: Implement Functor (if needed) 4. Add Map Function
let rec mapMyType (f: 'a -> 'b) (typ: MyType<'a>) : MyType<'b> =
match typ with
| Case1 (attrs, field1, field2) -> Case1 (f attrs, field1, field2)
| Case2 (attrs, others) -> Case2 (f attrs, others)Phase 3: Create Visitor Support (optional) 5. Design Visitor Interface/Record
Post-Workflow:
Duration: ~2-3 hours
When to use: Building validation or transformation pipelines with error handling
Prerequisites:
Steps:
Phase 1: Define Error Type
Create Error ADT
type ValidationError =
| EmptyName
| InvalidFormat of field: string * pattern: string
| OutOfRange of field: string * min: int * max: intDefine Result Type Alias
type ValidationResult<'T> = Result<'T, ValidationError>Phase 2: Build Railway Functions 3. Create Validation Functions
let validateNotEmpty fieldName value =
if String.IsNullOrWhiteSpace(value) then
Error (EmptyName)
else
Ok value
let validateRange fieldName min max value =
if value >= min && value <= max then
Ok value
else
Error (OutOfRange (fieldName, min, max))let (>>=) = Result.bind
let validatePerson name age email =
Ok (fun n a e -> { Name = n; Age = a; Email = e })
<!> validateNotEmpty "name" name
>>= fun f -> validateAge age >>= fun a -> Ok (f a)
>>= fun f -> validateEmail email >>= fun e -> Ok (f e)Phase 3: Handle Errors 5. Map Errors (if needed)
let result =
validatePerson name age email
|> Result.mapError (fun err ->
match err with
| EmptyName -> "Name cannot be empty"
| InvalidFormat (field, pattern) -> $"{field} format invalid"
| OutOfRange (field, min, max) -> $"{field} out of range [{min}-{max}]"
)let orElse alternative result =
match result with
| Ok _ -> result
| Error _ -> alternative
let getUser userId =
fetchFromCache userId
|> orElse (fetchFromDatabase userId)Post-Workflow:
Duration: ~1-2 hours
When to use: Need to update deeply nested immutable structures
Prerequisites:
Steps:
Phase 1: Define Lenses
Create Lens Type (F#)
type Lens<'S, 'A> = {
Get: 'S -> 'A
Set: 'A -> 'S -> 'S
}Define Field Lenses
let addressLens = {
Get = fun p -> p.Address
Set = fun a p -> { p with Address = a }
}
let cityLens = {
Get = fun a -> a.City
Set = fun c a -> { a with City = c }
}Phase 2: Compose Lenses 3. Create Lens Composition
let compose (outer: Lens<'A, 'B>) (inner: Lens<'B, 'C>) : Lens<'A, 'C> =
{
Get = fun a -> inner.Get (outer.Get a)
Set = fun c a -> outer.Set (inner.Set c (outer.Get a)) a
}
let (>>>) = composelet personCityLens = addressLens >>> cityLensPhase 3: Use Lenses 5. Perform Updates
let updatedPerson = Lens.set personCityLens "Shelbyville" person[<GenerateLenses>]
type Config = { Port: int; Host: string; Timeout: int }
// Myriad generates:
// module Config.Lenses =
// let port = { Get = fun c -> c.Port; Set = fun v c -> { c with Port = v } }
// let host = { Get = fun c -> c.Host; Set = fun v c -> { c with Host = v } }
// let timeout = { Get = fun c -> c.Timeout; Set = fun v c -> { c with Timeout = v } }Post-Workflow:
Duration: ~30 minutes - 1 hour
This skill proactively reviews morphir-dotnet architecture for:
ADT Design Anti-patterns - Non-exhaustive pattern matching, mutable state
var , search for _ in pattern matchesFP Pattern Violations - Improper monad usage, nullable instead of Option
null instead of None, exceptions instead of Resultnull, throw, check Result usageImmutability Violations - Mutable collections, mutable fields
List<T> instead of ImmutableList<T>, mutable propertiesList<, set; in propertiesIR Consistency Issues - Classic/Modern IR sync, missing conversions
Scheduled Review:
Session-Based Review:
On-Demand Review:
@skill morphir-architect reviewFindings Structure:
# Morphir Architect Review Report
**Date:** 2025-12-24
**Scope:** Full codebase architectural scan
**Duration:** ~10 minutes
## Summary
- ADT Types Reviewed: 42
- FP Pattern Usage: 95% compliant
- Immutability: 100% (all IR types immutable)
- IR Consistency: Classic/Modern in sync
## Findings
### Category: ADT Design
- **Finding 1:** Non-exhaustive pattern match in src/Morphir.Tooling/Transform.fs:145
- **Location:** src/Morphir.Tooling/Transform.fs:145
- **Severity:** High
- **Recommendation:** Add `_ -> failwith "Unreachable"` or handle all cases
### Category: FP Patterns
- **Finding 1:** Using `null` instead of Option in src/Morphir.Backends/Utils.cs:67
- **Location:** src/Morphir.Backends/Utils.cs:67
- **Severity:** Medium
- **Recommendation:** Replace with `Option<T>` or nullable reference type
## Trends
- Railway-Oriented Programming adoption increasing (5 new uses this quarter)
- Lens usage still manual (pending Myriad generator)
## Recommendations
1. **Immediate:** Fix non-exhaustive pattern matches
2. **Short-term:** Implement Myriad lens generator
3. **Long-term:** Standardize Result type across CLI tools
## Automation Opportunities
- Lens generation: Manual lenses in 12 files → Myriad generator
- Visitor generation: Manual visitors in 8 files → Source generator/MyriadLocation: .claude/skills/morphir-architect/scripts/
architecture-review.fsx
ir-consistency-check.fsx (future)
Usage:
# Run architectural review
dotnet fsi .claude/skills/morphir-architect/scripts/architecture-review.fsx
# Check IR consistency
dotnet fsi .claude/skills/morphir-architect/scripts/ir-consistency-check.fsxBefore completing a review:
See the knowledge bases for comprehensive pattern documentation:
Technical Writer:
QA Tester:
AOT Guru:
Elm-to-F# Guru:
When to Escalate:
How to Escalate:
architecture, maintainer-attentionWhat NOT to Decide:
Location: .claude/skills/morphir-architect/templates/
Purpose: Template for designing new AST/IR types When to Use: Creating new node types for Morphir IR or custom ASTs
Usage:
cp .claude/skills/morphir-architect/templates/new-ast-type.md .Purpose: Template for Railway-Oriented Programming pipelines When to Use: Building validation or transformation chains
Usage:
cp .claude/skills/morphir-architect/templates/transformation-pipeline.md .Purpose: Template for implementing visitor patterns When to Use: Need systematic AST traversal or transformation
Usage:
cp .claude/skills/morphir-architect/templates/visitor-implementation.md .Within This Project:
Knowledge Bases:
Project Guidance:
External Resources:
Last Updated: 2025-12-24 Version: 1.0.0-alpha Status: alpha Maintainer: Damian Reeves (@DamianReeves)
finos/morphir-dotnet
da2167e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.