IR 3.1.0 and v3 document trees
Status: Implemented in morphir-rust (finos/morphir-rust#256 and #257) and in the morphir CLI, and recorded in decision 0018. The normative text is now Document Tree File Formats (Version 3), the 3.1.0 section of What's New in Version 3, the v3 JSON Schema, and the format-version page. This draft stays as the design record. Where it and the implementation differ, the implementation and the normative pages win:
This draft Implementation Does not say how a reader treats a Specsdeclared below3.1.0Every reader (JSON, YAML, Ion) refuses it with specs_before_3_1Does not say what a Specsbody may holdIts own modules must be module specifications; a definition-shaped module is refused, and an Ion kind: specsdatagram refuses a definition withdefinition_in_specsDoes not say what migration does with a SpecsA v3 Specsmigrates to a v4Specswith"formatVersion": 4, as aLibrarydoesA v3 manifest has no entryPointsIt has no member beyond formatVersion,distribution,package,pathBudgetanddependencies:entryPoints,version,createdandlayoutareunknown_member, and a module manifest does not accept the v4 aliasmoduleDoes not say what a dependency may name A dependency naming the distribution's own package is invalid_distribution_shape; a repeated one isduplicate_memberread_any_treedispatches on the manifestAlso, the v4 tree reader refuses a manifest of major 3 with version_mismatchatmanifest#/formatVersionA tree of another 3.x release fails with unsupported_format_version_minorOnly a single-file document gets that diagnostic. The v3 tree reader accepts a manifest of exactly "3.1.0"and refuses any other 3.x release withversion_mismatchatmanifest#/formatVersion(finos/morphir-rust#262 tracks whether it should answer the minor diagnostic instead)Any v3 document tree says "3.1.0"Only a JSON or YAML tree. The draft Ion tree follows the Ion draft, where a v3 librarytree still says"3.0.0"The TypeScript binding runs the kit cases as pending It skips them ("version 3 not in capabilities") until finos/morphir-typescript#36 lands The Ion spelling writes own modules as module::specA repeated own module::specis refused withduplicate_name; own modules do not mergeTreeParts<P>holds a tree's partsThe kit has a Payloadtrait and aTreeModeltrait with associated file types; there is noTreePartstypemorphir ir migratewrites v3 JSON and YAML treesmorphir migrate(alsomorphir ir migrate)--target-version v3 --output-layout vfswrites v3 trees with--output-format json,yamlorion; in every tree format the manifest decides the version on read
This draft adds a minor revision of the classic IR, 3.1.0. The revision adds two things. A v3 distribution may be a Specs distribution, and a v3 distribution may be stored as a JSON or YAML document tree. Before this revision, only the Ion tree held v3 (issue 970).
A 3.0.0 document stays valid and keeps its meaning. A writer emits the lowest version that expresses its content. A reader that implements this draft accepts [3.0.0,3.2.0).
Version rules
| Content | formatVersion a writer emits |
|---|---|
Single-file v3 Library | 3 (the 3.0.0 release), unchanged |
Single-file v3 Specs | "3.1.0" |
| Any v3 JSON or YAML document tree | "3.1.0" in every tree file |
In a single-file document, a reader accepts the integer 3 and the release strings "3.0.0" and "3.1.0". 3.2.0 and later fail with unsupported_format_version_minor, as format-version support describes. The reference support table becomes [3.0.0,3.2.0),[4.0.0,4.1.0).
A binding that stays on [3.0.0,3.1.0) still reads every 3.0.0 document. It refuses a single-file Specs distribution with the minor-version diagnostic, which is the behaviour decision 0016 requires. A v3 tree manifest of any 3.x release other than "3.1.0" is version_mismatch (see the status note).
The Specs distribution
A v3 Specs distribution publishes a package's specification without its definitions. It mirrors Library:
{
"formatVersion": "3.1.0",
"distribution": [
"Specs",
[["my"], ["pkg"]],
[ [ [["morphir"], ["s", "d", "k"]], { "modules": [ … ] } ] ],
{ "modules": [ [ [["basics"]], { "types": [ … ], "values": [ … ], "doc": null } ] ] }
]
}
The third element lists dependency specifications, as Library does. The fourth element is a package specification: its modules are module specifications, spelled as a dependency's modules are.
The Ion spelling uses kind: specs in the morphir:: header and writes the distribution's own modules as module::spec values, as the v4 Ion spelling does.
Document tree files
A v3 tree uses the v4 tree's logical paths, profiles, escaping, truncation and fileNames rules without change (document-tree files). Only the file contents differ. Every file carries "formatVersion": "3.1.0". A file whose version differs from the manifest's is rejected at that file.
Envelope members hold canonical strings, the same as the v4 envelope: package, path, name, dependencies, and the keys of fileNames. A canonical string comes from the classic word arrays, so [["morphir"], ["s", "d", "k"]] is morphir/SDK. A def or spec payload is the classic v3 JSON for that entry, exactly as a single-file document writes it.
Distribution manifest
{ "formatVersion": "3.1.0", "distribution": "Library", "package": "my/pkg", "pathBudget": 4000, "dependencies": ["morphir/SDK"] }
distribution is Library or Specs. A v3 tree has no entryPoints. dependencies lists the deps/ packages in order and is omitted when empty. pathBudget is required, with the v4 floor of 64.
Module manifest
{ "formatVersion": "3.1.0", "path": "domain/user", "doc": "User records.", "types": ["user"], "values": ["find"] }
Under pkg/ in a Library, access is written only when it is Private. A Specs tree under pkg/, and every tree under deps/, holds module specifications, which have no access. types and values list names. A reader also accepts inline entries keyed by canonical name, whose values are node payloads, as the v4 reader does. fileNames records cut stems, as in v4.
Type and value files
{ "formatVersion": "3.1.0", "name": "user", "def": { "access": "Public", "value": { "doc": "", "value": ["TypeAliasDefinition", [], <Type>] } } }
{ "formatVersion": "3.1.0", "name": "int", "spec": { "doc": "", "value": ["OpaqueTypeSpecification", []] } }
{ "formatVersion": "3.1.0", "name": "find", "def": { "access": "Public", "value": { "doc": "", "value": { "inputTypes": [ … ], "outputType": <Type>, "body": <Value> } } } }
{ "formatVersion": "3.1.0", "name": "add", "spec": { "doc": "", "value": { "inputs": [ … ], "output": <Type> } } }
A Library tree holds def files under pkg/ and spec files under deps/. A Specs tree holds spec files in both. A def payload keeps v3's attribute slots, including a value's inferred types.
Implementation in morphir-rust
The kit's layout keeps one implementation of the rules the compatibility kit pins, over a generic payload:
TreeParts<P>holds the manifest envelope and each module's root, package, path, access, doc, and(name, payload)for its types and values. APayloadtrait lets the layout readformatVersion,nameand listings from a file of typeP, and build one.serde_json::Valueimplements it for the JSON and YAML profiles.- A
TreeModeltrait turnsTreePartsinto a distribution and back. The v4 model keeps today's decoders. The v3 model decodes and encodes the classic types. read_treeandwrite_treestay v4.read_tree_v3,write_tree_v3and the per-module streaming writers are new.read_any_treedispatches on the manifest'sformatVersion.- The document-tree transport accepts v3 for every profile, streams v3 modules through the kit's writers, and reads a tree at the version its manifest names. A manifest whose version differs from the selected one is
version_mismatch. morphir ir migratewrites v3 JSON and YAML trees, and version detection reads any manifest.ir_storage::read_valuereads any v3 tree. Compile output stays v4.
The Ion tree keeps its own layout code for now. A follow-up moves it onto TreeParts<ion_rs::Element>, so that the path, stem, budget and stray-file rules exist once for all three profiles.
Compatibility kit
New cases, each marked version=3:
document-tree: a v3 manifest; aLibrarymodule withdeffiles;deps/withspecfiles; aSpecstree; a cut stem recorded infileNames; the YAML profile of one tree; a v4 file inside a v3 tree, rejected.versions:3.1.0accepted,3.2.0rejected.distributions: a single-file v3Specsdistribution.
The Rust adapter declares these cases. The TypeScript binding skips them ("version 3 not in capabilities") until finos/morphir-typescript#36 lands.
Delivery
- The kit refactor lands alone and changes no behaviour. Every existing layout, kit, transport and Ion tree test passes unchanged.
3.1.0in the classic model:Specs, the version strings, the semantic header, the JSON, YAML and Ion single-file codecs, and the support table with its conformance corpus.- v3 JSON and YAML trees in the kit and the transport.
- The CLI, this draft's normative pages (
docs/spec/ir/schemas/v3/document-tree-files.mdand the 3.1.0whats-new), the v3 schema'sSpecs, the compatibility-kit cases, a kb Decision Record, and the changelog.
Out of scope
- TypeScript v3 trees and
3.1.0, and the3.1.0support-table raises in morphir-elm, morphir-scala and morphir-python, are follow-up issues. - Compile output stays v4.
write_v3keeps writing single files. - An application distribution has no v3 form.