From daddf5842774246be257156612623072cf76ef0e Mon Sep 17 00:00:00 2001 From: Damian Reeves <957246+DamianReeves@users.noreply.github.com> Date: Tue, 27 Oct 2026 19:06:07 -0500 Subject: [PATCH] docs(openspec): archive migrate-python-binding and sync its specs All 26 tasks are complete and released. Add the python/compiler, python/extension and tools/python-codegen main specs from the change's delta specs, move the change to the archive, and point its design page one directory further up for its assets. --- .../.openspec.yaml | 0 .../design.html | 10 +-- .../proposal.md | 0 .../specs/python/compiler/spec.md | 0 .../specs/python/extension/spec.md | 0 .../specs/tools/python-codegen/spec.md | 0 .../tasks.md | 0 openspec/specs/python/compiler/spec.md | 65 +++++++++++++++++ openspec/specs/python/extension/spec.md | 53 ++++++++++++++ openspec/specs/tools/python-codegen/spec.md | 69 +++++++++++++++++++ 10 files changed, 192 insertions(+), 5 deletions(-) rename openspec/changes/{migrate-python-binding => archive/2026-10-27-migrate-python-binding}/.openspec.yaml (100%) rename openspec/changes/{migrate-python-binding => archive/2026-10-27-migrate-python-binding}/design.html (98%) rename openspec/changes/{migrate-python-binding => archive/2026-10-27-migrate-python-binding}/proposal.md (100%) rename openspec/changes/{migrate-python-binding => archive/2026-10-27-migrate-python-binding}/specs/python/compiler/spec.md (100%) rename openspec/changes/{migrate-python-binding => archive/2026-10-27-migrate-python-binding}/specs/python/extension/spec.md (100%) rename openspec/changes/{migrate-python-binding => archive/2026-10-27-migrate-python-binding}/specs/tools/python-codegen/spec.md (100%) rename openspec/changes/{migrate-python-binding => archive/2026-10-27-migrate-python-binding}/tasks.md (100%) create mode 100644 openspec/specs/python/compiler/spec.md create mode 100644 openspec/specs/python/extension/spec.md create mode 100644 openspec/specs/tools/python-codegen/spec.md diff --git a/openspec/changes/migrate-python-binding/.openspec.yaml b/openspec/changes/archive/2026-10-27-migrate-python-binding/.openspec.yaml similarity index 100% rename from openspec/changes/migrate-python-binding/.openspec.yaml rename to openspec/changes/archive/2026-10-27-migrate-python-binding/.openspec.yaml diff --git a/openspec/changes/migrate-python-binding/design.html b/openspec/changes/archive/2026-10-27-migrate-python-binding/design.html similarity index 98% rename from openspec/changes/migrate-python-binding/design.html rename to openspec/changes/archive/2026-10-27-migrate-python-binding/design.html index 50ed7914..fc90f869 100644 --- a/openspec/changes/migrate-python-binding/design.html +++ b/openspec/changes/archive/2026-10-27-migrate-python-binding/design.html @@ -13,11 +13,11 @@ Code goes in
 (or json, toml, bash, diff). Give sections an id and
   list them in nav.toc.
 -->
-
-
-
-
-
+
+
+
+
+
 
 
 
