Skip to content
Draft
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
7 changes: 7 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ scripts/features/openspec_ext.feature text eol=lf
# Vendored Morphir Ion fixtures are checked by SHA-256, so their bytes must not change
tests/Fixtures/MorphirIon/** -text

# Preserve original upstream bytes used by the frontend provenance checks.
tests/Fixtures/Mep/** -text
tests/Fixtures/MorphirOut/** -text

# The portable case-set digest must not change with checkout newline settings.
conformance/** text eol=lf

# Embedded business fixtures use their checked-in LF bytes for provenance hashes.
examples/car-rental/models/car-rental-v1.json text eol=lf
examples/car-rental/models/car-rental-v2.json text eol=lf
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
</ItemGroup>
<ItemGroup>
<PackageVersion Include="Autofac" Version="7.0.0" />
<PackageVersion Include="Gherkin" Version="35.0.0" />
<PackageVersion Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Bogus" Version="34.0.2" />
<PackageVersion Include="BunsenBurner.Bogus" Version="$(BunsenBurnerVersion)" />
Expand Down
3 changes: 3 additions & 0 deletions Morphir.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<Project Path="build/_build.csproj" />
</Folder>
<Folder Name="/src/">
<Project Path="src/Morphir.Conformance/Morphir.Conformance.fsproj" />
<Project Path="src/Morphir.SDK.CSharp/Morphir.SDK.CSharp.csproj" />
<Project Path="src/Morphir.CodeGeneration.FSharp/Morphir.CodeGeneration.FSharp.fsproj" />
<Project Path="src/Morphir.CodeGeneration.CSharp/Morphir.CodeGeneration.CSharp.fsproj" />
Expand All @@ -41,6 +42,8 @@
<Project Path="src\Morphir.SDK.FSharp\Morphir.SDK.FSharp.fsproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/Morphir.Conformance.Smoke/Morphir.Conformance.Smoke.fsproj" />
<Project Path="tests/Morphir.Frontend.FSharp.Tests/Morphir.Frontend.FSharp.Tests.fsproj" />
<Project Path="tests/Morphir.SDK.Conformance.Tests/Morphir.SDK.Conformance.Tests.csproj" />
<Project Path="tests/Morphir.SDK.CSharp.Tests/Morphir.SDK.CSharp.Tests.csproj" />
<Project Path="tests/Morphir.CodeGeneration.FSharp.Tests/Morphir.CodeGeneration.FSharp.Tests.fsproj" />
Expand Down
27 changes: 27 additions & 0 deletions NOTICE.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
Morphir - FINOS
Copyright 2014 - 2020 Morgan Stanley

F# source frontend and compile steel thread (#499)
Copyright 2026 FINOS contributors.
The frontend tracking, conformance probes, tests and documentation are original
contributions under Apache-2.0. Existing notices and attribution
remain applicable. Any imported upstream evidence retains its provenance and
accompanying notices.

Pinned MEP and compile-record evidence is copied from finos/morphir revision
cde7722eb35987042a49dc646a846d693b0bd7f6. Its FINOS and Morgan Stanley notices
and Apache-2.0 license are retained in tests/Fixtures/Mep and
tests/Fixtures/MorphirOut. PROVENANCE.md files record the source paths and hashes.

This product includes software developed at the Fintech Open Source Foundation (https://www.finos.org/).
This product includes software developed at Morgan Stanley.

Expand Down Expand Up @@ -141,3 +153,18 @@ This documentation-only contribution synchronizes both landed C# CLI capability
deltas, archives the completed change, repairs links and records delivery status.
It adds no third-party source, license or restriction. Existing notices,
attribution and original-byte evidence remain applicable and are retained.

Portable F# frontend conformance seed (#499)

Copyright 2026 FINOS contributors.

This contribution adds hand-authored Gherkin cases, the seed step vocabulary,
case validation, matrix planning, structural comparisons and deterministic
reports in Morphir.Conformance, with in-process adapter and Native AOT tests.
The official Gherkin parser is consumed as the MIT-licensed Gherkin 35.0.0
package, already used transitively by Reqnroll. No parser source is copied or
modified. Existing notices and the pinned fixture provenance remain applicable.

The Windows checkout follow-up preserves pinned fixture bytes and portable
case-set newlines, with a CRLF manifest regression test. No upstream payload
or recorded provenance hash is modified.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,11 @@ SDK runtime delivery and archival evidence is recorded in the [runtime delivery

## OpenSpec change proposals

The [Morphir Compatibility Kit seed](conformance/README.md) contains portable
F# frontend cases, a closed step vocabulary and the deterministic library runner.
Its reader/runner tests and Native AOT smoke check are independent of the
production F# frontend, which is still being implemented.

This repository uses the shared `html-design` OpenSpec workflow. New proposals
produce capability specs, a standalone `design.html`, and implementation tasks.
See [the OpenSpec guide](docs/openspec.md) for setup, verification and updates.
1 change: 1 addition & 0 deletions build/Build.Testing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ void RunTests(string configuration)
var myriadExitCodes = new[]
{
"Morphir.TestSupport.Tests",
"Morphir.Frontend.FSharp.Tests",
"Morphir.SDK.Conformance.Tests",
"Morphir.SDK.CSharp.Tests",
"Morphir.IR.Pipeline.Plugins.Tests",
Expand Down
45 changes: 45 additions & 0 deletions conformance/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Morphir Compatibility Kit seed

The F# frontend suite is the first seed of the portable Morphir Compatibility
Kit. Cases and expectations are product data, independent of the implementation
under test. Do not record or regenerate expectations from compiler output.

- [Vocabulary 1.0](steps/steps-1.md) defines every supported step.
- [F# refusal seed](suites/frontend-fsharp/mutable.feature) covers four IR
versions and three source input kinds.
- [Deviations](deviations.md) records the upstream compatibility decisions.
- [Runner source](../src/Morphir.Conformance/) implements the official Gherkin
reader, linter, matrix checks, comparison and deterministic JSON reports.

Run the fast reader/runner tests:

```console
dotnet run --project tests/Morphir.Frontend.FSharp.Tests -- --sequenced
```

The `library` adapter accepts an implementation identity and a function from
compile input to an observation or operational failure. It receives no expected
outcome. Frontend binding is added when the production compiler is available;
these runner tests do not qualify an F# frontend.

Check the AOT boundary on macOS ARM64:

```console
dotnet publish tests/Morphir.Conformance.Smoke -c Release -r osx-arm64 -p:PublishAot=true -p:TrimmerSingleWarn=false -o /tmp/morphir-conformance-aot
/tmp/morphir-conformance-aot/Morphir.Conformance.Smoke conformance
```

The smoke program deliberately supplies an incorrect implementation. It exits
zero only when the implementation fails qualification and every planned variant
has a verdict. It writes the unqualified report to stdout. It validates the
runner, not the frontend.

The compiler and Core pins remain unchanged. The smoke's expanded FSharp.Core
warnings are compared with the existing AOT CLI baseline in the feature PRD;
no new warning is suppressed. Other RIDs can run the same smoke using their own
runtime identifier.

Kit manifests, profiles, claims planning, additional adapters, provenance review
records, report checking and literate documentation belong to the full
compatibility-kit change. The seed keeps those additions on the same reader,
content model and runner.
14 changes: 14 additions & 0 deletions conformance/deviations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Ecosystem deviations

Baseline: `finos/morphir@cde7722eb35987042a49dc646a846d693b0bd7f6`.
These entries explain the planned compatibility choices. They are not claims
that the new frontend or guest has passed qualification.

| Area | Recorded difference and decision | Evidence |
| --- | --- | --- |
| Compile streaming | The pinned Rust CLI implementation writes one compact report for `--json-lines`. The .NET plan emits events followed by a completed report. Keep this visible until an upstream agreement resolves it. | [STRM-2 and follow-up](../openspec/changes/add-fsharp-compile/design.html#strm-2), [planned upstream follow-ups](../openspec/changes/add-fsharp-compile/tasks.md#workflow-follow-up) |
| MEP guest qualification | The pinned generic guest kit requires legacy 0.1 and both frontend and backend capabilities. The planned F# guest offers only the current model-tier frontend contract. It will not add legacy protocol or a backend to satisfy the older kit. | [MEP-3](../openspec/changes/add-fsharp-compile/design.html#mep-3), [pinned contract provenance](../tests/Fixtures/Mep/PROVENANCE.md) |

Upstream issue links and qualification reports are added when those follow-ups
run. This seed does not claim an upstream guest-kit qualification or a released
kit supersession.
63 changes: 63 additions & 0 deletions conformance/steps/steps-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# MCK seed step vocabulary 1.0

This is the closed vocabulary for the F# frontend seed. The kit and report
version is `0.1.0-draft.1`, independent of IR, CLI and MEP versions.
`Morphir.Conformance.Corpus.read` refuses other vocabulary versions.

Features live under `suites/frontend-fsharp/`, declare
`@suite:frontend-fsharp`, and use stable `frontend-fsharp-NNNN` scenario ids.
The runner selects the implementation. Adapter, executable and implementation
selection tags are refused. Features must not name an adapter.

The supported tags are `@suite:frontend-fsharp`, `@semantic`, `@refusal`,
`@basis:hand-authored`, `@basis:spec-derived` and `@excluded`.
`@excluded` is valid only on Examples. Unknown eligibility metadata, including
known-gap tags, is refused until that behavior is implemented by a later
vocabulary version.

| Step text | Argument and meaning |
| --- | --- |
| `the F# package "<package>" exposing "<modules>"` | First step; declares the package and its exposed module. The seed takes one module name. |
| `the source "<uri>":` | A `fsharp` docstring containing the complete source document. URIs are unique portable root-relative paths. Step order is source order. |
| `it is compiled from <input> with IR version <version>` | Exactly one compile step. Both placeholders are supplied by the outline's Examples rows. Input is `files`, `directory` or `project file`; version keeps its exact spelling. |
| `compilation succeeds with no error diagnostics` | Exactly one outcome step. Success must be true, a JSON model must be present, and no error diagnostic is allowed. Non-error diagnostics are allowed by this step. |
| `compilation fails with diagnostics:` | Exactly one outcome step. A `json` docstring holds the expected diagnostic array in the MEP shape. Success must be false, no model may be returned, and diagnostics must match severity, code, URI and range exactly, in document/position order. Messages are not compared. No extra diagnostic is allowed. |
| `the IR equals "<path>"` | Adds a complete expected-IR sidecar, relative to the feature. The path may contain `<version>` and `<input>`. Comparison ignores JSON object-member order; arrays, strings, scalar kinds and number spellings remain exact. No other normalization is applied. |

The Given/When/Then/And keywords are Gherkin syntax, not part of the step text.
Other steps or argument shapes fail linting. The seed supports scenarios directly
under a feature, not Background or Rule inheritance. Generalization is tracked
in the separate `establish-morphir-compatibility-kit` change.

Every scenario explicitly lists the input × version matrix supplied to the
reader. Examples have `input` and `version` columns. An `@excluded` Examples
block also needs a non-empty `reason` for each row. Missing combinations,
duplicate combinations and reused case ids are errors. Exclusions remain in the
report and never invoke the adapter.

Positions are zero-based UTF-16 code units with exclusive ends. The diagnostic
shape is:

```json
{
"severity": "error",
"code": "fsharp.unsupported_mutable",
"location": {
"uri": "Pricing.fs",
"range": {
"start": {"line": 2, "character": 8},
"end": {"line": 2, "character": 15}
}
}
}
```

Reports keep requests, observations, verdicts and the first comparison difference
in plan order. They contain no timestamps. Their case-set SHA-256 includes all
captured artifact bytes, including vocabulary and sidecars, sorted by ordinal
portable path. Each file contributes its UTF-8 path byte length in invariant
decimal followed by a colon, path bytes, content length in the same framing,
then content bytes.

Model observations are stored as exact `modelJson` text, including malformed
payloads, so a failing implementation cannot prevent report serialization.
33 changes: 33 additions & 0 deletions conformance/suites/frontend-fsharp/mutable.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@suite:frontend-fsharp @refusal @basis:hand-authored
Feature: Refuse mutable bindings
Morphir values are immutable. This expectation is written from the frontend
contract, not captured from a compiler. See the fsharp-source-frontend spec.

Scenario Outline: frontend-fsharp-0001 Mutable bindings have a precise refusal
Given the F# package "acme/demo" exposing "Pricing"
And the source "Pricing.fs":
"""fsharp
namespace Acme.Demo
module Pricing =
let mutable total = 0
"""
When it is compiled from <input> with IR version <version>
Then compilation fails with diagnostics:
"""json
[{"severity":"error","code":"fsharp.unsupported_mutable","location":{"uri":"Pricing.fs","range":{"start":{"line":2,"character":8},"end":{"line":2,"character":15}}}}]
"""

Examples:
| input | version |
| files | 1 |
| files | 2 |
| files | 3 |
| files | 4 |
| directory | 1 |
| directory | 2 |
| directory | 3 |
| directory | 4 |
| project file | 1 |
| project file | 2 |
| project file | 3 |
| project file | 4 |
1 change: 1 addition & 0 deletions docs/content/contributing/design/prds/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

| PRD | Status | Phase | Current task |
| --- | --- | --- | --- |
| [F# source frontend and compile](./fsharp-source-frontend.md) | In Progress | Tracking and acceptance | #499; compile acceptance and native compilation contracts |
| [C# generation CLI](./integrate-csharp-generation-cli.md) | Implementation landed | Specification archive | #482 squash landed; 9/9 tasks, both deltas synced and archived; archive follow-up landing and coordinator cleanup pending, [#481](https://github.com/finos/morphir-dotnet/issues/481) |
| [C# code generation](./add-csharp-code-generation.md) | Implementation landed | Archive follow-up | #478 squash landed; 13/13 tasks, spec synced and archive prepared; follow-up landing and coordinator cleanup pending, #474 |
| [Immutable C# SDK](./add-immutable-csharp-sdk.md) | Implementation and archive landed | Cleanup pending | #470 and archive #472 landed; implementation worktree/artifacts removed; empty archive directory cleanup pending |
Expand Down
Loading
Loading