Starting Work: Check the status to see what’s being worked on
Implementation: Update the PRD’s Feature Status Tracking table as you complete features
Design Decisions: Add Implementation Notes to capture important decisions
Questions: Document answers to Open Questions as they’re resolved
For AI Agents
When asked “What should I work on?” or “What’s the current status?”:
Check this index for active PRDs
Open the relevant PRD and find the Feature Status Tracking table
Look for features with status β³ Planned (ready to start) or π§ In Progress
Update feature status in real-time as work progresses
Add Implementation Notes for significant design decisions
Creating a New PRD
Copy an existing PRD as a template
Fill in all sections with comprehensive detail
Include Feature Status Tracking table with all planned features
Add to this index with “Draft” status
Submit for review and approval before implementation begins
1 - PRD: IR JSON Schema Verification
Product Requirements Document for Morphir IR JSON schema verification tooling
Product Requirements Document: IR JSON Schema Verification
Status: β Phase 1 Complete | β³ Phase 2 Ready
Created: 2025-12-13
Last Updated: 2025-12-15
Phase 1 Completion Date: 2025-12-15
Current Phase: Phase 1 Complete - Ready for Phase 2
Author: Morphir .NET Team
Overview
This PRD defines the requirements for adding JSON schema verification capabilities to the Morphir .NET CLI and tooling. This feature will enable developers to validate Morphir IR JSON files against the official schema specifications for all supported format versions (v1, v2, v3).
The implementation will introduce WolverineFx as a messaging layer between the CLI and core tooling services, using Vertical Slice Architecture to organize features by use case rather than technical layers.
Problem Statement
Currently, developers working with Morphir IR JSON files have no built-in way to:
Validate IR correctness: Verify that generated or hand-written IR files conform to the expected schema
Debug format issues: Quickly identify structural problems in IR files
Ensure version compatibility: Confirm which schema version an IR file uses and whether it’s valid
Catch errors early: Detect malformed IR before it causes runtime failures in downstream tools
Current Pain Points
Manual validation: Developers must use external tools (Python jsonschema, Node.js ajv-cli) to validate IR
Version confusion: No automated way to detect which schema version an IR file uses
Poor error messages: External validators provide generic JSON schema errors without Morphir-specific context
Workflow friction: Validation requires switching between tools and languages
Goals
Primary Goals
Enable IR validation via CLI command for all supported schema versions (v1, v2, v3)
Establish WolverineFx integration with Vertical Slice Architecture as the foundation for future tooling commands
Provide excellent developer experience with clear, actionable error messages and multiple output formats
Support flexible input starting with file paths, with extensibility for stdin and multiple files
Auto-detect schema versions while allowing manual override when needed
Secondary Goals
Create reusable validation services in Morphir.Tooling that can be leveraged by other tools
Establish testing patterns using BDD scenarios for validation use cases
Document architectural decisions for Vertical Slice Architecture adoption
Non-Goals
Explicitly Out of Scope
IR migration/upgrade tooling: Will be addressed in a separate PRD (tracked below)
Schema generation: Creating schemas from .NET types
Real-time validation: IDE plugins or language servers
IR parsing/deserialization: This already exists in Morphir.Core
Schema authoring: Schemas are maintained in the upstream Morphir repository
User Stories
Story 1: Validate IR File
As a Morphir developer
I want to validate my IR JSON file against the official schema
So that I can catch structural errors before using the IR in other tools
Acceptance Criteria:
User runs morphir ir verify path/to/morphir-ir.json
Tool auto-detects schema version from JSON
Tool validates against appropriate schema
Tool returns clear success or detailed error messages
Exit code is 0 for valid, non-zero for invalid
Story 2: Validate Specific Schema Version
As a Morphir tooling developer
I want to validate IR against a specific schema version
So that I can test version-specific compatibility
Acceptance Criteria:
User runs morphir ir verify --schema-version 3 path/to/morphir-ir.json
Tool validates against specified schema version regardless of file content
Tool reports validation results for the specified version
Story 3: Machine-Readable Output
As a CI/CD pipeline
I want to get validation results in JSON format
So that I can parse and process errors programmatically
Acceptance Criteria:
User runs morphir ir verify --json path/to/morphir-ir.json
Tool outputs structured JSON with validation results
JSON includes error locations, messages, and metadata
Story 4: Quick Status Check
As a developer in a CI pipeline
I want to validate IR without verbose output
So that I can keep build logs clean
Acceptance Criteria:
User runs morphir ir verify --quiet path/to/morphir-ir.json
Tool only outputs errors (if any)
Exit code indicates success/failure
Story 5: Detect IR Version
As a Morphir developer
I want to identify which schema version my IR file uses
So that I know which tools and features are compatible
Acceptance Criteria:
User runs morphir ir detect-version path/to/morphir-ir.json
Tool analyzes IR structure and reports detected version
Tool provides confidence level or rationale for detection
Detailed Requirements
Functional Requirements
FR-1: Command Interface
Command Structure:
morphir ir verify <file-path> [options]
Required Arguments:
<file-path>: Path to the Morphir IR JSON file to validate
Options:
--schema-version <version>: Explicitly specify schema version (1, 2, or 3)
--json: Output results in JSON format
--quiet: Suppress output except errors
-v, --verbose: Show detailed validation information
Exit Codes:
0: Validation successful
1: Validation failed (schema errors)
2: Operational error (file not found, invalid JSON, etc.)
FR-2: Input Format Support
Phase 1 (Initial Release):
β File paths (absolute and relative)
Phase 2 (Future):
β³ Stdin support: cat morphir-ir.json | morphir ir verify -
β³ Multiple files: morphir ir verify file1.json file2.json file3.json
β³ Directory validation: morphir ir verify --recursive ./ir-files/
FR-3: Schema Version Handling
Auto-Detection Logic (default behavior):
Look for formatVersion field in JSON
Analyze tag capitalization patterns:
All lowercase tags β v1
Mixed capitalization β v2
All capitalized tags β v3
If ambiguous, report detection failure with suggestions
Manual Override:
--schema-version option forces validation against specified version
Validate against JSON Schema Draft 07 specification
Provide detailed error locations using JSON Path notation
Include contextual information in error messages
FR-6: Version Detection Helper
Command:
morphir ir detect-version <file-path>
Output Example:
Detected schema version: v3
Confidence: High
Rationale:
- All tags are capitalized ("Library", "Public", "Apply", etc.)
- Contains formatVersion: 3
Implementation Status: β³ Planned for Phase 2
FR-7: Error Reporting Quality
Error Messages Must Include:
JSON Path to the error location
Expected value/format
Actual value found
Line and column numbers (when possible)
Suggested fixes (when applicable)
Example Error:
Error: Invalid access control tag
Location: $.modules[0].types["Account"].accessControlled[0]
Expected: One of ["Public", "Private"]
Found: "public"
Suggestion: Change "public" to "Public" (capitalize first letter)
Non-Functional Requirements
NFR-1: Performance
Targets:
Small files (<100KB): Validation completes in <100ms
Typical files (<1MB): Validation completes in <500ms
Large files (>1MB): Validation completes in <2 seconds
Benchmarking:
Use BenchmarkDotNet for performance testing
Test with representative IR files of varying sizes
Profile schema loading and validation separately
NFR-2: Reliability
Error Handling:
Gracefully handle malformed JSON with clear error messages
Catch and report file I/O errors (file not found, permission denied, etc.)
Handle edge cases: empty files, extremely large files, invalid UTF-8
Never crash; always return meaningful error messages
Validation Accuracy:
100% compliance with JSON Schema Draft 07 specification
Zero false positives (valid IR rejected)
Zero false negatives (invalid IR accepted)
NFR-3: Usability
CLI Experience:
Clear, consistent command naming following morphir <noun> <verb> pattern
Colored output for terminal readability (green=success, red=errors, yellow=warnings)
Progress indicators for large files
Helpful error messages with actionable suggestions
Documentation:
CLI help text: morphir ir verify --help
User guide in main docs: /docs/guides/validating-ir.md
// Add new IR subcommandvar irCommand = new Command("ir", "Morphir IR operations");
var verifyCommand = new Command("verify", "Verify IR against JSON schema")
{
filePathArgument,
schemaVersionOption,
jsonFormatOption,
quietOption
};
verifyCommand.SetHandler(async (string filePath, int? version, bool json, bool quiet) =>
{
// Dispatch to WolverineFx handler via message busvar command = new VerifyIR(filePath, version, json, quiet);
var result = await messageBus.InvokeAsync<VerifyIRResult>(command);
// Format and display result DisplayResult(result, json, quiet);
});
irCommand.AddCommand(verifyCommand);
rootCommand.AddCommand(irCommand);
# Features/VerifyMultipleIR.featureScenario: Validate multiple files in batch
Given I have 10 valid IR files
When I run "morphir ir verify file1.json file2.json ... file10.json"
Then all 10 files should be validated
And the summary should show "10 valid, 0 invalid"
# Features/DetectVersion.featureScenario: Detect version with high confidence
Given a valid IR v3 file with formatVersion field
When I run "morphir ir detect-version file.json"
Then the detected version should be "3"
And the confidence level should be "High"
Integration Tests to Add:
CLI with stdin input (pipe)
CLI with multiple file arguments
CLI with glob patterns
Parallel processing performance
π Documentation Updates for Phase 2
New Documentation:
docs/content/docs/cli/ir-detect-version.md - New command reference
2 - BDD Test Scenarios: IR JSON Schema Verification
Comprehensive BDD test scenarios in Gherkin syntax for IR schema verification feature
BDD Test Scenarios: IR JSON Schema Verification
This document defines comprehensive BDD scenarios using Gherkin syntax for the IR JSON Schema Verification feature. These scenarios will be implemented as Reqnroll feature files in tests/Morphir.Core.Tests/Features/.
Feature: IR Schema Verification
As a Morphir developer
I want to validate IR JSON files against schemas
So that I can catch structural errors early
Background: Given the Morphir CLI is installed
And the schema files v1, v2, and v3 are available
Rule: Valid IR files pass validation
Scenario: Validate a valid v3 IR file
Given a valid Morphir IR v3 JSON file "valid-v3.json"
When I run "morphir ir verify valid-v3.json"
Then the exit code should be 0And the output should contain "β Validation successful"
And the output should contain "Schema: v3 (auto-detected)"
And the output should contain "File: valid-v3.json"
Scenario: Validate a valid v2 IR file
Given a valid Morphir IR v2 JSON file "valid-v2.json"
When I run "morphir ir verify valid-v2.json"
Then the exit code should be 0And the output should contain "β Validation successful"
And the output should contain "Schema: v2 (auto-detected)"
Scenario: Validate a valid v1 IR file
Given a valid Morphir IR v1 JSON file "valid-v1.json"
When I run "morphir ir verify valid-v1.json"
Then the exit code should be 0And the output should contain "β Validation successful"
And the output should contain "Schema: v1 (auto-detected)"
Scenario Outline: Validate various valid IR files across versions
Given a valid Morphir IR <version> JSON file "<filename>"
When I run "morphir ir verify <filename>"
Then the exit code should be 0And the output should contain "β Validation successful"
And the output should contain "Schema: <version> (auto-detected)"
Examples:
| version | filename | | v1 | library-v1.json | | v1 | complex-types-v1.json | | v2 | library-v2.json | | v2 | complex-types-v2.json | | v3 | library-v3.json | | v3 | complex-types-v3.json |
Rule: Invalid IR files fail validation with clear errors
Scenario: Validate an IR file with incorrect tag capitalization
Given an invalid Morphir IR v3 JSON file "invalid-tags.json" with lowercase tags
When I run "morphir ir verify invalid-tags.json"
Then the exit code should be 1And the output should contain "β Validation failed"
And the output should contain "Invalid type tag"
And the output should contain "Expected: \"Public\" or \"Private\""
And the output should contain "Found: \"public\""
Scenario: Validate an IR file with missing required fields
Given an invalid Morphir IR v3 JSON file "missing-fields.json" missing the "name" field
When I run "morphir ir verify missing-fields.json"
Then the exit code should be 1And the output should contain "β Validation failed"
And the output should contain "Missing required field"
And the output should contain "Path: $.package.modules"
And the output should contain "Required property 'name' is missing"
Scenario: Validate an IR file with invalid type structure
Given an invalid Morphir IR v3 JSON file "invalid-structure.json" with malformed type definitions
When I run "morphir ir verify invalid-structure.json"
Then the exit code should be 1And the output should contain "β Validation failed"
And the error count should be greater than 0Scenario: Validate an IR file with multiple errors
Given an invalid Morphir IR v3 JSON file "multiple-errors.json" with 5 validation errors
When I run "morphir ir verify multiple-errors.json"
Then the exit code should be 1And the output should contain "5 errors found"
And the output should list all 5 errors with JSON paths
Rule: Schema version can be manually specified
Scenario: Force validation against specific schema version
Given a Morphir IR JSON file "mixed-version.json"
When I run "morphir ir verify --schema-version 2 mixed-version.json"
Then the validation should use schema v2And the output should contain "Schema: v2 (manual)"
Scenario: Override auto-detection with explicit version
Given a valid Morphir IR v3 JSON file "valid-v3.json"
When I run "morphir ir verify --schema-version 3 valid-v3.json"
Then the exit code should be 0And the output should contain "Schema: v3 (manual)"
Scenario: Validate v2 file against v3 schema (should fail)
Given a valid Morphir IR v2 JSON file "valid-v2.json"
When I run "morphir ir verify --schema-version 3 valid-v2.json"
Then the exit code should be 1And the output should contain "β Validation failed against schema v3"
Scenario Outline: Validate with explicit version specification
Given a valid Morphir IR <actual-version> JSON file "<filename>"
When I run "morphir ir verify --schema-version <specified-version><filename>"
Then the exit code should be <exit-code>And the output should contain "Schema: <specified-version> (manual)"
Examples:
| filename | actual-version | specified-version | exit-code | | valid-v1.json | v1 | 1 | 0 | | valid-v2.json | v2 | 2 | 0 | | valid-v3.json | v3 | 3 | 0 | | valid-v1.json | v1 | 3 | 1 | | valid-v2.json | v2 | 1 | 1 |
Rule: Multiple output formats are supported
Scenario: Output validation results as JSON
Given an invalid Morphir IR JSON file "errors.json"
When I run "morphir ir verify --json errors.json"
Then the output should be valid JSON
And the JSON should have field "valid" with value false
And the JSON should have field "errors" as an array
And each error should include "path", "message", "expected", and "found"
Scenario: Output successful validation as JSON
Given a valid Morphir IR v3 JSON file "valid-v3.json"
When I run "morphir ir verify --json valid-v3.json"
Then the output should be valid JSON
And the JSON should have field "valid" with value true
And the JSON should have field "schemaVersion" with value "3"
And the JSON should have field "detectionMethod" with value "auto"
And the JSON should have field "errorCount" with value 0Scenario: Quiet mode suppresses success messages
Given a valid Morphir IR v3 JSON file "valid-v3.json"
When I run "morphir ir verify --quiet valid-v3.json"
Then the exit code should be 0And the output should be empty
Scenario: Quiet mode shows only errors
Given an invalid Morphir IR v3 JSON file "invalid-tags.json"
When I run "morphir ir verify --quiet invalid-tags.json"
Then the exit code should be 1And the output should contain error messages
And the output should not contain "β Validation failed"
And the output should not contain headers or decorations
Scenario: Verbose mode shows detailed information
Given a valid Morphir IR v3 JSON file "valid-v3.json"
When I run "morphir ir verify --verbose valid-v3.json"
Then the exit code should be 0And the output should contain "β Validation successful"
And the output should contain "Schema: v3 (auto-detected)"
And the output should contain "File: valid-v3.json"
And the output should contain validation timestamp
And the output should contain schema file path
Rule: Error messages are clear and actionable
Scenario: Error message includes JSON path
Given an invalid Morphir IR v3 JSON file "bad-path.json" with error at "$.modules[0].types.MyType"
When I run "morphir ir verify bad-path.json"
Then the exit code should be 1And the output should contain "Path: $.modules[0].types.MyType"
Scenario: Error message includes line and column numbers
Given an invalid Morphir IR v3 JSON file "line-col-error.json" with error at line 42, column 12When I run "morphir ir verify line-col-error.json"
Then the exit code should be 1And the output should contain "Line: 42, Column: 12"
Scenario: Error message suggests fixes
Given an invalid Morphir IR v3 JSON file "lowercase-tag.json" with lowercase "public" tag
When I run "morphir ir verify lowercase-tag.json"
Then the exit code should be 1And the output should contain 'Suggestion: Change "public" to "Public"'
Rule: Edge cases and error handling
Scenario: File not found
When I run "morphir ir verify non-existent-file.json"
Then the exit code should be 2And the output should contain "File not found: non-existent-file.json"
Scenario: Malformed JSON
Given a file "malformed.json" with invalid JSON syntax
When I run "morphir ir verify malformed.json"
Then the exit code should be 2And the output should contain "Invalid JSON"
And the output should contain the JSON parsing error location
Scenario: Empty file
Given an empty file "empty.json"
When I run "morphir ir verify empty.json"
Then the exit code should be 2And the output should contain "File is empty"
Scenario: Very large file
Given a valid Morphir IR v3 JSON file "large-10mb.json" of size 10MB
When I run "morphir ir verify large-10mb.json"
Then the validation should complete within 2 seconds
And the exit code should be 0Scenario: Invalid schema version specified
Given a valid Morphir IR v3 JSON file "valid-v3.json"
When I run "morphir ir verify --schema-version 5 valid-v3.json"
Then the exit code should be 2And the output should contain "Schema version must be 1, 2, or 3"
Scenario: File with invalid UTF-8 encoding
Given a file "invalid-utf8.json" with invalid UTF-8 bytes
When I run "morphir ir verify invalid-utf8.json"
Then the exit code should be 2And the output should contain "Invalid file encoding"
Feature 2: Version Detection
Feature File: IrVersionDetection.feature
Feature: IR Version Detection
As a Morphir developer
I want to automatically detect which schema version my IR uses
So that I can validate against the correct schema
Background: Given the Morphir CLI is installed
And the schema files v1, v2, and v3 are available
Rule: Auto-detection works for files with formatVersion field
Scenario: Detect version from formatVersion field (v3)
Given a Morphir IR JSON file "with-format-v3.json" containing "formatVersion": 3When I run "morphir ir verify with-format-v3.json"
Then the validation should use schema v3And the output should contain "Schema: v3 (auto-detected)"
Scenario Outline: Detect version from formatVersion field
Given a Morphir IR JSON file "<filename>" containing "formatVersion": <version>When I run "morphir ir verify <filename>"
Then the validation should use schema v<version>And the output should contain "Schema: v<version> (auto-detected)"
Examples:
| filename | version | | format-v1.json | 1 | | format-v2.json | 2 | | format-v3.json | 3 |
Rule: Auto-detection uses tag capitalization when formatVersion is absent
Scenario: Detect v1 from lowercase tags
Given a Morphir IR JSON file "no-format-v1.json" without formatVersion
And the file uses all lowercase tags like "library", "public", "apply"
When I run "morphir ir verify no-format-v1.json"
Then the validation should use schema v1And the output should contain "Schema: v1 (auto-detected)"
Scenario: Detect v3 from capitalized tags
Given a Morphir IR JSON file "no-format-v3.json" without formatVersion
And the file uses all capitalized tags like "Library", "Public", "Apply"
When I run "morphir ir verify no-format-v3.json"
Then the validation should use schema v3And the output should contain "Schema: v3 (auto-detected)"
Scenario: Detect v2 from mixed capitalization
Given a Morphir IR JSON file "no-format-v2.json" without formatVersion
And the file uses mixed case tags
When I run "morphir ir verify no-format-v2.json"
Then the validation should use schema v2And the output should contain "Schema: v2 (auto-detected)"
Rule: Standalone version detection command
Scenario: Detect version with dedicated command
Given a Morphir IR JSON file "detect-me.json" with v3 structure
When I run "morphir ir detect-version detect-me.json"
Then the exit code should be 0And the output should contain "Detected schema version: v3"
And the output should contain "Confidence: High"
And the output should contain "Rationale:"
Scenario: Version detection shows rationale
Given a Morphir IR JSON file "v3-with-format.json" containing "formatVersion": 3When I run "morphir ir detect-version v3-with-format.json"
Then the output should contain "Contains formatVersion: 3"
Scenario: Version detection analyzes tag patterns
Given a Morphir IR JSON file "v3-no-format.json" without formatVersion but with capitalized tags
When I run "morphir ir detect-version v3-no-format.json"
Then the output should contain 'All tags are capitalized ("Library", "Public", "Apply")'
Scenario Outline: Detect version with varying confidence levels
Given a Morphir IR JSON file "<filename>" with <indicators>When I run "morphir ir detect-version <filename>"
Then the output should contain "Confidence: <confidence>"
Examples:
| filename | indicators | confidence | | clear-v3.json | formatVersion and cap tags | High | | likely-v1.json | lowercase tags only | Medium | | ambiguous.json | minimal structure | Low |
Feature 3: Multiple File Support (Phase 2)
Feature File: IrMultiFileVerification.feature
Feature: Multiple File Verification
As a Morphir developer working with multiple IR files
I want to validate several files at once
So that I can efficiently verify my entire project
Background: Given the Morphir CLI is installed with Phase 2 features
Rule: Multiple files can be validated in one command
Scenario: Validate two valid files
Given valid IR files "file1.json" and "file2.json"
When I run "morphir ir verify file1.json file2.json"
Then the exit code should be 0And the output should show results for "file1.json"
And the output should show results for "file2.json"
And both files should pass validation
Scenario: Validate mix of valid and invalid files
Given a valid IR file "valid.json"
And an invalid IR file "invalid.json"
When I run "morphir ir verify valid.json invalid.json"
Then the exit code should be 1And the output should show "valid.json" passed
And the output should show "invalid.json" failed with errors
Scenario: Validate multiple files with summary
Given 10 valid IR files
And 3 invalid IR files
When I run "morphir ir verify *.json"
Then the exit code should be 1And the output should contain "Summary: 10 passed, 3 failed"
Rule: Stdin support for piped input
Scenario: Validate IR from stdin
Given a valid Morphir IR v3 JSON file "valid-v3.json"
When I run "cat valid-v3.json | morphir ir verify -"
Then the exit code should be 0And the output should contain "β Validation successful"
And the output should contain "Source: stdin"
Scenario: Validate invalid IR from stdin
Given an invalid Morphir IR JSON file "invalid.json"
When I run "cat invalid.json | morphir ir verify -"
Then the exit code should be 1And the output should contain "β Validation failed"
Scenario: Combine file and stdin (stdin represented as -)
Given a valid IR file "file.json"
And valid IR JSON content in stdin
When I run "cat stdin.json | morphir ir verify file.json -"
Then the exit code should be 0And the output should show results for "file.json"
And the output should show results for "stdin"
Rule: Batch processing is efficient
Scenario: Validate 100 files efficiently
Given 100 valid IR files in "batch/" directory
When I run "morphir ir verify batch/*.json"
Then the validation should complete within 10 seconds
And the exit code should be 0And the output should contain "Summary: 100 passed, 0 failed"
Scenario: Stop on first error (--fail-fast option)
Given 5 valid IR files and 1 invalid IR file
When I run "morphir ir verify --fail-fast *.json"
Then the validation should stop at the first error
And the exit code should be 1And not all files should be processed
Feature 4: Directory Validation (Phase 3)
Feature File: IrDirectoryVerification.feature
Feature: Directory Verification
As a Morphir developer with many IR files
I want to validate entire directories
So that I can ensure all my IR files are correct
Background: Given the Morphir CLI is installed with Phase 3 features
Rule: Directories can be validated recursively
Scenario: Validate all JSON files in directory
Given a directory "ir-files/" with 5 valid IR JSON files
When I run "morphir ir verify --recursive ir-files/"
Then the exit code should be 0And all 5 files should be validated
And the output should contain "5 files validated, 5 passed"
Scenario: Validate directory with mixed results
Given a directory "mixed/" with 3 valid and 2 invalid IR files
When I run "morphir ir verify --recursive mixed/"
Then the exit code should be 1And the output should contain "5 files validated, 3 passed, 2 failed"
Scenario: Skip non-JSON files in directory
Given a directory "mixed-types/" with JSON and non-JSON files
When I run "morphir ir verify --recursive mixed-types/"
Then only JSON files should be validated
And the output should list which files were skipped
Scenario: Validate nested directory structure
Given a nested directory structure:
""" project/
βββ src/
β βββ module1/
β β βββ ir.json
β βββ module2/
β βββ ir.json
βββ tests/
βββ fixtures/
βββ ir.json
"""When I run "morphir ir verify --recursive project/"
Then all 3 IR files should be validated
And the output should show the relative paths of all files
Rule: Directory validation supports filtering
Scenario: Validate only specific file patterns
Given a directory with various JSON files
When I run "morphir ir verify --recursive --pattern 'morphir-*.json' dir/"
Then only files matching "morphir-*.json" should be validated
Scenario: Exclude specific directories
Given a directory structure with "node_modules/" and "src/"
When I run "morphir ir verify --recursive --exclude 'node_modules' ."
Then files in "node_modules/" should be skipped
And files in "src/" should be validated
Feature 5: Error Reporting Quality
Feature File: IrValidationErrorReporting.feature
Feature: Validation Error Reporting
As a Morphir developer fixing validation errors
I want detailed, actionable error messages
So that I can quickly identify and fix issues
Background: Given the Morphir CLI is installed
Rule: Errors include precise location information
Scenario: Error with JSON path
Given an IR file "error.json" with invalid value at "$.modules[0].types.MyType.accessControlled[0]"
When I run "morphir ir verify error.json"
Then the output should contain the exact JSON path
And the path should be formatted as "$.modules[0].types.MyType.accessControlled[0]"
Scenario: Error with line and column numbers
Given an IR file "error.json" with syntax error at line 42, column 12When I run "morphir ir verify error.json"
Then the output should contain "Line: 42, Column: 12"
Scenario: Error shows context snippet
Given an IR file with error at line 42When I run "morphir ir verify --verbose error.json"
Then the output should include a code snippet around line 42And the error line should be highlighted
Rule: Errors explain what was expected vs found
Scenario: Type mismatch error
Given an IR file with string where number is expected
When I run "morphir ir verify error.json"
Then the output should contain "Expected: number"
And the output should contain 'Found: "some string"'
Scenario: Enum value error
Given an IR file with invalid access control tag
When I run "morphir ir verify error.json"
Then the output should contain 'Expected: One of ["Public", "Private"]'
And the output should contain 'Found: "public"'
Scenario: Array length constraint error
Given an IR file with array that violates length constraints
When I run "morphir ir verify error.json"
Then the output should contain "Expected: Array with 2 elements"
And the output should contain "Found: Array with 3 elements"
Rule: Errors provide helpful suggestions
Scenario: Suggest capitalization fix
Given an IR file with lowercase tag in v3 IR
When I run "morphir ir verify error.json"
Then the output should contain 'Suggestion: Change "public" to "Public"'
Scenario: Suggest adding missing field
Given an IR file missing required "name" field
When I run "morphir ir verify error.json"
Then the output should contain 'Suggestion: Add required field "name"'
Scenario: Suggest similar field names for typos
Given an IR file with "nmae" instead of "name"
When I run "morphir ir verify error.json"
Then the output should contain 'Did you mean "name"?'
Rule: Multiple errors are clearly enumerated
Scenario: List multiple errors with numbering
Given an IR file with 3 validation errors
When I run "morphir ir verify error.json"
Then the output should contain "Error 1:"
And the output should contain "Error 2:"
And the output should contain "Error 3:"
And the output should contain "3 errors found"
Scenario: Group errors by category
Given an IR file with type errors and missing field errors
When I run "morphir ir verify error.json"
Then errors should be grouped by type
And the output should show "Type Errors (2)" and "Missing Fields (3)"
Scenario: Limit error display with --max-errors option
Given an IR file with 50 validation errors
When I run "morphir ir verify --max-errors 10 error.json"
Then only the first 10 errors should be displayed
And the output should contain "... and 40 more errors"
Rule: Error output is machine-readable in JSON mode
Scenario: JSON error format includes all details
Given an IR file with validation errors
When I run "morphir ir verify --json error.json"
Then the JSON output should include:
| field | description | | valid | false | | errors | Array of error objects | | errors[].path | JSON path to error | | errors[].line | Line number | | errors[].column | Column number | | errors[].message| Human-readable error message | | errors[].code | Machine-readable error code |
Scenario: Error codes are consistent and documented
Given an IR file with a missing required field
When I run "morphir ir verify --json error.json"
Then the error should have code "MISSING_REQUIRED_FIELD"
And the error code should be documented
Feature 6: Performance and Scalability
Feature File: IrValidationPerformance.feature
Feature: Validation Performance
As a developer integrating validation in CI/CD
I want fast validation even for large files
So that builds remain efficient
Background: Given the Morphir CLI is installed
Rule: Validation meets performance targets
Scenario Outline: Validate files of varying sizes
Given a valid Morphir IR v3 JSON file of size <size>When I run "morphir ir verify <filename>"
Then the validation should complete within <max-time>And the exit code should be 0Examples:
| size | filename | max-time | | 10KB | small.json | 100ms | | 100KB | medium.json | 100ms | | 1MB | large.json | 500ms | | 10MB | very-large.json | 2000ms |
Scenario: Schema caching improves performance
Given 10 valid IR files
When I run "morphir ir verify file1.json ... file10.json"
Then schemas should only be loaded once
And subsequent validations should be faster
Scenario: Memory usage remains bounded
Given a 50MB IR file
When I run "morphir ir verify huge.json"
Then memory usage should not exceed 500MB
And validation should complete successfully
Rule: Validation supports progress reporting
Scenario: Show progress for multiple files
Given 100 IR files to validate
When I run "morphir ir verify --progress *.json"
Then the output should show a progress indicator
And the progress should update as files are validated
Scenario: Show progress for large single file
Given a 10MB IR file
When I run "morphir ir verify --progress large.json"
Then the output should show validation progress
Implementation Notes
Step Definition Organization
Step definitions should be organized in the following files within tests/Morphir.Core.Tests/StepDefinitions/:
IrVerificationSteps.cs: Common steps for file setup, CLI execution, output assertions
IrSchemaSteps.cs: Steps specific to schema validation
IrVersionDetectionSteps.cs: Steps for version detection scenarios
IrFileManagementSteps.cs: Steps for file and directory operations
Test Data Strategy
Test IR JSON files should be stored in tests/Morphir.Core.Tests/TestData/IrFiles/:
3 - PRD: Product Manager Skill for Morphir Ecosystem
Product Requirements Document for an AI Product Manager skill with comprehensive Morphir ecosystem knowledge
Product Requirements Document: Product Manager Skill for Morphir Ecosystem
Status: π Draft
Created: 2025-12-18
Last Updated: 2025-12-18
Current Phase: Phase 1 - Planning and Design
Author: Morphir .NET Team
Related Issue: #228
Overview
This PRD defines requirements for creating a specialized Product Manager skill for AI coding agents. This skill will provide comprehensive product management capabilities tailored to the Morphir ecosystem across all FINOS Morphir repositories, helping users create better PRDs, craft meaningful issues, understand the ecosystem, and make product decisions aligned with Morphir’s philosophy.
Problem Statement
Currently, contributors working across the Morphir ecosystem face several challenges:
Fragmented Knowledge: Morphir spans multiple repositories (morphir-elm, morphir-jvm, morphir-scala, morphir-dotnet, etc.) with varying maturity levels, features, and conventions
Inconsistent Issue Quality: Issues and PRs often lack context, proper categorization, or alignment with project goals
PRD Gaps: Not all features have comprehensive PRDs, and creating high-quality PRDs requires deep Morphir knowledge
Cross-Repo Blind Spots: Contributors may duplicate work or miss opportunities for cross-repository synergies
UX/DX Debt: User experience and developer experience improvements need dedicated advocacy
Manual Ecosystem Tracking: No automated way to track trends, backlogs, or health metrics across the ecosystem
Current Pain Points
New contributors struggle to understand where to contribute and how to write good issues
Maintainers spend time triaging poorly-written issues and PRs
Product decisions lack ecosystem-wide context and may not align with Morphir’s functional modeling philosophy
Documentation gaps make it hard to understand feature status across implementations
Backlog management is manual and repository-siloed
Goals
Primary Goals
Expert PRD Guidance: Help users create comprehensive, well-structured PRDs aligned with Morphir principles
Issue Quality Improvement: Assist in crafting high-quality issues (bugs, features, enhancements) with proper context
Ecosystem Intelligence: Provide real-time awareness of backlogs, trends, and status across all Morphir repositories
UX/DX Advocacy: Champion user and developer experience improvements
Intelligent Questioning: Push back constructively on features that don’t align with Morphir’s ethos
GitHub Automation: Provide F# scripts for querying, analyzing, and reporting across the ecosystem
Secondary Goals
Cross-Skill Integration: Coordinate effectively with qa-tester and release-manager skills
Knowledge Management: Maintain and share institutional knowledge about Morphir
Template Library: Provide reusable templates for common product management tasks
Metrics & Analytics: Track and report ecosystem health metrics
Non-Goals
Explicitly Out of Scope
Code Implementation: Development agents handle implementation
As a contributor
I want to validate that my feature idea aligns with Morphir’s philosophy
So that I don’t waste effort on something that won’t be accepted
Acceptance Criteria:
User proposes a feature idea
Product Manager asks probing questions about motivation, alternatives
Filter by label, state, milestone, assignee, author
Sort by created, updated, comments, reactions
Format output as JSON, Markdown, or table
Cache results for performance
Script: analyze-backlog.fsx
// Analyze backlog health metrics
// Usage: dotnet fsi analyze-backlog.fsx --repo "finos/morphir-dotnet"
// Output: Health report with metrics and recommendations
Features:
Calculate backlog age distribution
Identify stale issues (no activity in 90+ days)
Analyze priority distribution
Compare against ecosystem averages
Generate recommendations for triage
Script: trend-analysis.fsx
// Identify trending topics across ecosystem
// Usage: dotnet fsi trend-analysis.fsx --since "30 days ago"
// Output: Trend report with top labels, themes, activity
Features:
Most active labels in time period
Emerging themes from issue titles/descriptions
Spike detection (unusual activity)
Cross-repository correlation
Sentiment analysis (positive/negative)
Script: check-ecosystem.fsx
// Check status across all Morphir repositories
// Usage: dotnet fsi check-ecosystem.fsx
// Output: Ecosystem health dashboard
Features:
Latest release versions
CI/CD status
Open PR counts
Recent activity summary
Documentation status
Test coverage (if available)
Script: generate-prd.fsx
// Generate PRD from template with interactive prompts
// Usage: dotnet fsi generate-prd.fsx --template "standard"
// Output: PRD markdown file
Features:
Interactive questionnaire for PRD sections
Pre-fill from existing issues or discussions
Validate completeness
Preview before saving
Save to docs/content/contributing/design/prds/
FR-5: Integration with Other Skills
With qa-tester:
Coordinate on acceptance criteria definition
Align test plans with PRD requirements
Validate feature completeness against PRD
Review test coverage for PRD features
With release-manager:
Align features with release roadmap
Coordinate changelog entries
Review “What’s New” documentation
Prioritize features for releases
With development agents:
Provide clear requirements and context
Answer questions during implementation
Validate implementation against PRD
Document design decisions in PRD
FR-6: Knowledge Management
Morphir Core Concepts:
Functional modeling approach
Type-driven development
Business domain modeling
Distribution and intermediate representation
Cross-language support strategy
Architecture Patterns:
Vertical Slice Architecture
Railway-oriented programming
ADT-first design
Immutability and pure functions
Effect management at boundaries
Decision-Making Framework:
IR fidelity over convenience
Minimize dependencies
Performance requires benchmarks
Keep effects at edges
Prefer explicit ADTs
Non-Functional Requirements
NFR-1: Response Time
Script execution < 30 seconds for single-repo queries
Script execution < 2 minutes for ecosystem-wide queries
PRD generation interactive (responds to each question in < 5 seconds)
NFR-2: Accuracy
Cross-repository queries return 100% accurate results
Trend analysis validated against manual review (>95% agreement)
Issue recommendations relevant (>80% user acceptance)
NFR-3: Maintainability
Scripts use GitHub CLI (gh) for authentication
Scripts use standard F# libraries (no exotic dependencies)
Scripts include help text and examples
Scripts handle rate limiting gracefully
NFR-4: Usability
Clear, conversational interaction style
Asks clarifying questions before making assumptions
Provides rationale for recommendations
Offers alternatives when pushing back
Links to relevant documentation
NFR-5: Documentation
Comprehensive skill.md with all capabilities
README with quick start guide
Script documentation with usage examples
Template documentation with instructions
Integration guide for other skills
Technical Design
Skill Structure
.claude/skills/product-manager/
βββ skill.md # Main skill definition and playbooks
βββ README.md # Quick start and overview
βββ scripts/ # F# automation scripts
ββββ query-issues.fsx # Multi-repo issue queries
ββββ analyze-backlog.fsx # Backlog health analysis
ββββ trend-analysis.fsx # Trend detection and reporting
ββββ check-ecosystem.fsx # Ecosystem status dashboard
ββββ generate-prd.fsx # Interactive PRD generation
ββββ update-knowledge.fsx # Update knowledgebase from live sources
ββββ common/ # Shared utilities
ββββ github-api.fsx # GitHub API helpers
ββββ formatting.fsx # Output formatting
ββββ cache.fsx # Result caching
βββ templates/ # Document templates
ββββ prd-standard.md # Standard feature PRD
ββββ prd-architecture.md # Architecture change PRD
ββββ prd-breaking.md # Breaking change PRD
ββββ issue-feature.md # Feature request template
ββββ issue-bug.md # Bug report template
ββββ issue-enhancement.md # Enhancement proposal template
βββ knowledge/ # Curated knowledgebase (markdown)
ββββ README.md # Knowledgebase overview and index
ββββ morphir-principles.md # Core Morphir philosophy and principles
ββββ ecosystem-map.md # Repository overview and relationships
ββββ architecture/ # Architecture patterns and decisions
βββββ ir-design.md # IR architecture and versioning
βββββ vertical-slices.md # Vertical Slice Architecture
βββββ type-system.md # Morphir type system
βββββ distribution-model.md # Cross-language distribution
ββββ repositories/ # Per-repository knowledge
βββββ morphir-core.md # finos/morphir (specs)
βββββ morphir-elm.md # finos/morphir-elm (reference)
βββββ morphir-jvm.md # finos/morphir-jvm
βββββ morphir-scala.md # finos/morphir-scala
βββββ morphir-dotnet.md # finos/morphir-dotnet (this repo)
βββββ morphir-examples.md # finos/morphir-examples
ββββ features/ # Feature status across repos
βββββ cli-tools.md # CLI feature parity
βββββ ir-versions.md # IR version support matrix
βββββ backends.md # Backend/codegen support
βββββ testing-tools.md # Testing capabilities
ββββ conventions/ # Standards and conventions
βββββ naming.md # Naming conventions
βββββ code-style.md # Code style per language
βββββ commit-messages.md # Commit message format
βββββ issue-labels.md # Standard labels across repos
ββββ workflows/ # Common workflows and processes
βββββ contributing.md # Contribution workflow
βββββ prd-process.md # PRD creation and review
βββββ release-process.md # Release workflow
βββββ issue-triage.md # Issue triage guidelines
ββββ faq/ # Frequently asked questions
ββββ product-decisions.md # Common product decision rationales
ββββ technical-choices.md # Technical architecture FAQs
ββββ cross-repo-alignment.md # How to align features across repos
βββ docs/ # Skill-specific documentation
βββ integration-guide.md # Integration with other skills
Morphir Ecosystem Model
Repository Categories:
Core Specification (finos/morphir)
Language specification
IR schema definitions (v1, v2, v3)
Authoritative documentation
Reference Implementation (finos/morphir-elm)
Elm frontend compiler
CLI tools
Example models
Most mature implementation
Platform Implementations:
finos/morphir-jvm: Java/Kotlin support
finos/morphir-scala: Scala support
finos/morphir-dotnet: C#/F# support
Resources:
finos/morphir-examples: Example models and documentation
Cross-Repository Queries:
// Example: Find all IR-related issues across ecosystem
let irIssues = MorphirRepos.All
|> Seq.collect (fun repo -> GitHub.queryIssues repo "label:IR")|> Seq.sortByDescending (_.UpdatedAt)
queryEcosystemIssues {
search(query: "org:finos morphir in:name is:issue label:enhancement", type: ISSUE, first: 100) {
nodes {
...on Issue {
title
number
repository { name }
labels(first: 10) { nodes { name } }
}
}
}
}
Knowledgebase Management
Purpose: The Product Manager skill maintains a curated knowledgebase of Morphir ecosystem knowledge as markdown files within the skill directory. This enables offline access, version control, and structured knowledge organization.
// update-knowledge.fsx: Fetch latest info from live sources
// Usage: dotnet fsi update-knowledge.fsx --category repositories
// Fetch latest README from each repo
let updateRepositoryDocs repos = repos
|> Seq.iter (fun repo ->let readme = GitHub.fetchFile repo "README.md"let repoDoc = Knowledge.parseRepositoryInfo readme
Knowledge.save $"knowledge/repositories/{repo.name}.md" repoDoc
)// Fetch latest feature status
let updateFeatureMatrix () =let cliFeatures = MorphirRepos.All
|> Seq.collect (fun repo -> GitHub.searchCode repo "CLI commands") Knowledge.generateFeatureMatrix cliFeatures
|> Knowledge.save "knowledge/features/cli-tools.md"// Validate knowledgebase consistency
let validateKnowledge () = Knowledge.checkBrokenLinks ()
Knowledge.validateMarkdown ()
Knowledge.checkOutdatedInfo ()
Knowledge Access Patterns:
When asked about Morphir principles:
1. Read knowledge/morphir-principles.md
2. Cite specific sections with links
3. Provide examples from knowledge/faq/
When comparing repos:
1. Read knowledge/ecosystem-map.md for overview
2. Read specific knowledge/repositories/{repo}.md
3. Consult knowledge/features/ for capability matrix
When validating feature alignment:
1. Reference knowledge/morphir-principles.md
2. Check knowledge/architecture/ for design patterns
3. Review knowledge/faq/product-decisions.md for precedents
Knowledge Maintenance:
Manual Curation: Maintainers update knowledge files as authoritative sources
Periodic Updates: Run update-knowledge.fsx quarterly to refresh from live sources
Version Control: Knowledge evolves with the skill, tracked in git
Validation: CI validates markdown formatting and internal links
Review Process: Knowledge changes reviewed like code changes
Knowledge vs. Live Data:
Knowledgebase: Stable, curated, architectural, and philosophical knowledge
Live Queries: Real-time issue data, PR status, recent activity
Hybrid Approach: Use knowledge for context, live queries for current state
PRD Template Engine
Interactive Generation:
// Prompt user for each section
let prd = PRD.Interactive [ Section.Overview [ Question "What feature are you proposing?" Question "Why is this feature needed?"] Section.Goals [ Question "What are the primary goals? (one per line)" Question "What is explicitly out of scope?"]// ... more sections
]// Validate completeness
let validation = PRD.validate prd
// Save to file
PRD.save "docs/content/contributing/design/prds/my-feature.md" prd
Skill Activation Triggers
Keywords:
“PRD”, “product requirements”, “feature spec”
“create issue”, “file bug”, “report enhancement”
“ecosystem”, “cross-repo”, “morphir repos”
“backlog”, “triage”, “issue health”
“trend”, “popular”, “common issues”
“align with morphir”, “morphir philosophy”
Scenarios:
User asks for help creating a PRD
User wants to file an issue
User asks “what should I work on?”
User asks about feature status across repos
User proposes a feature that may not align
User asks about Morphir architecture or principles
Q3: Should F# scripts use GitHub CLI or direct API calls?
Status: Open
Options:
GitHub CLI (gh) for simplicity and auth
Direct API calls via HTTP client for flexibility
Hybrid approach
Recommendation: GitHub CLI for Phase 1, evaluate direct API if needed
Decision Needed By: Phase 1 (Week 1)
Impact: Affects script architecture and dependencies
Q4: How deep should trend analysis go?
Status: Open
Options:
Label frequency and time-series only
Add NLP for theme extraction from titles/descriptions
Add sentiment analysis
Recommendation: Start with label frequency, add NLP in Phase 3 if valuable
Decision Needed By: Phase 3 (Week 3)
Impact: Affects trend-analysis.fsx complexity and dependencies
Refactor the morphir-dotnet deployment architecture to fix critical packaging issues, separate tool distribution from executable distribution, implement comprehensive build testing, and establish changelog-driven versioning as the single source of truth.
Problem: The current deployment failed due to package naming mismatches (lowercase “morphir” vs “Morphir”), inconsistent tool command naming, and lack of automated testing to catch these issues before CI deployment.
Solution: Separate concerns into distinct projects (Morphir.Tool for dotnet tool, Morphir for executables), reorganize build system following vertical slice architecture, implement Ionide.KeepAChangelog for version management, and add comprehensive build testing infrastructure.
Impact: Eliminates deployment failures, provides clear distribution strategy for different user personas, enables confident releases with automated validation, and establishes maintainable build architecture.
Successful deployment to NuGet.org and GitHub Releases
β Separate concerns
Distinct Morphir.Tool project for dotnet tool
Morphir project for standalone executables
Clear boundaries and responsibilities
β Implement comprehensive testing
Package structure validation
Metadata correctness verification
Local installation smoke tests
Catch issues before CI deployment
β Establish changelog-driven versioning
CHANGELOG.md as single source of truth
Ionide.KeepAChangelog integration
Support pre-release versions (alpha, beta, rc)
Automated release preparation
β Dual distribution strategy
NuGet tool package for .NET developers
GitHub releases with executables for non-SDK users
Persona-based documentation
β Organize build system
Split Build.cs by domain (vertical slices)
Extract helper classes for testability
Align with Morphir.Tooling architecture
Maintainable and scalable structure
Non-Goals
β Automated pre-release version bumping (Phase 2, future work)
β TestContainers integration (Phase 3 of testing, when needed)
β Package rename/migration (Keeping current names for backward compatibility)
β Breaking changes to public APIs (Maintain compatibility)
var toolPackage = OutputDir.GlobFiles("Morphir.Tool.*.nupkg")
.FirstOrDefault();
BDD Tests:
Feature: Project structure refactor
Scenario: Build Morphir.Tool package
Given Morphir.Tool project exists
When I run "./build.sh PackTool"
Then Morphir.Tool.*.nupkg should be created
And package should contain tools/net10.0/any/morphir.dll
Scenario: Build split successfully
Given Build.cs is split into partial classes
When I run "./build.sh --help"
Then all targets should be available
And no build errors should occur
cd build
dotnet add package Ionide.KeepAChangelog --version 0.2.0
Add using statement to Build.cs:
using KeepAChangelogParser;
using Semver;
2.2 Implement Version Extraction
Create ChangelogHelper.GetVersionFromChangelog():
publicstatic SemVersion GetVersionFromChangelog(AbsolutePath changelogPath)
{
var content = File.ReadAllText(changelogPath);
var parser = new ChangelogParser();
var result = parser.Parse(content);
if (!result.IsSuccess)
thrownew Exception($"Failed to parse CHANGELOG.md: {result.Error}");
var changelog = result.Value;
var latest = changelog.SectionCollection.FirstOrDefault()
?? thrownew Exception("No releases found in CHANGELOG.md");
if (!SemVersion.TryParse(latest.MarkdownVersion, SemVersionStyles.Any, outvar version))
thrownew Exception($"Invalid version: {latest.MarkdownVersion}");
return version;
}
2.3 Implement Release Notes Extraction
Create ChangelogHelper.GetReleaseNotes():
publicstaticstring GetReleaseNotes(AbsolutePath changelogPath)
{
var content = File.ReadAllText(changelogPath);
var parser = new ChangelogParser();
var result = parser.Parse(content);
if (!result.IsSuccess) returnstring.Empty;
var latest = result.Value.SectionCollection.FirstOrDefault();
if (latest == null) returnstring.Empty;
var notes = new StringBuilder();
AppendSection("Added", latest.SubSections.Added);
AppendSection("Changed", latest.SubSections.Changed);
// ... other sectionsreturn notes.ToString();
}
publicstatic SemVersion GetNextPreReleaseVersion(AbsolutePath changelogPath)
{
var currentVersion = GetVersionFromChangelog(changelogPath);
if (!currentVersion.IsPrerelease)
thrownew Exception("Cannot auto-bump non-prerelease version");
// Extract pre-release type and number// e.g., "alpha.1" β type: "alpha", number: 1var prereleaseParts = currentVersion.Prerelease.Split('.');
var type = prereleaseParts[0]; // alpha, beta, preview, rcvar number = int.Parse(prereleaseParts.Length > 1 ? prereleaseParts[1] : "0");
// Increment number number++;
// Create new versionvar newPrerelease = $"{type}.{number}";
returnnew SemVersion(
currentVersion.Major,
currentVersion.Minor,
currentVersion.Patch,
newPrerelease
);
}
Create target for auto-bump (used in CI):
Target BumpPreRelease => _ => _
.Description("Auto-bump pre-release version (CI only)")
.Executes(() =>
{
var currentVersion = Version;
if (!currentVersion.IsPrerelease)
{
Serilog.Log.Information("Not a pre-release, skipping auto-bump");
return;
}
var nextVersion = ChangelogHelper.GetNextPreReleaseVersion(ChangelogFile);
Serilog.Log.Information("Auto-bumping {0} β {1}", currentVersion, nextVersion);
// Update CHANGELOG.md with empty section for next pre-release ChangelogHelper.AddPreReleaseSection(ChangelogFile, nextVersion.ToString());
});
BDD Tests:
Feature: Changelog-driven versioning
Scenario: Extract version from CHANGELOG
Given CHANGELOG.md has [0.2.1] - 2025-12-20When I call GetVersionFromChangelog()
Then version should be 0.2.1Scenario: Prepare release
Given CHANGELOG.md has [Unreleased] with content
When I run "./build.sh PrepareRelease --version 0.2.1"
Then CHANGELOG.md should have [0.2.1] - 2025-12-20And [Unreleased] should be empty
And changes should be staged
Scenario: Block release without content
Given CHANGELOG.md [Unreleased] is empty
When I run "./build.sh PrepareRelease --version 0.2.1"
Then build should fail
And error should mention "empty"
Feature: Build testing infrastructure
Scenario: Validate tool package structure
Given Morphir.Tool package is built
When I run package structure tests
Then all required files should be present
And tool settings should be correct
Scenario: Validate version consistency
Given all packages are built
When I run metadata tests
Then all packages should have same version
And version should match CHANGELOG.md
Scenario: Test local installation
Given tool package is in local folder
When I install tool from local source
Then installation should succeed
And morphir command should be available
Phase 4: Deployment & Distribution (2-3 days)
Goal: Update workflows for dual distribution
Tasks
4.1 Update Deployment Workflow
Update .github/workflows/deployment.yml:
name: Deploymenton:
push:
tags:
- 'v*'# Trigger on version tags (e.g., v0.2.1)workflow_dispatch:
inputs:
release_version:
description: 'Version to deploy (optional, reads from CHANGELOG if not provided)'required: falsejobs:
validate-version:
runs-on: ubuntu-latestoutputs:
version: ${{ steps.get-version.outputs.version }}steps:
- uses: actions/checkout@v4 - name: Get version from CHANGELOGid: get-versionrun: | # Extract from tag name (v0.2.1 β 0.2.1)
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/v}
echo "version=$VERSION" >> $GITHUB_OUTPUT
elif [[ -n "${{ github.event.inputs.release_version }}" ]]; then
echo "version=${{ github.event.inputs.release_version }}" >> $GITHUB_OUTPUT
else
echo "No version specified"
exit 1
fi - name: Validate version in CHANGELOGrun: | VERSION=${{ steps.get-version.outputs.version }}
if ! grep -q "\[$VERSION\]" CHANGELOG.md; then
echo "Version $VERSION not found in CHANGELOG.md"
exit 1
fibuild-executables:
needs: validate-version# ... existing build-executables jobs ...release:
needs: [validate-version, build-executables]runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4 - name: Setup .NET SDKuses: actions/setup-dotnet@v4with:
global-json-file: global.json - name: Restore dependenciesrun: ./build.sh Restore - name: Buildrun: ./build.sh Compile - name: Run testsrun: ./build.sh TestAll # Includes build tests! - name: Download executablesuses: actions/download-artifact@v4 - name: Pack packagesrun: ./build.sh PackAll - name: Run build testsrun: ./build.sh TestBuild - name: Publish to NuGetrun: ./build.sh PublishAll --api-key ${{ secrets.NUGET_TOKEN }}env:
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}create-github-release:
needs: [validate-version, build-executables, release]runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4 - name: Download executablesuses: actions/download-artifact@v4with:
path: artifacts/executables - name: Extract release notes from CHANGELOGid: release-notesrun: | VERSION=${{ needs.validate-version.outputs.version }}
# Extract section for this version from CHANGELOG.md
awk '/## \['$VERSION'\]/,/## \[/ {print}' CHANGELOG.md | head -n -1 > release-notes.md - name: Create GitHub Releaseuses: softprops/action-gh-release@v1with:
tag_name: v${{ needs.validate-version.outputs.version }}name: Release v${{ needs.validate-version.outputs.version }}body_path: release-notes.mdfiles: | artifacts/executables/morphir-*
artifacts/executables/morphir.exeenv:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Target PublishTool => _ => _
.DependsOn(PackTool)
.Description("Publish Morphir.Tool to NuGet.org")
.Executes(() =>
{
if (string.IsNullOrEmpty(ApiKey))
thrownew Exception("API_KEY required");
var toolPackage = OutputDir.GlobFiles("Morphir.Tool.*.nupkg")
.FirstOrDefault();
if (toolPackage == null)
thrownew Exception($"Morphir.Tool package not found in {OutputDir}");
Serilog.Log.Information($"Publishing {toolPackage}");
DotNetNuGetPush(s => s
.SetTargetPath(toolPackage)
.SetSource(NuGetSource)
.SetApiKey(ApiKey)
.SetSkipDuplicate(true));
});
BDD Tests:
Feature: Deployment workflow
Scenario: Deploy on tag push
Given tag v0.2.1 is pushed
When deployment workflow runs
Then version should be extracted from CHANGELOG.md
And packages should be built
And build tests should run
And packages should be published to NuGet
And executables should be uploaded to GitHub release
Scenario: Block deployment if version not in CHANGELOG
Given tag v0.2.2 is pushed
But CHANGELOG.md doesn't have [0.2.2]
When deployment workflow runs
Then workflow should fail
And no packages should be published
Phase 5: Documentation (1-2 days)
Goal: Comprehensive documentation for all stakeholders
Tasks
5.1 Update AGENTS.md
Add section: “Build System Configuration”
## Build System Configuration
### Nuke Parameters
The build system uses Nuke with these parameters:
-`--configuration`: Build configuration (Debug/Release)
-`--version`: Version override (reads from CHANGELOG.md by default)
-`--api-key`: NuGet API key for publishing
-`--nuget-source`: NuGet source URL
-`--skip-tests`: Skip test execution
### Environment Variables
-`NUGET_TOKEN`: NuGet API key (CI only)
-`CONFIGURATION`: Build configuration override
-`MORPHIR_EXECUTABLE_PATH`: E2E test executable path
Add section: “Changelog-Driven Versioning”
## Changelog-Driven Versioning
Morphir uses CHANGELOG.md as the single source of truth for versions.
### Version Format
Follows [Semantic Versioning](https://semver.org/):
-`MAJOR.MINOR.PATCH` for releases (e.g., `0.2.1`)
-`MAJOR.MINOR.PATCH-TYPE.NUMBER` for pre-releases (e.g., `0.2.1-beta.2`)
Supported pre-release types: alpha, beta, preview, rc
### Release Preparation Workflow
1. During development, add changes to `[Unreleased]` section
2. When ready to release, run: `./build.sh PrepareRelease --version X.Y.Z`3. Review staged changes: `git diff --staged`4. Commit: `git commit -m "chore: prepare release X.Y.Z"`5. Create release branch: `git checkout -b release/X.Y.Z`6. Push and create PR to main
7. After PR merge, create tag: `git tag -a vX.Y.Z -m "Release X.Y.Z"`8. Push tag: `git push origin vX.Y.Z` (triggers deployment)
Add section: “Dual Distribution Strategy”
## Dual Distribution Strategy
Morphir provides two distribution channels:
### NuGet Tool Package (Morphir.Tool)
**For**: .NET developers with SDK installed
**Install**: `dotnet tool install -g Morphir.Tool`**Update**: `dotnet tool update -g Morphir.Tool`**Command**: `morphir`### Platform Executables
**For**: Shell scripts, containers, non-.NET environments
**Install**: Use install scripts or download from GitHub releases
**Platforms**: linux-x64, linux-arm64, win-x64, osx-arm64
**Command**: `morphir` or `./morphir-{platform}`
5.2 Update CLAUDE.md
Add build organization guidance
Document PrepareRelease workflow
Add testing requirements
Update commit message examples
5.3 Update README.md
Add persona-based installation instructions:
## Installation
### For .NET Developers
If you have the .NET SDK installed:
```bash
dotnet tool install -g Morphir.Tool
morphir --version
For Shell Scripts / Containers
If you don’t have .NET SDK or need a standalone executable:
Feature: Morphir Tool Installation
As a .NET developer
I want to install Morphir as a dotnet tool
So that I can use it in my development workflow
Scenario: Install from NuGet
Given I am a .NET developer with SDK installed
When I run "dotnet tool install -g Morphir.Tool"
Then the tool should install successfully
And I should be able to run "morphir --version"
And the version should match CHANGELOG.md
Scenario: Update tool
Given Morphir.Tool is already installed
When I run "dotnet tool update -g Morphir.Tool"
Then the tool should update successfully
And the new version should be active
Feature: Morphir Executable Download
As a shell script user
I want to download a standalone executable
So that I can use Morphir without installing .NET SDK
Scenario: Download from GitHub releases
Given I am using a minimal container
When I download morphir-linux-x64 from GitHub releases
Then I should be able to run "./morphir-linux-x64 --version"
And the version should match CHANGELOG.md
Scenario: Install via script
Given I have curl available
When I run the install script
Then morphir should be installed to /usr/local/bin
And morphir command should be in PATH
BDD Tests:
Feature: Documentation completeness
Scenario: All distribution methods documented
Given README.md exists
When I read installation instructions
Then I should see dotnet tool installation
And I should see executable download instructions
And I should see persona-based recommendations
Scenario: Release process documented
Given AGENTS.md exists
When I read the release preparation section
Then I should see PrepareRelease workflow
And I should see tag creation steps
And I should see deployment trigger explanation
BDD Acceptance Criteria
Epic-Level Scenarios
Feature: Morphir Deployment Architecture
As a Morphir maintainer
I want a robust deployment architecture
So that releases are reliable and users can install easily
Background: Given the morphir-dotnet repository is up to date
And all dependencies are installed
Scenario: Successful deployment to NuGet and GitHub
Given CHANGELOG.md has [0.2.1] - 2025-12-20And all changes are committed
When I create and push tag v0.2.1Then deployment workflow should complete successfully
And Morphir.Tool.0.2.1.nupkg should be published to NuGet.org
And Morphir.Core.0.2.1.nupkg should be published to NuGet.org
And Morphir.Tooling.0.2.1.nupkg should be published to NuGet.org
And morphir-linux-x64 should be in GitHub release v0.2.1And morphir-win-x64 should be in GitHub release v0.2.1And morphir-osx-arm64 should be in GitHub release v0.2.1And release notes should match CHANGELOG.md
Scenario: Build tests catch package issues
Given I modify package structure incorrectly
When I run "./build.sh TestBuild"
Then tests should fail
And I should see clear error message
And CI deployment should be blocked
Scenario: Version consistency across packages
Given I prepare release 0.2.1When I build all packages
Then all packages should have version 0.2.1And version should match CHANGELOG.md [0.2.1]
And all package release notes should match
Scenario: .NET developer installation
Given Morphir.Tool is published to NuGet
When .NET developer runs "dotnet tool install -g Morphir.Tool"
Then tool should install successfully
And "morphir --version" should work
And version should match published version
Scenario: Container user installation
Given morphir-linux-x64 is in GitHub releases
When container user downloads executable
Then "./morphir-linux-x64 --version" should work
And version should match release version
And no .NET SDK should be required
Product Requirements Document for layered Morphir configuration and workspace support
Product Requirements Document: Layered Configuration and Workspaces
Status: π Draft Created: 2025-12-22 Last Updated: 2025-12-22 Author: Morphir .NET Team
Overview
Introduce a layered configuration system for Morphir tooling with global and workspace-scoped TOML files, optional user and CI overlays, and standardized cache path resolution. Centralize configuration models in a new F# project (Morphir.Configuration) so all tools can share the same domain types. Morphir.Tooling will reference Morphir.Configuration and provide resolver and IO services.
Problem Statement
Morphir tooling lacks a consistent configuration mechanism for workspace-scoped settings, user-specific overrides, and CI-specific behavior. This results in scattered, ad-hoc configuration approaches, inconsistent cache locations, and poor ergonomics for CLI usage in CI/CD environments.
Goals
Provide layered configuration with deterministic precedence across global, workspace, user, and CI overlays.
Define workspace discovery rules and standard config file locations.
Centralize configuration domain models in Morphir.Configuration (F#).
Expose a resolver in Morphir.Tooling with a clear API for consumers.
Document configuration files, precedence, and CI activation behavior.
Non-Goals
Implementing cache read/write behavior (only path resolution and configuration).
Introducing new CLI commands beyond config selection and CI activation flags.
Complex schema validation beyond basic TOML parsing and sanity checks.
Breaking compatibility with existing tooling workflows without migration guidance.
User Stories
Story 1: Workspace Configuration
As a developer I want workspace-level Morphir configuration in .morphir/morphir.toml So that I can keep project settings out of the repository root
Story 2: Personal Overrides
As a developer I want a local override file (.morphir/morphir.user.toml) So that I can keep personal settings out of version control
Story 3: CI Profiles
As a CI pipeline I want a CI overlay (.morphir/morphir.ci.toml) So that CI-specific settings apply only when needed
Story 4: Global Defaults
As a developer I want global defaults in OS-standard config locations So that I can reuse defaults across repositories
Detailed Requirements
Functional Requirements
FR-1: Layered Precedence
Load configuration in the following order (lowest to highest precedence):
Global config (OS-standard path)
Workspace config: .morphir/morphir.toml
User override: .morphir/morphir.user.toml (optional)
CI override: .morphir/morphir.ci.toml (optional, conditional)
FR-2: Workspace Root Discovery
Workspace root is discovered by:
VCS root (Git) when available.
If no VCS root is found, the nearest .morphir/ directory when walking up from the current directory.
If neither is found, treat as no workspace configuration.
Log selection decisions and conflicts (e.g., when .morphir/ exists below VCS root).
Linux: $XDG_CONFIG_HOME/morphir or ~/.config/morphir
macOS: ~/Library/Application Support/morphir
Global config file name: morphir.toml.
FR-5: Cache Paths (Resolution Only)
Expose resolved cache paths:
Workspace cache: .morphir/cache/ (overridable by config)
Global cache: OS-standard cache dir (overridable by config)
No caching behavior is implemented in this phase.
FR-6: Shared Domain Models
Create a new F# project:
src/Morphir.Configuration/ containing domain models and pure configuration types
tests/Morphir.Configuration.Tests/ containing unit tests for models and parsing behavior
Morphir.Tooling references Morphir.Configuration and provides the resolver and IO boundary.
Non-Functional Requirements
Deterministic merging behavior with explicit precedence.
Minimal dependencies; avoid heavy configuration frameworks.
Respect CLI logging rules (stdout reserved for command output; diagnostics to stderr).
Keep domain models immutable and free of IO.
Proposed Architecture
Projects
Morphir.Configuration (F#)
Config models (records, DU types)
Pure merge logic
CI activation options and detection helpers (pure, env injected)
Morphir.Tooling (C# / F#)
Config loader/resolver
Workspace discovery
TOML parsing and file IO
Public API Sketch (Morphir.Configuration)
typeCiProfileMode=| On
| Off
| Auto
typeCachePaths={ WorkspaceCache:string option
GlobalCache:string option }typeMorphirConfig={ Cache: CachePaths
// Additional fields as needed
}typeConfigLayer={ Path:string Config: MorphirConfig }typeConfigResolution={ Effective: MorphirConfig
Layers: ConfigLayer list WorkspaceRoot:string option
CiProfileApplied:bool}
Testing Strategy
Morphir.Configuration.Tests
Merge precedence and overrides
Optional fields and missing values
CI activation mode handling (with injected env map)
Morphir.Tooling.Tests
Global path selection per OS (parameterized)
Workspace discovery rules
Layered load behavior with missing optional files
CI activation flag (on/off/auto) and detection
Documentation Requirements
New documentation page describing config locations, precedence, and CI behavior.
Update troubleshooting doc with config resolution guidance.
Add .morphir/morphir.user.toml and cache paths to git-ignore guidance.
Minimal TOML Schema (v1)
morphir.toml supports optional project, workspace, and morphir sections.
morphir is optional and contains dist, tools, and extensions subsections (defaults apply when omitted).
workspace.projects accepts an array of project globs for monorepo layouts.
workspace.outputDir defaults to ${WorkspaceHome}/out/.
WorkspaceHome defaults to the .morphir/ folder at the workspace root and is overridable via config.
project defaults to supporting the properties currently available in the Morphir project file (morphir.json in finos/morphir-elm).
Feature Status Tracking
Feature
Status
Notes
Morphir.Configuration project + tests
β³ Planned
New F# domain project
Configuration model definitions
β³ Planned
Records/DU types + merge logic
Workspace discovery
β³ Planned
.morphir/ and VCS root
Layered resolver in Morphir.Tooling
β³ Planned
IO boundary + merge
CI profile activation
β³ Planned
on/off/auto + env detection
Cache path resolution
β³ Planned
Expose effective paths
Documentation updates
β³ Planned
CLI and troubleshooting
Implementation Notes
Add implementation notes here as decisions are made.
Start with a morphir.toml that supports optional project and workspace sections.
Add an optional morphir section containing dist, tools, and extensions subsections (defaults apply when omitted).
workspace.projects accepts an array of project globs for monorepo layouts.
workspace.outputDir defaults to ${WorkspaceHome}/out/.
WorkspaceHome defaults to the .morphir/ folder at the workspace root and is overridable via config.
project defaults to supporting the properties currently available in the Morphir project file (morphir.json in finos/morphir-elm).
6 - Vulnerability Resolver Skill Requirements
Product requirements for the Vulnerability Resolver skill - automated CVE detection, resolution, and suppression
Vulnerability Resolver Skill Requirements
Executive Summary
The Vulnerability Resolver skill provides automated assistance for managing security vulnerabilities detected by OWASP Dependency-Check. It enables developers to efficiently triage, fix, or suppress CVEs while maintaining a documented audit trail of security decisions.
Background
Context
FINOS active projects require CVE scanning alongside Dependabot. morphir-dotnet implemented OWASP Dependency-Check scanning in PR #273, which runs:
On push/PR to main
Weekly on Monday at 3:00 UTC
Fails builds on CVSS score >= 7
PR #276 addressed initial vulnerabilities, identifying that some reported CVEs were false positives due to binary scanning misidentification of package versions or confusion with similarly-named packages.
Problem Statement
When dependency scanning detects vulnerabilities:
Developers must manually research each CVE to determine if it’s genuine or a false positive
There’s no standardized process for documenting suppression decisions
Suppression files must be manually created following OWASP Dependency-Check XML schema
No easy way to trigger scans on specific branches during development
No guided workflow for fix vs. suppress decisions
Success Criteria
Automation: Reduce manual effort for vulnerability resolution by 70%
Documentation: 100% of suppressions have documented rationale
Auditability: Clear audit trail for all security decisions
Developer Experience: Interactive prompts guide users through resolution
CI Integration: Ability to trigger scans on any branch
Functional Requirements
FR-1: Scan Triggering
FR-1.1: Trigger dependency-check workflow on any branch
# Example invocation@skill vulnerability-resolver
Scan branch feature/new-dependency for vulnerabilities
FR-1.2: Support manual workflow dispatch with parameters:
Branch/ref to scan
Fail threshold (CVSS score, default 7)
Output format (HTML, JSON, XML)
Suppression file path
FR-1.3: Report scan status and provide link to workflow run
FR-2: Vulnerability Analysis
FR-2.1: Parse dependency-check reports (HTML, JSON, XML formats)
FR-2.2: For each vulnerability, extract:
CVE identifier
CVSS score and severity
Affected package/file
Package identifier (purl, CPE)
Description and references
Whether it’s a transitive dependency
FR-2.3: Categorize vulnerabilities by:
Severity (Critical, High, Medium, Low)
Fix availability (update available, no fix, N/A)
False positive likelihood (based on patterns)
FR-3: Interactive Resolution
FR-3.1: Present vulnerabilities with resolution options:
CVE-2022-4742 (CVSS 9.8) in JsonPointer.Net@6.0.0
Options:
1. Fix: Update to version 6.0.1 (recommended)
2. Suppress: Mark as false positive with reason
3. Skip: Handle later
4. Research: Open CVE details in browser
FR-3.2: For each resolution choice:
Fix: Generate package update commands, verify fix in scan
Suppress: Create/update suppression XML with documented rationale
Skip: Track for follow-up, don’t block
FR-3.3: Detect false positive patterns:
Version misidentification in binary scanning
Package name confusion (e.g., Cecil vs Mono.Cecil)
Already-fixed transitive dependencies
Suggest suppression when patterns match
FR-4: Suppression Management
FR-4.1: Create and manage suppression file (dependency-check-suppressions.xml)
FR-4.2: Suppression file structure following OWASP schema:
FR-5.1: Generate fix commands for different package managers:
# NuGet (Directory.Packages.props)# Update JsonPointer.Net from 6.0.0 to 6.0.1# In Directory.Packages.props:<PackageVersion Include="JsonPointer.Net" Version="6.0.1" />
FR-5.2: Verify fix effectiveness:
Check if new version resolves CVE
Warn if update introduces breaking changes
Validate update doesn’t introduce new CVEs
FR-5.3: Handle transitive dependencies:
Identify which direct dependency pulls the vulnerable package
Suggest upgrade path
Note when fix requires waiting for upstream update
FR-6: Reporting and Documentation
FR-6.1: Generate resolution summary:
## Vulnerability Resolution Summary
**Scan Date**: 2024-01-15
**Branch**: main
**Total Vulnerabilities**: 4
### Fixed (1)
- CVE-2022-4742 in JsonPointer.Net: Updated 6.0.0 β 6.0.1
### Suppressed (3)
- CVE-2023-36415 in Azure.Identity: Already fixed in 1.17.1 (transitive)
- CVE-2023-4914 in Mono.Cecil.Mdb: False positive (different package)
- CVE-2012-2055 in Octokit: Not applicable to this library
### Pending (0)
None
FR-6.2: Maintain resolution history for audit purposes
FR-6.3: Generate PR description for vulnerability fixes
Non-Functional Requirements
NFR-1: Security
Never expose actual vulnerability details in logs
Suppression decisions must be committed to version control
Support for security team review workflow
NFR-2: Performance
Skill invocation < 5 seconds for analysis
Report parsing < 10 seconds for typical reports
No impact on regular CI pipeline speed
NFR-3: Maintainability
Follow existing skill template patterns
Reusable scripts for automation
Clear documentation for manual fallback
NFR-4: Auditability
All suppressions traceable to commits
Suppression history preserved
Quarterly review reminders
Technical Design
Workflow Modifications
Update .github/workflows/cve-scanning.yml to support:
on:
push:
branches: [main]pull_request:
branches: [main]schedule:
- cron: '0 3 * * 1'workflow_dispatch:
inputs:
branch:
description: 'Branch to scan'required: falsedefault: 'main'fail-cvss:
description: 'Fail on CVSS score >= N'required: falsedefault: '7'suppression-file:
description: 'Path to suppression file'required: falsedefault: './dependency-check-suppressions.xml'
QA Tester Skill: Coordinate for regression testing after dependency updates
Release Manager Skill: Ensure no unresolved vulnerabilities before release
AOT Guru Skill: Verify dependency updates don’t break AOT compatibility
User Stories
US-1: Developer Fixes Vulnerability
As a developer, when the dependency check fails, I want to quickly identify which vulnerabilities are genuine and how to fix them so I can unblock my PR.
US-2: Security Review for False Positive
As a developer, when I identify a false positive, I want to suppress it with proper documentation so future scans don’t flag the same issue.
US-3: Pre-merge Vulnerability Check
As a developer, I want to check my branch for vulnerabilities before creating a PR so I can address issues proactively.
US-4: Quarterly Security Review
As a maintainer, I want to review all active suppressions quarterly to ensure they’re still valid and no fixes have become available.
US-5: Audit Trail
As a security auditor, I want to see a complete history of vulnerability decisions so I can verify the project follows security best practices.
Implementation Phases
Phase 1: Core Infrastructure (MVP)
Update workflow for manual dispatch
Create suppression file with initial false positives
Basic skill definition with manual resolution workflow