diff --git a/openspec/changes/migrate-python-binding/proposal.md b/openspec/changes/archive/2026-10-27-migrate-python-binding/proposal.md similarity index 100% rename from openspec/changes/migrate-python-binding/proposal.md rename to openspec/changes/archive/2026-10-27-migrate-python-binding/proposal.md diff --git a/openspec/changes/migrate-python-binding/specs/python/compiler/spec.md b/openspec/changes/archive/2026-10-27-migrate-python-binding/specs/python/compiler/spec.md similarity index 100% rename from openspec/changes/migrate-python-binding/specs/python/compiler/spec.md rename to openspec/changes/archive/2026-10-27-migrate-python-binding/specs/python/compiler/spec.md diff --git a/openspec/changes/migrate-python-binding/specs/python/extension/spec.md b/openspec/changes/archive/2026-10-27-migrate-python-binding/specs/python/extension/spec.md similarity index 100% rename from openspec/changes/migrate-python-binding/specs/python/extension/spec.md rename to openspec/changes/archive/2026-10-27-migrate-python-binding/specs/python/extension/spec.md diff --git a/openspec/changes/migrate-python-binding/specs/tools/python-codegen/spec.md b/openspec/changes/archive/2026-10-27-migrate-python-binding/specs/tools/python-codegen/spec.md similarity index 100% rename from openspec/changes/migrate-python-binding/specs/tools/python-codegen/spec.md rename to openspec/changes/archive/2026-10-27-migrate-python-binding/specs/tools/python-codegen/spec.md diff --git a/openspec/changes/migrate-python-binding/tasks.md b/openspec/changes/archive/2026-10-27-migrate-python-binding/tasks.md similarity index 100% rename from openspec/changes/migrate-python-binding/tasks.md rename to openspec/changes/archive/2026-10-27-migrate-python-binding/tasks.md diff --git a/openspec/specs/python/compiler/spec.md b/openspec/specs/python/compiler/spec.md new file mode 100644 index 00000000..7bf61d62 --- /dev/null +++ b/openspec/specs/python/compiler/spec.md @@ -0,0 +1,65 @@ +# python/compiler Specification + +## Purpose + +Compile supported Python domain models to Morphir IR and generate executable Python through an ordinary typed Python API, sharing behavior with native Rust and MEP callers. + +## Requirements + +### Requirement: Compile the established Python subset +Compilation SHALL preserve the migrated binding's frozen dataclasses, named unions, fixed tuples, annotated pure functions, scalar literals and comparisons, conditionals, typed calls, unary contextual lambdas, local imports and module exposure behavior. It MUST reject unsupported syntax with structured diagnostics. (bead: morphir-ukvn.1.6) + +#### Scenario: Compile a multi-module model +- **WHEN** a caller submits models and rules modules with imported ADTs, tuple aliases and annotated conditional functions +- **THEN** compilation preserves their module identities, references, declaration order and configured public/private module access + +#### Scenario: Reject an unsupported construct +- **WHEN** a model uses an assignment or an unsupported external import +- **THEN** compilation reports the binding's diagnostic code and available source location without returning a successful partial model + +### Requirement: Source processing is static +Compilation and generation MUST NOT import or execute submitted source or generated Python. (bead: morphir-ukvn.1.6) + +#### Scenario: Input attempts a side effect +- **WHEN** source includes top-level file-writing code +- **THEN** compilation rejects the unsupported construct and no file is written by that source + +### Requirement: Versioned IR interoperates with the Python library +Compilation SHALL produce v3.0 Library IR by default and draft v4 Library IR on request. Generated models MUST decode with this repository's matching codec. Generation SHALL accept supported v3.0 and draft v4 Library models, reject unsupported versions and constructs, and preserve arbitrary-precision integers wherever the selected format permits them. (bead: morphir-ukvn.1.6) + +#### Scenario: Python codec reads compiler output +- **WHEN** a supported model is compiled to each advertised target version +- **THEN** the shared format reader and matching Python codec decode it without losing module, type or value semantics + +#### Scenario: Independently authored IR generates Python +- **WHEN** a Library model using supported constructs comes from the Python codec or an independent ecosystem fixture +- **THEN** generation produces valid Python without requiring that model to originate in this compiler + +#### Scenario: Unsupported distribution or format +- **WHEN** generation receives a Specs distribution or an unsupported format version +- **THEN** it reports an explicit diagnostic rather than inventing value bodies or silently migrating the model + +### Requirement: Typed Python API supports both directions +The Python API SHALL accept in-memory source documents, package identity, exposure and target-version options, and return immutable typed results with a version-specific Python IR model. Generation SHALL accept a supported Python IR model and return immutable relative-path source artifacts. Failures SHALL expose structured diagnostics with code, severity and available source location. (bead: morphir-ukvn.1.6) + +#### Scenario: Python caller compiles and generates +- **WHEN** a caller supplies source strings through the documented Python API and passes the compiled model to generation +- **THEN** both operations work in process without MEP envelopes, Rust types, temporary files, or a separate executable in the caller's code + +#### Scenario: Warning accompanies success +- **WHEN** the compiler returns valid IR with a warning +- **THEN** the result retains the warning and its severity separately from successful model data + +### Requirement: Round trips preserve supported semantics +For supported models, compile/generate/recompile SHALL preserve normalized module access, references, type definitions and value behavior in each target IR version. Source formatting and equivalent expression spelling need not be preserved. (bead: morphir-ukvn.1.6) + +#### Scenario: Conditional and higher-order behavior survives generation +- **WHEN** a model using conditional branches, a typed call and a unary closure is generated and recompiled +- **THEN** normalized IR remains equivalent and executing the generated code in a separate verification process produces the expected results + +### Requirement: Source document order does not affect results +Compilation SHALL produce equivalent models and diagnostics for permutations of the same source set, with deterministic artifact ordering during generation. (bead: morphir-ukvn.1.6) + +#### Scenario: Reordered modules +- **WHEN** callers compile the same dependent source modules in different orders +- **THEN** normalized models and diagnostic ordering agree, and subsequent generation returns the same ordered artifacts diff --git a/openspec/specs/python/extension/spec.md b/openspec/specs/python/extension/spec.md new file mode 100644 index 00000000..f8ca615e --- /dev/null +++ b/openspec/specs/python/extension/spec.md @@ -0,0 +1,53 @@ +# python/extension Specification + +## Purpose + +Distribute the Python language compiler from morphir-python for native Rust consumers, ordinary Python installations and Morphir extension hosts, with one maintained implementation. + +## Requirements + +### Requirement: Rust consumers use an independently buildable crate +The migrated crate SHALL retain the `morphir-python-binding` package name and native `PythonExtension` frontend/backend API. A Rust consumer MUST be able to build and invoke it without a morphir-rust sibling checkout, CPython or Python extension linking. (bead: morphir-ukvn.1.6) + +#### Scenario: External Cargo consumer +- **WHEN** a standalone Cargo project depends on the binding at an exact committed revision in morphir-python +- **THEN** it builds and invokes compile and generate using declared core/SDK dependencies without local source overrides or Python development libraries + +### Requirement: MEP behavior remains compatible +The extension SHALL retain identity `morphir-python`, Python language/target identifiers, initialization and capability negotiation, compile/generate methods and advertised IR v3/v4 support. Native Rust, Python and packaged WASM invocations SHALL agree on supported model semantics and language diagnostic codes. (bead: morphir-ukvn.1.6) + +#### Scenario: Existing host installs the replacement bundle +- **WHEN** a compatible Morphir host installs the bundle built from morphir-python +- **THEN** initialization, capability discovery, Python compilation and Python generation succeed through the existing protocol + +### Requirement: Python installation hides Rust implementation details +Installing `morphir-python` on qualified CPython platforms SHALL install its compatible native compiler dependency automatically from wheels. Compilation and generation MUST require neither a Rust toolchain nor a separately installed Morphir CLI at runtime. The `morphir` distribution SHALL keep zero third-party runtime dependencies. (bead: morphir-ukvn.1.6) + +#### Scenario: Clean wheel-only installation +- **WHEN** a user installs the qualified distributions into a clean CPython 3.14 environment on a supported wheel platform with Rust absent +- **THEN** Python API compilation and generation and both new CLI commands succeed + +#### Scenario: Library-only user +- **WHEN** a user installs only `morphir` +- **THEN** IR and SDK imports work without the compiler dependency or native shared libraries + +### Requirement: Qualified artifacts are self-contained +Native wheels and source distributions SHALL include required runtime files, typing information and build inputs. Published artifact metadata SHALL declare supported interpreter/platform combinations and compatible package versions. Qualification MUST install built artifacts outside the source checkout. (bead: morphir-ukvn.1.6) + +#### Scenario: Build from the source archive +- **WHEN** a contributor builds the native wheel from its source archive with the documented build toolchain +- **THEN** the build succeeds without missing workspace files or sibling repositories and the wheel passes an installed API smoke test + +### Requirement: Python extension releases belong to morphir-python +Replacement Python extension bundles SHALL be built, qualified and released from finos/morphir-python with checksums, capability claims and source provenance. Ecosystem installation references SHALL resolve the replacement provider there; historical release assets MUST remain available. (bead: morphir-ukvn.1.6) + +#### Scenario: Ecosystem installation follows the new owner +- **WHEN** a qualified replacement is published and selected in the ecosystem extension repository +- **THEN** `morphir extension install morphir-python` obtains its bundle from morphir-python, verifies it and supports offline compile/generate/recompile + +### Requirement: Old implementation is removed after replacement qualification +The cutover SHALL remove the Python binding implementation, workspace membership and Python-specific publishing ownership from morphir-rust. Tests and integrations MUST use the new provider or independent host fixtures without depending on the deleted directory. (bead: morphir-ukvn.1.6) + +#### Scenario: Rust workspace after removal +- **WHEN** the replacement provider has passed migration qualification and the coordinated removal lands +- **THEN** the morphir-rust workspace and its CI remain valid with no Python compiler implementation or references requiring the former crate directory diff --git a/openspec/specs/tools/python-codegen/spec.md b/openspec/specs/tools/python-codegen/spec.md new file mode 100644 index 00000000..2e5a94cd --- /dev/null +++ b/openspec/specs/tools/python-codegen/spec.md @@ -0,0 +1,69 @@ +# tools/python-codegen Specification + +## Purpose + +Let morphir-python CLI users compile supported Python models into versioned Morphir IR and generate Python packages from supported IR using the same Python API as application callers. + +## Requirements + +### Requirement: Compile Python sources from the CLI +`morphir-python compile SOURCE --package PACKAGE --output FILE` SHALL compile one Python file or a directory source tree through the public Python API. It SHALL default to IR v3 and support `--ir-version v4` and repeated `--expose MODULE` selections. Omitting exposure SHALL expose every module. Output SHALL be a versioned IR document readable by `ir inspect`. (bead: morphir-ukvn.1.6) + +#### Scenario: Compile a source directory +- **WHEN** the user compiles a directory containing supported models and locally importing rules modules +- **THEN** the tool writes one versioned Library document with correct module identities and requested exposure, and exits zero + +#### Scenario: Choose draft v4 +- **WHEN** the user requests `--ir-version v4` +- **THEN** the tool writes draft v4 and reports its draft status + +### Requirement: Generate Python from versioned IR +`morphir-python generate FILE --output-dir DIRECTORY` SHALL read IR with the shared format reader and matching Python codec, then call the public generation API. It SHALL write all generated relative-path Python modules while preserving their directory structure. (bead: morphir-ukvn.1.6) + +#### Scenario: Generate modules with imports +- **WHEN** a supported Library contains models and rules in nested modules +- **THEN** generation produces importable modules with the expected paths and supported behavior + +#### Scenario: Version parsing uses the shared contract +- **WHEN** an input has a malformed, missing or unsupported format version +- **THEN** generation reports the same version diagnostic code as the Python library and exits non-zero + +### Requirement: Report failures and warnings consistently +Both commands SHALL preserve Python API diagnostic codes, severities and available locations. Failures SHALL exit non-zero, with diagnostics on stderr. Success warnings SHALL remain distinguishable from errors and SHALL NOT cause a non-zero exit. (bead: morphir-ukvn.1.6) + +#### Scenario: Invalid source +- **WHEN** compilation encounters a supported parser error or unsupported construct +- **THEN** stderr identifies its code and available source location, and no successful IR output is written + +#### Scenario: Unsupported IR construct +- **WHEN** generation receives an unsupported value form +- **THEN** it reports the API diagnostic and writes no generated modules + +### Requirement: Output publication preserves existing user data +Commands MUST finish validation before publishing output. Compile failures MUST preserve an existing output file. Generation SHALL require a nonexistent or empty output directory, reject escaping or colliding artifact paths, and stage all files before publishing them. Failures MUST leave no partial generated output or overwritten user files. (bead: morphir-ukvn.1.6) + +#### Scenario: Existing generation directory contains files +- **WHEN** the user selects a nonempty output directory +- **THEN** the command fails clearly and preserves every existing file + +#### Scenario: Generation fails on a later module +- **WHEN** generation cannot handle one module after processing another +- **THEN** no final output module is published + +#### Scenario: Artifact path escapes the selected directory +- **WHEN** an artifact path is absolute, traverses a parent directory, or would traverse a symlink +- **THEN** the command rejects the artifact set before writing any output + +### Requirement: Existing IR commands remain independent +`ir inspect` and `ir convert` SHALL retain their existing options, shared library behavior and results without initializing the Python compiler. (bead: morphir-ukvn.1.6) + +#### Scenario: Inspect and convert an existing fixture +- **WHEN** the user runs either existing command after installing the updated tools +- **THEN** its existing compatibility and diagnostic tests continue to pass + +### Requirement: CLI supports a complete round trip +Generated Python in the supported subset SHALL be accepted by `compile` when supplied the original package and exposure options, yielding equivalent normalized IR in the chosen target version. (bead: morphir-ukvn.1.6) + +#### Scenario: Compile generate recompile +- **WHEN** the user compiles a supported multi-module model, generates Python, then recompiles that output with the same package, exposure and IR version +- **THEN** both IR documents describe equivalent supported definitions and generated Python returns the expected results