Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ morphir-python/
`morphir.ir.format_version`, convert only through `morphir.ir.migrate`, and keep
diagnostic codes in `morphir.ir.diagnostics`. See
[packages/morphir/README.md](packages/morphir/README.md#morphir-ir-versions) and
`openspec/changes/add-ir-v3-support/`
the `ir/*` and `tools/ir-interchange` specs in `openspec/specs/`
- Type definitions and type algebra
- Pure functional primitives
- Morphir SDK runtime (`morphir.sdk`): Basics, Char, String, List, Dict, Set, Maybe, Result, Tuple, Decimal, Int, Number
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,10 @@ morphir-tools ir convert morphir-ir.json --to v4 -o morphir-ir.v4.json
```

See the [morphir package README](packages/morphir/README.md#morphir-ir-versions)
for format-version handling, the draft v4 status, and migrations, and the
[`add-ir-v3-support` change](openspec/changes/add-ir-v3-support/proposal.md) for
the design.
for format-version handling, the draft v4 status, and migrations, the
[IR specs](openspec/specs/) for the requirements, and the archived
[`add-ir-v3-support` change](openspec/changes/archive/2026-10-27-add-ir-v3-support/proposal.md)
for the design.

## Morphir SDK

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
Code goes in <pre><code class="language-python"> (or json, toml, bash, diff). Give sections an id and
list them in nav.toc.
-->
<link rel="stylesheet" href="../../../docs/templates/assets/agent-document.css">
<link rel="stylesheet" href="../../../docs/templates/assets/highlight/styles/github.min.css" media="(prefers-color-scheme: light)">
<link rel="stylesheet" href="../../../docs/templates/assets/highlight/styles/github-dark.min.css" media="(prefers-color-scheme: dark)">
<script defer src="../../../docs/templates/assets/highlight/highlight.min.js"></script>
<script defer src="../../../docs/templates/assets/agent-document.js"></script>
<link rel="stylesheet" href="../../../../docs/templates/assets/agent-document.css">
<link rel="stylesheet" href="../../../../docs/templates/assets/highlight/styles/github.min.css" media="(prefers-color-scheme: light)">
<link rel="stylesheet" href="../../../../docs/templates/assets/highlight/styles/github-dark.min.css" media="(prefers-color-scheme: dark)">
<script defer src="../../../../docs/templates/assets/highlight/highlight.min.js"></script>
<script defer src="../../../../docs/templates/assets/agent-document.js"></script>
</head>
<body>
<main>
Expand Down
57 changes: 57 additions & 0 deletions openspec/specs/ir/format-version/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# ir/format-version Specification

## Purpose
Defines how morphir-python reads, normalizes, checks, and writes the `formatVersion` field of a versioned Morphir IR document, so every codec and the CLI reject unsupported input the same way, with stable diagnostic codes, before any distribution decoding starts.

## Requirements

### Requirement: Permissive format version recognition
The reader SHALL accept a JSON integer `N`, a string `"N"`, and a string `"N.minor.patch"`, and normalize each to an exact release (`3`, `"3"` and `"3.0.0"` all mean 3.0.0). Components MUST be unsigned 32-bit base-10 integers with no leading zeros. Booleans, null, floats, negatives, collections, whitespace, prerelease and build suffixes MUST be rejected. (bead: morphir-ukvn.8)

#### Scenario: Integer and string spellings normalize alike
- **WHEN** documents declare `formatVersion` as `3`, `"3"`, and `"3.0.0"`
- **THEN** each normalizes to release 3.0.0

#### Scenario: Boolean is not an integer
- **WHEN** a document declares `formatVersion` as `true`
- **THEN** recognition fails with `invalid_format_version_type`

#### Scenario: Malformed release string
- **WHEN** a document declares `formatVersion` as `"3.0.0-alpha"` or `" 3.0.0"`
- **THEN** recognition fails with `invalid_format_version_syntax`

### Requirement: Divergence from morphir-elm and the finos contract is deliberate
Accepting string spellings SHALL be documented as a deliberate divergence from morphir-elm, whose decoder accepts only an integer, and accepting `"N"` as a divergence from the finos/morphir contract, which accepts only integer `N` or `"N.minor.patch"`. Both exist to read files written by other tools during migration. (bead: morphir-ukvn.8)

#### Scenario: Divergence is visible to users
- **WHEN** a user reads the package documentation for format version handling
- **THEN** it states which accepted spellings morphir-elm and the finos/morphir contract would reject

### Requirement: Support table is checked before decoding
The reader SHALL declare the support table `[3.0.0,3.1.0),[4.0.0,4.1.0)` and MUST finish recognition and the support check before invoking any distribution decoder or migration. A release outside the table SHALL fail with `unsupported_format_version_major` or `unsupported_format_version_minor`. (bead: morphir-ukvn.8)

#### Scenario: IR 3.1.0 is refused for now
- **WHEN** a document declares `formatVersion` `"3.1.0"`
- **THEN** reading fails with `unsupported_format_version_minor` before the distribution is decoded

#### Scenario: Historical versions are refused
- **WHEN** a document declares `formatVersion` `1` or `2`
- **THEN** reading fails with `unsupported_format_version_major` and a message that formats 1 and 2 are follow-up work

### Requirement: Stable format version diagnostics
Format-version failures SHALL use the finos/morphir contract codes: `missing_format_version`, `duplicate_format_version`, `invalid_format_version_type`, `invalid_format_version_syntax`, `format_version_out_of_range`, `unsupported_format_version_major`, and `unsupported_format_version_minor`. (bead: morphir-ukvn.8)

#### Scenario: Missing version
- **WHEN** the document root has no `formatVersion` member
- **THEN** reading fails with `missing_format_version`

#### Scenario: Duplicate version member
- **WHEN** the document root contains `formatVersion` twice
- **THEN** reading fails with `duplicate_format_version` rather than keeping the last value

### Requirement: Canonical format version output
Writers SHALL emit `formatVersion` first, as the integer `N` for a baseline release `N.0.0` and as the exact release string otherwise, regardless of the spelling that was read. (bead: morphir-ukvn.8)

#### Scenario: Read string, write integer
- **WHEN** a document read with `formatVersion` `"3.0.0"` is written again
- **THEN** the output declares `formatVersion` as the integer `3`
69 changes: 69 additions & 0 deletions openspec/specs/ir/migrate/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# ir/migrate Specification

## Purpose
Defines migrations between Morphir IR format 3.0.0 and IR v4, including how attributes, documentation, and package versions map, so programs move forward safely and reverse migration fails explicitly when v4 content cannot be represented in v3.

## Requirements

### Requirement: Forward migrate v3 to v4
The library SHALL migrate a valid v3 Library to a v4 Library. Unit type attributes SHALL become empty v4 type attributes; each v3 value attribute type SHALL become the v4 `inferred_type` with no source and no extensions; `WholeNumberLiteral` SHALL become `IntegerLiteral`. The caller MAY supply a package release version; otherwise it is absent. (bead: morphir-ukvn.8)

#### Scenario: Shared Library content migrates forward
- **WHEN** a v3 Library is migrated to v4
- **THEN** every v4 definition matches its source, and every value and pattern carries the source type as `inferred_type`

#### Scenario: Package version comes from the caller
- **WHEN** a v3 Library is migrated with package version `1.2.0`
- **THEN** the v4 package identity carries `1.2.0`, and without that argument the version is absent

### Requirement: Reverse migrate v4 to v3 with known failure paths
The library SHALL migrate v4 to v3. When the input uses concepts v3 cannot represent, migration SHALL fail with structured errors and MUST NOT invent or drop IR content, except as allowed by the dropped-information requirement. (bead: morphir-ukvn.8)

#### Scenario: Specs and Application do not downgrade
- **WHEN** a v4 Specs or Application distribution is migrated to v3
- **THEN** migration fails with `unsupported_v4_downgrade` naming the distribution kind

#### Scenario: V4-only nodes are reported
- **WHEN** a v4 Library contains a `Hole`, `Native`, or `External` value, or a `DocumentLiteral`
- **THEN** migration fails with `unsupported_v4_downgrade` naming the construct and its location

#### Scenario: Missing types are reported
- **WHEN** a v4 value, pattern, or value-definition input has no `inferred_type`
- **THEN** migration fails with `missing_inferred_type` at that location

#### Scenario: Semantic attributes are reported
- **WHEN** v4 type attributes carry a source, constraints, or extensions, or value attributes carry extensions
- **THEN** migration fails with `unsupported_attributes_downgrade`

#### Scenario: Absent member documentation is reported
- **WHEN** a v4 type or value member has no documentation
- **THEN** migration fails with `unsupported_documentation_downgrade`, because v3 member documentation is a required string

#### Scenario: Compatible Library downgrades
- **WHEN** a v4 Library uses only concepts v3 can represent
- **THEN** migration succeeds with a v3 Library

### Requirement: Dropped information is limited and reported
Reverse migration SHALL drop the package release version, which v3 cannot hold, and report `package_version_dropped`. Value source locations SHALL fail with `unsupported_attributes_downgrade` unless the caller opts in to dropping them, which reports `source_location_dropped`. Nothing else MAY be dropped. (bead: morphir-ukvn.8)

#### Scenario: Package version is dropped with a warning
- **WHEN** a compatible v4 Library with package version `1.2.0` is migrated to v3
- **THEN** migration succeeds and reports `package_version_dropped`

#### Scenario: Source locations need opt-in
- **WHEN** a v4 value carries a source location and the caller did not opt in to dropping it
- **THEN** migration fails with `unsupported_attributes_downgrade`

### Requirement: Shared subset round trips
For any valid v3 Library, migrating to v4 and back to v3 SHALL succeed and SHALL yield a Library semantically equal to the original. (bead: morphir-ukvn.8)

#### Scenario: Elm fixture survives v3 to v4 to v3
- **WHEN** a golden morphir-elm format-3 fixture is migrated to v4 and back
- **THEN** the result is semantically equal to the decoded fixture and reports only `package_version_dropped` if a version was supplied

### Requirement: Migration results are explicit
Migration APIs SHALL return a success value with any warnings, or a failure holding one or more diagnostics. Each diagnostic SHALL expose a stable code, the construct name, and a location path. Codes SHALL reuse upstream codes where they exist. (bead: morphir-ukvn.8)

#### Scenario: Failure is machine-readable
- **WHEN** reverse migration fails
- **THEN** callers can read each diagnostic's code, construct, and location without parsing message text
54 changes: 54 additions & 0 deletions openspec/specs/ir/v3/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# ir/v3 Specification

## Purpose
Provides a first-class Morphir IR format 3.0.0 model and JSON codecs in Python, with morphir-elm as the normative source, so tooling can load and produce the IR that morphir-elm emits today.

## Requirements

### Requirement: morphir-elm is normative for v3 content
The v3 model and JSON shapes SHALL follow morphir-elm `Morphir.IR.*` types and `*.Codec` modules for format 3.0.0. Where finos/morphir v3 schemas or docs disagree with morphir-elm on 3.0.0 content, morphir-elm MUST win. (bead: morphir-ukvn.8)

#### Scenario: Elm output decodes
- **WHEN** a `morphir-ir.json` written by morphir-elm `morphir make` is decoded
- **THEN** decoding succeeds without a Python-specific JSON variant

### Requirement: V3 model mirrors morphir-elm concepts
The library SHALL expose a v3 model whose concepts match morphir-elm: names as word lists, paths, qualified and fully qualified names, access control, documented values, literals, types, values, patterns, modules, packages, and the `Library` distribution. A v3 `Library` SHALL carry type attributes of unit and value attributes of a v3 type, as in elm. (bead: morphir-ukvn.8)

#### Scenario: Library distribution is representable
- **WHEN** a caller constructs a v3 Library with a package name, dependency specifications, and a package definition whose values carry types
- **THEN** the model keeps those parts without any v4-only construct

#### Scenario: Library has no package version
- **WHEN** a caller constructs a v3 Library
- **THEN** the model has no package release version, because format 3.0.0 has none

### Requirement: V3 JSON matches morphir-elm format 3.0.0
The library SHALL encode and decode versioned distribution JSON with the shapes of morphir-elm `Morphir.IR.FormatVersion.Codec` and the codecs it uses. Node codecs SHALL take attribute codecs as parameters, as elm does. Version recognition SHALL use the shared format-version reader. (bead: morphir-ukvn.8)

#### Scenario: Round trip versioned morphir-ir.json
- **WHEN** a valid morphir-elm `morphir-ir.json` with `formatVersion` 3 is decoded and encoded again
- **THEN** the output declares `formatVersion` 3 and its distribution is semantically equal to the input

#### Scenario: String version is accepted on read
- **WHEN** an otherwise valid format-3 document declares `formatVersion` `"3.0.0"`
- **THEN** it decodes, and encoding writes the integer `3`

#### Scenario: Wrong family at the v3 entry point
- **WHEN** a document with `formatVersion` 4 is passed to the v3 decoder
- **THEN** decoding fails with `unsupported_format_version_major` naming the found version and that only 3.0.x is accepted

#### Scenario: Unknown distribution kind
- **WHEN** a format-3 document has a distribution tag other than `Library`
- **THEN** decoding fails with a structured error naming the tag

### Requirement: V3 is independently usable
Callers SHALL be able to construct, inspect, encode, and decode v3 IR without converting to v4, and v3 classes SHALL be distinct from v4 classes. (bead: morphir-ukvn.8)

#### Scenario: Inspect without migration
- **WHEN** a caller loads a format-3 distribution
- **THEN** they can read package, module, type, and value structure through the v3 API alone

#### Scenario: Versions do not mix
- **WHEN** a caller passes a v4 node where the v3 model expects a v3 node
- **THEN** static type checking reports an error
53 changes: 53 additions & 0 deletions openspec/specs/ir/v4/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# ir/v4 Specification

## Purpose
Keeps Morphir IR v4 as a first-class Python model alongside v3, and adds a draft v4 JSON codec, while stating plainly that upstream IR v4 is not yet stable and that this surface will follow upstream changes.

## Requirements

### Requirement: V4 remains a first-class IR surface
The library SHALL continue to expose the current IR v4 model (names, paths, types, values, modules, packages, and the Library, Specs, and Application distributions) as a first-class API usable without converting to v3. (bead: morphir-ukvn.8)

#### Scenario: V4 types stay available
- **WHEN** a caller imports the v4 IR surface
- **THEN** they can construct Library, Specs, and Application distributions and the existing v4 type and value structures

### Requirement: V4 package identity is explicit
The library SHALL make IR v4 reachable at `morphir.ir.v4`. `morphir.ir` and its existing submodule paths SHALL keep working and SHALL re-export the same class objects, so identity and `isinstance` checks agree across paths. (bead: morphir-ukvn.8)

#### Scenario: Versioned and compatibility imports agree
- **WHEN** a caller imports a v4 class from `morphir.ir.v4` and the same class from `morphir.ir` or an existing submodule such as `morphir.ir.name`
- **THEN** both names refer to the same class object

### Requirement: Package release version is optional
The v4 package identity SHALL hold a package name and an optional release version. A missing version SHALL be valid, because v4 JSON has no package-version member and release versions belong to distribution bindings. (bead: morphir-ukvn.8)

#### Scenario: Unversioned package
- **WHEN** a caller constructs a v4 Library without a package release version
- **THEN** construction succeeds and the version reads as absent

### Requirement: Draft v4 JSON codec
The library SHALL encode and decode v4 versioned distributions as `formatVersion` first and a distribution wrapped by its kind (`Library`, `Specs`, `Application`) using finos/morphir v4 JSON profile spellings at a pinned commit. The codec MUST be documented as draft and MUST NOT write morphir-elm format-3 shapes. (bead: morphir-ukvn.8)

#### Scenario: V4 round trip
- **WHEN** a v4 Library built only from constructs the codec supports is encoded and decoded
- **THEN** the result equals the original, except that the package release version is absent

#### Scenario: V4 encode is not format 3
- **WHEN** a v4 distribution is encoded
- **THEN** the output declares `formatVersion` 4 and uses no format-3 tagged-array distribution shape

#### Scenario: Unsupported spelling is refused
- **WHEN** v4 input contains a node kind or member the codec does not recognize
- **THEN** decoding fails with a structured error that names the node and its location, and does not guess

#### Scenario: Construct with no draft spelling
- **WHEN** a caller encodes a v4 value that has no spelling in the pinned v4 JSON profile, such as the Python `Native` or `External` value nodes
- **THEN** encoding fails with a structured error naming the construct rather than dropping it

### Requirement: Draft status is explicit
Documentation for the v4 model and codec SHALL state that IR v4 is a draft upstream, name the pinned finos/morphir commit, and state that spellings may change in later minor releases of this package. (bead: morphir-ukvn.8)

#### Scenario: Draft notice is present
- **WHEN** a user reads the v4 codec documentation
- **THEN** it identifies v4 as draft and names the pinned upstream commit
Loading
Loading