Skip to content

Release v0.4.0-alpha.5

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 13 Nov 03:24
· 23 commits to main since this release
3abb0de

Added

  • Releases attach the Windows toolchain for win-arm64 as well as win-x64: the self-contained host, the F# frontend and backend archives, the candidate, the extension selection and the bundle, with one SHA256SUMS over both platforms' files, so morphirup-win-arm64.exe setup installs from the release. Each Windows toolchain is built once, by the same job as its manager, after CheckBuildMachine; the separate win-x64 toolchain job is gone. Pull requests build and check the win-arm64 toolchain in morphirup.yml and keep it downloadable for seven days.
  • deployment.yml has a dry-run input for a manual run: it builds every platform, assembles the release assets and checksum files, uploads them as the dry-run-release-assets artifact, and publishes nothing to NuGet or GitHub releases.
  • Windows ARM64 (win-arm64) is a released platform. The release workflow builds morphir-win-arm64.exe, morphir-win-arm64-v<version>.tar.gz, morphirup-win-arm64.exe and morphirup-win-arm64-v<version>.tar.gz on the hosted windows-11-arm runner, natively, and the morphirup checksum file lists the morphirup ones; the pull request workflow morphirup.yml builds and checks morphirup there too. The names, contents and checks of the other platforms' assets are unchanged. Earlier releases have none of these files (#611).
  • Experimental Windows release setup with morphirup: in a project, morphirup setup --yes downloads the runtime-bundled Morphir host and the independent F# frontend and backend from the exact finos/morphir-dotnet release of the manager's own version (or the exact version .morphir/toolchain.toml requests), checks each file's byte count and SHA-256 against the release's SHA256SUMS, installs the complete folders into the content-addressed store, runs the fixed readiness workflow and selects the completed generation last. morphirup run -- compile and morphirup run -- generate then use the installed folders with no network access. setup --dry-run creates nothing. This is release-checksum delivery, not publisher-authenticated: HTTPS and a matching checksum do not authenticate the publisher, and trust commands are not included. The release transport starts at the exact repository and version tag and follows at most three HTTPS redirects to github.com, release-assets.githubusercontent.com or objects.githubusercontent.com. The release attaches the setup bundle for Windows x64 and Windows ARM64. Morphir.Extensions.Installation is a new library package (#608).
  • Build: CheckBuildMachine --rid <rid> fails unless the machine's operating system and architecture are the RID's and the .NET SDK runs natively (not x64 under emulation on ARM64), and logs the machine. The CLI release jobs and cli-offering.yml run it before they build.
  • Build: FlattenReleaseAssets copies each downloaded platform output into the release assets once, accepting byte-identical duplicates and failing on two different files under one name instead of overwriting; the morphirup checksum jobs use it. WindowsToolchainChecksums writes one SHA256SUMS over both Windows toolchain outputs, and writes nothing when a Windows RID's bundle or manager is missing, or an asset its bundle names is missing or differs.
  • scripts/install-windows.ps1 and the proto plugin choose win-arm64 on Windows ARM64, where they refused before. The Windows installer now reads the machine's architecture, so an x64 PowerShell under emulation on ARM64 also chooses win-arm64. Releases before this one have no win-arm64 file, so asking either for an earlier version fails on Windows ARM64.
  • Experimental configured Python WASM commands: morphir compile --language python and morphir generate --target python use extensions.python.path through the existing MEP model adapters and owned WASM sessions. Includes a pinned Python 0.6.0 walkthrough; Windows x64 retains its named runtime refusal (#610).
  • Experimental WASM host-kit reports adopt the pinned parent report schema, computed ordered verdict reasons, per-profile guest-kit citations and eight named refusals. All 22 fixed policy controls are checked independently; the runner retains observation recomputation and exact report rebuilding. Citation-bearing runs fail closed with an explicit unavailable-checker result until the actual generic-kit report checker is integrated. This does not qualify a host, guest, artifact or platform (#564).
  • Internal current-family standalone and embedded claim reader, retaining supplied JSON beside existing MEP values and an understood-member projection, with recursive metadata checks, critical paths and host comparators reused from Toolchain. Claims 27/27 and affected SharedReaders 19/19 pass on Windows, repeated after pinned formatting. Public codecs, full corpus conformance, probes and installation lifecycle remain unfinished (#558).
  • Internal extension-family reader increment with pinned, offline hash-checked schemas and case data from parent 0439c4c0. The focused Windows SharedReaders suite passes 19/19, including six parent release/schema cases and two I-JSON refusal cases with valid parse controls. Full claim/package semantics, state codecs, inventory, probes and command lifecycle remain unfinished; the 137 identity and 66 package cases have not all been executed (#558).
  • Extension installer security corrections: descriptor-bound single-file publication, interprocess run-directory ownership coordination, directory/owner identity checks, and gzip metadata/padding budgets with cancellation below the tar reader. Windows regression evidence is 131 passed with three Unix-only cases ignored. Independent security/interop re-review of b6936ba5 found no concrete remaining finding under the approved cooperating Home boundary; Unix runs and AOT remain pending. Recorded the approved selection, command, provenance and explicit developer-consent decisions; inherited default staged auto grants no automatic install consent. Shared readers use parent source 0439c4c0; portable pin/overlay formats remain parent toolchain work (#558).
  • Extension installation library foundations: portable distribution identifiers over the existing domain types, RID/descriptor/index mappings and exact implemented MEP eligibility within the supported 0.2 line. Includes an Expecto suite and C# probe; CLI installation and Native AOT qualification remain unfinished (#558).
  • Extension installation I/O: a single-file content-addressed store that hashes while copying, rehashes reuse and refuses corrupt objects without replacement; ZIP and tar-gzip inspection with the recorded path, type and resource limits; and exclusive ownership and stale cleanup of .NET run directories. Windows tests and the C# JIT probe cover these APIs. Package formats, extraction, inventory, activation and CLI installation are unfinished; Unix filesystem runs and Native AOT qualification remain pending (#558).
  • morphir mck <args> runs the separately installed Morphir Compatibility Kit, morphir-mck <args>, a development tool from finos/morphir, in every entry point (morphir, the managed morphir, dotnet-morphir). The arguments, working directory, environment and standard streams are passed on unchanged, and the exit code is the tool's. MORPHIR_MCK names the tool's absolute path; otherwise only absolute PATH folders are searched, never the current directory, and on Windows only morphir-mck.exe is a candidate. With no tool, the CLI writes a diagnostic to stderr and exits with 127. On Windows, killing morphir also stops the tool and its children (a kill-on-close job object); on Linux and macOS, SIGTERM or SIGHUP stops the tool's tree. mck is the only command routed to another executable, and the CLI includes no kit of its own (#589)
  • morphir extension list --json and dotnet-morphir extension list --json say which offering is running and what it needs from the machine: deployment (native-aot, self-contained, framework-dependent or dotnet-tool), set at build time by the named release profile and never inferred from how the process runs; execution (native-aot or jit); platform (the runtime identifier, left out for the portable tool package); and prerequisites (runtime, sdk with its phase, and native). A build that no named profile produced, such as dotnet run, a test host or the legacy morphir-<rid> single-file assets, leaves out deployment, platform and prerequisites. Each extension entry gains deployments, prerequisites and, when it is not available, a reason. The host member keeps its meaning as the command name (#558)
  • The built-in F# frontend is listed as morphir-dotnet.fsharp, available in the self-contained and framework-dependent morphir and in dotnet-morphir. The Native AOT morphir lists it as not available, with the reason; the registry and compile take the claim from the same declaration (#558)
  • Morphir.Toolchain: the recipe binding checks of the toolset recipe page, as pure functions over a recipe, the authenticated targets and their bytes. RecipeBindings.check runs every host row (a tool-release offering's v1 release descriptor is authenticated, not revoked, served with its length and digest and readable, and lists each bound artifact for its platform with matching tool-artifact metadata) and every provider row (the extension release descriptor is bound, served, readable and names the provider's identity; each artifact or package is bound and listed with the delivery's runtime, digest and platform; and the claims name the capability kind, MEP version, language or target and model tuple), each with the row's code and path. It reads tool release metadata v1 release descriptors (ToolReleaseDocument), extension release descriptors at 2.0.0-draft.2 (ExtensionReleaseDocument) and capability claim sets at 0.1.0-draft.2 (ClaimSetDocument), and nothing earlier. ToolResolver.resolve selects a tool release by the v1 channel, CLI compatibility and platform rules. No command uses them, nothing is downloaded, and none of it has had a security review (#558)
  • morphirup setup --dry-run: the same request as setup --plan, with the same plan, planSha256, result, refusals and exit codes, and nothing created, changed or removed. --plan is unchanged; both may be given; neither permits --yes. Every later morphirup command that creates, updates, repairs or removes persistent state will have --dry-run on its own planning and validation path (#558)
  • Morphir.Toolchain: Comparator.allMet, which says whether an installed version meets a list of comparators by the Rust semver and npm rule (a prerelease matches only when a comparator names its own major, minor and patch with a prerelease; an empty list is always met), and Discovery.requestIn with Discovery.PathCase, which compares names without case on Windows, and Discovery.within. The suite also runs the contract's nfc, setup-platforms, prerequisite-versions and version binding cases and its Windows discovery case (#558)
  • Morphir.Toolchain: the verification part of a TUF client for the tool release metadata v1 profile, as pure functions over supplied bytes and one trusted start time: strict readers for root, timestamp, snapshot and targets metadata, Ed25519 threshold verification with BouncyCastle, root rotation with both thresholds and a retry after 32 roots, timestamp, snapshot and targets checks with rollback, link and expiry, and typed failures mapped to the stable codes. A refresh returns a RefreshResult whose every outcome (Refreshed, NothingNew for a timestamp at the trusted version, RootRotationPaused and Failed) carries the newest trust state accepted, so a root accepted before a failure is kept. No exception leaves the client: a defect is a Failed with TufFailure.Internal. A trust anchor carries its purpose, and a Production anchor must be the repository identity the caller names; every root accepted later meets the same rules. Text inside signed that is not NFC, a member differing from a known member only in letter case, a prerelease spec_version and an integer above 2^53 - 1 are refused. It does no I/O and no command uses it. It has not had a security review and must not be used to authenticate a production release until it has. Where the profile is silent it takes the stricter reading, marked provisional (#558)
  • morphir generate --extension and the configuration key [codegen.<target>].extension select a MEP backend extension (a command name, a path, or a .dll, as for compile --extension), for example morphir-fsharp-backend. The option wins over the key; without either, the built-in backend runs as before. Every entry point has the option. The generate report names the extension by its own id and adds backendTransport (mep) and backendVersion. Failures have the codes extension.unavailable, extension.protocol_error, extension.timeout, extension.tuple_unavailable, extension.not_admitted, extension.unsafe_artifact and invalid-extension. An extension cannot be selected by an installed extension id yet
  • MepGenerationBackend in Morphir.Tooling: an IGenerationBackend over a MEP guest's morphir.backend.generateModel. It sends the model's JSON IR (IR 4.0.0 or 3.0.0) only within the limits that the session agreed (MepBackendOptions.OfferLimits), accepts at most 10,000 artifacts in one result, and checks every returned artifact path before publication, including Windows-equivalent names, reserved device names and file/directory conflicts. MepBackendOptions.Environment sets the guest's whole environment. A guest that gives the id of a built-in backend is refused
  • MepGuestProcess in Morphir.Compilation.Mep: the guest-process lifecycle (start, initialize, describe, request with a deadline, shutdown or kill of the process tree) that MepFrontend and MepGenerationBackend share. MepGuestOptions.Environment, and an additive MepFrontend.ConnectAsync overload, set the guest's whole environment
  • Morphir.CodeGeneration.FSharp, for C# callers: the public union FSharpFormatting (Applied, Off, Unavailable, with WireValue), which Transpiler.formatting now records; FSharpGeneration.TryGetFormatting(tree, out formatting) and FSharpGeneration.WithRequiredSemantics(context, semantics); and GenerationOptions.WithOptimize and WithFormat. The project now sets nullable metadata, warnings as errors, and the trim and AOT properties
  • morphir generate --target fsharp --option format=false: the F# backend option format (Boolean, true by default, beside optimize) turns Fantomas formatting off. The files are the generator's own text, with LF line endings only and exactly one final LF, the same bytes on every operating system, under JIT and Native AOT, and in every culture. The external backend morphir-fsharp-backend accepts the same option through the generateModel options. GenerationOptions.Format and Transpiler.formatting carry it in Morphir.CodeGeneration.FSharp
  • The generate report member formatting: applied, off or unavailable, written for a built-in backend only: an external backend (backendTransport mep) has no formatting member, even with format=false, because a MEP generate result does not say whether the guest formatted. compile --target writes the same member in its generation report member. Under the default, a host that cannot run the formatter (the Native AOT morphir) writes a warning on stderr from generate and from compile --target, never on stdout or in the JSON output; its bytes are unchanged

Changed

  • Build: the repository builds with the .NET 11 SDK (11.0.100) and C# 15. morphir, dotnet-morphir, morphirup, the F# extension tools and the tests target net11.0, so the framework-dependent morphir and dotnet-morphir need Microsoft.NETCore.App 11.x, and installing dotnet-morphir needs a .NET SDK 11.0.100 or later. The library packages target net11.0 and net10.0, so .NET 10 applications can still use them. .NET 8 and .NET 9 reached the end of support on 2026-11-10, so .NET 10 is the lowest target. The MSBuild tasks and Myriad plugins in Morphir.Myriad and Morphir.Myriad.SystemTextJson stay on net10.0, so the .NET 10 SDK can load them. Their MSBuild reference packages return to the 18.0 line, because 18.1 and later have no net10.0 assets. Microsoft.Extensions.* and System.Text.Json move to 11.0.0, which also supports net10.0. The target frameworks are set in one place, Directory.Build.props
  • Build: the Native AOT publish of morphir treats IL3050 as an error. The F# formatter (Emit.formatCore, Fantomas) is [RequiresDynamicCode] and is called only behind RuntimeFeature.IsDynamicCodeSupported, so if that guard goes, ILC refuses the publish instead of putting Fantomas (about 20 MB) into an image that cannot run it
  • Release process: finos/morphir now builds, qualifies and publishes the official Morphir CLI from a pinned commit of this repository. The deployment workflow here publishes the library packages and the F# extension tools, and builds and publishes the CLI (the morphir-<rid> executables, the morphirup-<rid> assets and the Morphir.Tool package) only when the repository variable MORPHIR_DOTNET_CLI_PUBLICATION is enabled, as a rollback. The reusable workflow cli-offering.yml builds one of the four offerings for one platform at a given commit with PackRelease, starts the produced executable (or the installed dotnet-morphir) with --version, and uploads the file, its launch metadata and its SHA-256. PublishTool is now PublishCliTool plus PublishExtensionTools, and PublishTool and PublishAll publish what they did. See docs/contributing/cli-build-interface.md (#589)
  • In the Native AOT morphir, the hint of compile.frontend_unavailable for F# now names the offerings that have a built-in F# frontend and the process delivery morphir-fsharp-frontend (#558)
  • Morphir.Toolchain and morphirup follow the finos/morphir toolchain contract at 9e1f853b7 (the draft finos/morphir#1147), re-pinned from 1451c081f. The same input now gets another code or path:
    • a MEP version (protocolVersion, an offering's protocolVersions) that is not a version in canonical SemVer spelling, such as 0.1, v0.2.0 or one past the version bound, is invalid_value instead of unsupported_case. A SemVer version off the 0.2 line, such as 0.1.0, stays unsupported_case;
    • a recipe provider with delivery built-in is inconsistent_document instead of unsupported_case;
    • in a receipt, a workflow check without workflow and a probed claim check without probeSource are invalid_document instead of inconsistent_document, and a component whose readiness is workflow-qualified is inconsistent_document instead of unsupported_case;
    • in a result, a plan without planSha256 is invalid_document instead of inconsistent_document, and a host component whose source names another artifact is refused at source.target instead of source
  • Documents that were read before are now refused with inconsistent_document: a built-in with protocolVersion or modelTuple in a recipe, lock or receipt, at that member; an offering that lists a MEP version or a WASM ABI twice, at the repetition; a plan whose host source is recipe-digest or whose provider source is repository-target, at its source; and a diagnostic with document or path but not both, at the one present
  • morphirup results: an unsupported-artifact diagnostic from setup --plan names the request member it concerns with document request and path, such as toolset.providers[1], and a repository_mismatch diagnostic names the recipe member (setup --plan) or lock member (doctor) that names another repository. A recipe, lock or request that 0.4.0-alpha.4 accepted and that is now refused turns a planned or succeeded result (exit 0) into a failed one (exit 1); for example, a recipe that lists a MEP version twice in one offering. Exit code 2 (the command was not run) and exit code 3 (an internal error) mean what they did, and the plan digest of any input both read is unchanged. The version bound (256 characters, 15 digits per numeric identifier) is the contract's rule and no longer provisional
  • Morphir.Toolchain API, for F# callers: DiagnosticDetail.UnsupportedArtifact and DiagnosticDetail.TrustFailure have a last field concerns: DocumentMember option; BindingFailure.RepositoryMismatch carries the document whose member its path names; Diagnostics.ofTargetRefusal takes that document; and RecipeTargets.checkLockedGeneration takes the generation's recipe as a function unit -> Result<Recipe, TargetRefusal>, asked for only for the host release row (a refusal it returns is the check's, and an exception it throws is invalid_document at recipe.target), and refuses a generation whose recipe target the trust state does not list with target_not_authenticated at recipe.target, before reading the recipe (#558)
  • morphir generate --input now loads the project configuration (from --project, --config or discovery) to read [codegen.<target>].extension when --extension is absent. With --project or --config, a configuration that fails to load fails the command with exit code 2. With neither, a discovered configuration that does not load is a warning on stderr and the built-in backend runs, as before
  • compile --extension, from the shared guest-process lifecycle: a user cancellation (Ctrl+C) while the guest initializes is now a cancelled compile (exit code 130) instead of extension.unavailable, and a guest that honors $/cancelRequest at the deadline now gives extension.timeout instead of extension.protocol_error
  • compile --target does not run a backend extension: it runs the built-in backend of the target, as before. When the project configuration sets [codegen.<target>].extension for that target, it now prints a note on stderr saying so
  • GenerationOptions (Morphir.CodeGeneration.FSharp) has the new field Format, so a record expression that sets every field needs it, and C# that calls its two-argument constructor must pass both; { GenerationOptions.Default with Optimize = ... } and GenerationOptions.Default.WithOptimize(...) keep working

Fixed

  • morphirup setup: a setup.readiness_failed refusal now includes the host's own diagnostic code and message; a completed attempt removes its staging folders under Home, on success and on refusal; run -- generate --target csharp --output fsharp keeps the built-in C# backend, because installed F# providers are chosen from the --target and --language values only; and on Windows, dry run and apply refuse before creating Home when an installed host or provider executable path would reach 260 characters, naming Home, the executable and the 259-character limit (#616).

  • WASM execution now refuses Windows x64 with win_x64_trap_unsupported before loading the engine or a module. Other CLI capabilities remain available; the CET/Spectre-preserving fix is tracked in #609. The diagnostic signal-trap override is removed.

  • Corrected Darwin ARM64 variadic mode placement for installer open/openat, preserving no-follow/exclusive flags and descriptor identity. Hosted macOS failures at 4c908047 are the regression evidence. Focused Windows Store 15/15 and RunCopies 5/5 pass, with three Unix-only tests ignored; exact-head macOS/Linux verification remains pending (#593).

  • Configuration: a member of [frontend] that is not a table (for example [frontend] note = "x") is no longer refused as an invalid language table. Only an object-valued member is a [frontend.<language>] table with project_file and extension; any other member stays unconstrained, as the upstream schema leaves it

  • compile --extension: a frontend guest that never answers compileModel now fails with extension.timeout at the deadline, and a guest that does not answer shutdown or take exit within the deadline (for example one whose handler blocks its read loop) has its process tree stopped. Before, the host could wait indefinitely in both cases. The same holds for generate --extension, so a timed-out request ends within about two deadlines

  • A frontend or backend guest that fails to start or initialize no longer leaves its stderr reader or process handle behind

  • compile: for a language id with a dot, [frontend."acme.lang"].extension and project_file are now read from that quoted table; before, the id was split at the dot, so the keys were looked up under [frontend.acme.lang] and the quoted table was ignored. A project file key that is not read for lack of a language is now named with the quotes too

Security

  • Morphir.Live: centrally pin the transitive Microsoft.AspNetCore.WebUtilities dependency to 9.0.23 for CVE-2026-114149 / GHSA-x33c-w48q-qrgc, replacing the affected 9.0.19 selected through Fun.Blazor.
  • compile --extension, generate --extension and the extension configuration keys refuse a guest that reports an extension id that starts with morphir., which is reserved for the built-in frontends and backends, including built-ins that do not ship yet (extension.unavailable). Before, generate refused only the id of a registered built-in backend, and compile checked no id. org.finos.morphir.* ids are not reserved. Morphir.Mep.ReservedExtensionIds and ExtensionId.ReservedForBuiltIns hold the rule
  • compile --extension, generate --extension and the extension configuration keys: a command name without a directory (for example morphir-fsharp-backend) is now searched only in the absolute PATH directories, and the resolved absolute path starts. Before, process start also looked in the directory of morphir and in the current directory first (CreateProcess on Windows, and .NET on Unix), so a checkout could plant a binary of that name and have it run. A .dll extension's dotnet host is resolved the same way when DOTNET_HOST_PATH is not set. A name that is not on PATH now fails with extension.unavailable before any process starts. MepFrontendExtensionConnector.Resolve throws FileNotFoundException for such a name instead of returning it unchanged