From 902343512943475bddd92c6d8815eb0440b21a36 Mon Sep 17 00:00:00 2001
From: Tomas Grosup <Tomas.Grosup@gmail.com>
Date: Sat, 17 Jan 2026 16:10:36 +0100
Subject: [PATCH 01/14] Srtp bugfix for curryN via applydefaults

---
 EXECUTIVE_SUMMARY.md                          | 77 +++++++++++++++++++
 azure-pipelines-PR.yml                        |  9 +++
 src/Compiler/Checking/CheckDeclarations.fs    |  7 +-
 .../IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs     | 36 +++++++++
 4 files changed, 127 insertions(+), 2 deletions(-)
 create mode 100644 EXECUTIVE_SUMMARY.md

diff --git a/EXECUTIVE_SUMMARY.md b/EXECUTIVE_SUMMARY.md
new file mode 100644
index 00000000000..9937985ccc9
--- /dev/null
+++ b/EXECUTIVE_SUMMARY.md
@@ -0,0 +1,77 @@
+# FSharpPlus curryN Regression Fix - Executive Summary
+
+## The Bug
+
+FSharpPlus `curryN` pattern worked in F# SDK 8.0 but failed in SDK 9.0+ with error FS0030 (value restriction):
+
+```fsharp
+let _x1 = curryN f1 100  // SDK 8: OK | SDK 9+: FS0030 error
+```
+
+## Root Cause
+
+In `CheckDeclarations.fs`, the `ApplyDefaults` function processes unsolved type variables at the end of type checking. The existing code only solved typars with `StaticReq <> None`:
+
+```fsharp
+if (tp.StaticReq <> TyparStaticReq.None) then
+    ChooseTyparSolutionAndSolve cenv.css denvAtEnd tp
+```
+
+**The problem**: Some SRTP (Statically Resolved Type Parameter) typars have `MayResolveMember` constraints but `StaticReq=None`. These typars were being skipped, leaving them unsolved. When `CheckValueRestriction` ran next, it found unsolved typars and reported FS0030.
+
+## Why This Is a Bug
+
+The `ApplyDefaults` code checks `StaticReq <> None` to identify SRTP typars that need solving. However, a typar may participate in an SRTP constraint (having a `MayResolveMember` constraint) without having `StaticReq` set. This can happen when:
+
+1. The typar is the **result type** of an SRTP method call, not the head type
+2. The typar is constrained through SRTP constraints but isn't directly marked with `^`
+
+**Key insight from instrumentation:**
+```
+  - ? (StaticReq=None, Constraints=[MayResolveMember, CoercesTo])  ← HAS SRTP CONSTRAINT!
+[ApplyDefaults] After processing: 17 still unsolved  ← SRTP typar SKIPPED because StaticReq=None
+```
+
+The condition `tp.StaticReq <> None` was too narrow - it missed typars that have SRTP constraints but no explicit static requirement.
+
+## Regression Analysis - Git Blame Evidence
+
+**Root Cause: PR #15181 (commit `b73be1584`) - Nullness Checking Feature**
+
+The regression was introduced by `FreshenTypar` added in PR #15181:
+
+```fsharp
+// src/Compiler/Checking/NameResolution.fs:1600-1604
+let FreshenTypar (g: TcGlobals) rigid (tp: Typar) =
+    let clearStaticReq = g.langVersion.SupportsFeature LanguageFeature.InterfacesWithAbstractStaticMembers
+    let staticReq = if clearStaticReq then TyparStaticReq.None else tp.StaticReq  // ← BUG!
+    ...
+```
+
+**The Mechanism:**
+
+1. **SDK 8**: `FreshenTypar` did not exist. When typars were freshened, `StaticReq` was preserved from the original typar.
+
+2. **SDK 9+**: When `InterfacesWithAbstractStaticMembers` is enabled (always on), `FreshenTypar` **clears `StaticReq` to `None`** unconditionally.
+
+3. **Effect**: SRTP typars still have `MayResolveMember` constraints, but lose their `StaticReq` marker.
+
+4. **Consequence**: `ApplyDefaults` checks `if tp.StaticReq <> None` → returns false → typar never solved → FS0030 error.
+
+**The fix** adds an alternative check for `MayResolveMember` constraints directly, making `ApplyDefaults` robust against this `StaticReq` clearing.
+
+## The Fix
+
+Added a check for `MayResolveMember` constraints in addition to `StaticReq`:
+
+```fsharp
+let hasSRTPConstraint = tp.Constraints |> List.exists (function TyparConstraint.MayResolveMember _ -> true | _ -> false)
+if (tp.StaticReq <> TyparStaticReq.None) || hasSRTPConstraint then
+    ChooseTyparSolutionAndSolve cenv.css denvAtEnd tp
+```
+
+## Verification
+
+- ✅ New curryN-style regression test passes
+- ✅ FSharpPlus `curryN` pattern compiles without type annotations
+- ✅ No regressions introduced in SRTP-related tests
diff --git a/azure-pipelines-PR.yml b/azure-pipelines-PR.yml
index 83919f27499..e4049c2ae01 100644
--- a/azure-pipelines-PR.yml
+++ b/azure-pipelines-PR.yml
@@ -914,3 +914,12 @@ stages:
           buildScript: build.sh
           displayName: FSharpPlus_Linux
           useVmImage: $(UbuntuMachineQueueName)
+        - repo: fsprojects/FSharpPlus
+          commit: 2648efe
+          buildScript: build.cmd
+          displayName: FsharpPlus_NET10
+        - repo: fsprojects/FSharpPlus
+          commit: 2648efe
+          buildScript: build.sh
+          displayName: FsharpPlus_Net10_Linux
+          useVmImage: $(UbuntuMachineQueueName)
diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs
index ce1bd58263d..a5139a1cea7 100644
--- a/src/Compiler/Checking/CheckDeclarations.fs
+++ b/src/Compiler/Checking/CheckDeclarations.fs
@@ -5672,10 +5672,13 @@ let ApplyDefaults (cenv: cenv) g denvAtEnd m moduleContents extraAttribs =
                     // the defaults will be propagated to the new type variable. 
                     ApplyTyparDefaultAtPriority denvAtEnd cenv.css priority tp)
 
-        // OK, now apply defaults for any unsolved TyparStaticReq.HeadType 
+        // OK, now apply defaults for any unsolved TyparStaticReq.HeadType or typars with SRTP (MayResolveMember) constraints
+        // Note: We also check for MayResolveMember constraints because some SRTP typars may not have StaticReq set
+        // (this can happen when the typar is involved in an SRTP constraint but isn't the "head type" itself)
         unsolved |> List.iter (fun tp ->     
             if not tp.IsSolved then 
-                if (tp.StaticReq <> TyparStaticReq.None) then
+                let hasSRTPConstraint = tp.Constraints |> List.exists (function TyparConstraint.MayResolveMember _ -> true | _ -> false)
+                if (tp.StaticReq <> TyparStaticReq.None) || hasSRTPConstraint then
                     ChooseTyparSolutionAndSolve cenv.css denvAtEnd tp)
     with RecoverableException exn ->
         errorRecovery exn m
diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs
index b5fe18bac80..2f904cf2874 100644
--- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs
+++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs
@@ -1767,3 +1767,39 @@ printfn "Success: %d" result
         |> compileAndRun
         |> shouldSucceed
 
+    // Regression test for GitHub issue #18344 and FSharpPlus curryN pattern
+    // This tests that SRTP typars with MayResolveMember constraints are properly solved
+    // even when StaticReq is None
+    [<Fact>]
+    let ``SRTP curryN-style pattern should compile without value restriction error`` () =
+        FSharp """
+module CurryNTest
+
+open System
+
+// Minimal reproduction of the FSharpPlus curryN pattern
+type Curry =
+    static member inline Invoke f =
+        let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member Curry: _*_ -> _) b, a)
+        call_2 (Unchecked.defaultof<Curry>, Unchecked.defaultof<'t>) (f: 't -> 'r) : 'args
+    
+    static member Curry (_: Tuple<'t1>        , _: Curry) = fun f t1                   -> f (Tuple<_> t1)
+    static member Curry ((_, _)               , _: Curry) = fun f t1 t2                -> f (t1, t2)
+    static member Curry ((_, _, _)            , _: Curry) = fun f t1 t2 t3             -> f (t1, t2, t3)
+
+let inline curryN f = Curry.Invoke f
+
+// Test functions
+let f1  (x: Tuple<_>) = [x.Item1]
+let f2  (x, y)    = [x + y]
+let f3  (x, y, z) = [x + y + z]
+
+// These should compile without value restriction error (regression test for #18344)
+let _x1 = curryN f1 100
+let _x2 = curryN f2 1 2
+let _x3 = curryN f3 1 2 3
+    """
+        |> asLibrary
+        |> compile
+        |> shouldSucceed
+

From 27a51e8121d70b69d543cb4581636a2a27940ca0 Mon Sep 17 00:00:00 2001
From: Tomas Grosup <Tomas.Grosup@gmail.com>
Date: Sat, 17 Jan 2026 19:55:53 +0100
Subject: [PATCH 02/14] Update azure-pipelines-PR.yml

---
 azure-pipelines-PR.yml | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/azure-pipelines-PR.yml b/azure-pipelines-PR.yml
index e4049c2ae01..196a0592e62 100644
--- a/azure-pipelines-PR.yml
+++ b/azure-pipelines-PR.yml
@@ -918,6 +918,11 @@ stages:
           commit: 2648efe
           buildScript: build.cmd
           displayName: FsharpPlus_NET10
+         # remove this before merging
+        - repo: fsprojects/FSharpPlus
+          commit: 2648efe
+          buildScript: dotnet msbuild build.proj -target:Build;Pack;Test;AllDocs
+          displayName: FsharpPlus_NET10_FullTestSuite 
         - repo: fsprojects/FSharpPlus
           commit: 2648efe
           buildScript: build.sh

From e979d4be841fa9ecbc2ae9d02b36532ca3e35217 Mon Sep 17 00:00:00 2001
From: Tomas Grosup <Tomas.Grosup@gmail.com>
Date: Sat, 17 Jan 2026 20:45:26 +0100
Subject: [PATCH 03/14] Apply suggestion from @T-Gro

---
 azure-pipelines-PR.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/azure-pipelines-PR.yml b/azure-pipelines-PR.yml
index 196a0592e62..71193c5082e 100644
--- a/azure-pipelines-PR.yml
+++ b/azure-pipelines-PR.yml
@@ -921,7 +921,7 @@ stages:
          # remove this before merging
         - repo: fsprojects/FSharpPlus
           commit: 2648efe
-          buildScript: dotnet msbuild build.proj -target:Build;Pack;Test;AllDocs
+          buildScript: dotnet msbuild build.proj -target:"Build;Pack;Test;AllDocs"
           displayName: FsharpPlus_NET10_FullTestSuite 
         - repo: fsprojects/FSharpPlus
           commit: 2648efe

From 04cfafc2de366de49bb745c74429116c105e765c Mon Sep 17 00:00:00 2001
From: Tomas Grosup <Tomas.Grosup@gmail.com>
Date: Sun, 18 Jan 2026 20:17:50 +0100
Subject: [PATCH 04/14] Apply suggestion from @T-Gro

---
 azure-pipelines-PR.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/azure-pipelines-PR.yml b/azure-pipelines-PR.yml
index 71193c5082e..1102278d7f9 100644
--- a/azure-pipelines-PR.yml
+++ b/azure-pipelines-PR.yml
@@ -921,7 +921,7 @@ stages:
          # remove this before merging
         - repo: fsprojects/FSharpPlus
           commit: 2648efe
-          buildScript: dotnet msbuild build.proj -target:"Build;Pack;Test;AllDocs"
+          buildScript: dotnet msbuild build.proj -target:'Build;Pack;Test;AllDocs'
           displayName: FsharpPlus_NET10_FullTestSuite 
         - repo: fsprojects/FSharpPlus
           commit: 2648efe

From c98d9f5e63aa8d56a8b7788ffa12342a6fe51f4c Mon Sep 17 00:00:00 2001
From: Tomas Grosup <Tomas.Grosup@gmail.com>
Date: Sun, 18 Jan 2026 20:20:34 +0100
Subject: [PATCH 05/14] Apply suggestion from @T-Gro

---
 azure-pipelines-PR.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/azure-pipelines-PR.yml b/azure-pipelines-PR.yml
index 1102278d7f9..e2d8c204887 100644
--- a/azure-pipelines-PR.yml
+++ b/azure-pipelines-PR.yml
@@ -921,7 +921,7 @@ stages:
          # remove this before merging
         - repo: fsprojects/FSharpPlus
           commit: 2648efe
-          buildScript: dotnet msbuild build.proj -target:'Build;Pack;Test;AllDocs'
+          buildScript: dotnet msbuild build.proj -t:Build`;Pack`;Test`;AllDocs
           displayName: FsharpPlus_NET10_FullTestSuite 
         - repo: fsprojects/FSharpPlus
           commit: 2648efe

From fa1f33e255d8e25965cfec48d0dea2a7b2294109 Mon Sep 17 00:00:00 2001
From: Tomas Grosup <Tomas.Grosup@gmail.com>
Date: Mon, 19 Jan 2026 13:14:23 +0100
Subject: [PATCH 06/14] Fix buildScript syntax in azure-pipelines-PR.yml

---
 azure-pipelines-PR.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/azure-pipelines-PR.yml b/azure-pipelines-PR.yml
index e2d8c204887..2ac87e6157e 100644
--- a/azure-pipelines-PR.yml
+++ b/azure-pipelines-PR.yml
@@ -921,7 +921,7 @@ stages:
          # remove this before merging
         - repo: fsprojects/FSharpPlus
           commit: 2648efe
-          buildScript: dotnet msbuild build.proj -t:Build`;Pack`;Test`;AllDocs
+          buildScript: dotnet msbuild build.proj -t:Build;Pack;Test;AllDocs
           displayName: FsharpPlus_NET10_FullTestSuite 
         - repo: fsprojects/FSharpPlus
           commit: 2648efe

From ac6123b2c3f3e91a701116f10f393f3eab837892 Mon Sep 17 00:00:00 2001
From: Tomas Grosup <Tomas.Grosup@gmail.com>
Date: Mon, 19 Jan 2026 13:15:09 +0100
Subject: [PATCH 07/14] Update regression-test-jobs.yml

---
 eng/templates/regression-test-jobs.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/eng/templates/regression-test-jobs.yml b/eng/templates/regression-test-jobs.yml
index 3216eeaed4e..74f81e06820 100644
--- a/eng/templates/regression-test-jobs.yml
+++ b/eng/templates/regression-test-jobs.yml
@@ -153,7 +153,7 @@ jobs:
         Write-Host "============================================"
         Write-Host ""
         
-        $buildScript = "${{ item.buildScript }}"
+        $buildScript = '${{ item.buildScript }}'
         
         # Check if it's a built-in dotnet command or a file-based script
         # Note: buildScript comes from the pipeline YAML configuration (testMatrix parameter),

From c18d6cf6211cb420209530953763f639e1ecce2c Mon Sep 17 00:00:00 2001
From: Tomas Grosup <Tomas.Grosup@gmail.com>
Date: Mon, 19 Jan 2026 14:03:11 +0100
Subject: [PATCH 08/14] Add release notes for SRTP resolution fix

---
 docs/release-notes/.FSharp.Compiler.Service/10.0.200.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.200.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.200.md
index 54f1b13f05a..e82d8bdb6a6 100644
--- a/docs/release-notes/.FSharp.Compiler.Service/10.0.200.md
+++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.200.md
@@ -1,5 +1,6 @@
 ### Fixed
 
+* Fixed SRTP resolution regression causing FS0030 value restriction errors with FSharpPlus curryN-style patterns in .NET 9 SDK. ([PR #19218](https://github.com/dotnet/fsharp/pull/19218))
 * Type relations cache: optimize key generation ([Issue #19116](https://github.com/dotnet/fsharp/issues/18767)) ([PR #19120](https://github.com/dotnet/fsharp/pull/19120))
 * Fixed QuickParse to correctly handle optional parameter syntax with `?` prefix, resolving syntax highlighting issues. ([Issue #11008753](https://developercommunity.visualstudio.com/t/F-Highlighting-fails-on-optional-parame/11008753)) ([PR #XXXXX](https://github.com/dotnet/fsharp/pull/XXXXX))
 * Fix `--preferreduilang` switch leaking into `fsi.CommandLineArgs` when positioned after script file ([PR #19151](https://github.com/dotnet/fsharp/pull/19151))

From 0f0e96cf93cafcace62552557c43240ea73c5115 Mon Sep 17 00:00:00 2001
From: Tomas Grosup <Tomas.Grosup@gmail.com>
Date: Mon, 19 Jan 2026 16:11:42 +0100
Subject: [PATCH 09/14] Update regression-test-jobs.yml

---
 eng/templates/regression-test-jobs.yml | 218 +++++++++++++++----------
 1 file changed, 131 insertions(+), 87 deletions(-)

diff --git a/eng/templates/regression-test-jobs.yml b/eng/templates/regression-test-jobs.yml
index 74f81e06820..1e9996f1faf 100644
--- a/eng/templates/regression-test-jobs.yml
+++ b/eng/templates/regression-test-jobs.yml
@@ -5,13 +5,13 @@ parameters:
 - name: testMatrix
   type: object
 - name: dependsOn
-  type: string
+  type:  string
   default: 'EndToEndBuildTests'
 
 jobs:
 - ${{ each item in parameters.testMatrix }}:
   - job: RegressionTest_${{ replace(item.displayName, '-', '_') }}_${{ replace(replace(item.repo, '/', '_'), '-', '_') }}
-    displayName: '${{ item.displayName }} Regression Test'
+    displayName:  '${{ item.displayName }} Regression Test'
     dependsOn: ${{ parameters.dependsOn }}
     ${{ if item.useVmImage }}:
       pool:
@@ -19,7 +19,7 @@ jobs:
     ${{ else }}:
       pool:
         name: $(DncEngPublicBuildPool)
-        demands: ImageOverride -equals $(_WindowsMachineQueueName)
+        demands:  ImageOverride -equals $(_WindowsMachineQueueName)
     timeoutInMinutes: 60
     steps:
     - checkout: self
@@ -27,7 +27,7 @@ jobs:
 
     - task: DownloadPipelineArtifact@2
       displayName: Download F# Compiler FSC Artifacts
-      inputs:
+      inputs: 
         artifactName: 'FSharpCompilerFscArtifacts'
         downloadPath: '$(Pipeline.Workspace)/FSharpCompiler/artifacts/bin/fsc'
 
@@ -35,53 +35,50 @@ jobs:
       displayName: Download F# Core Artifacts
       inputs:
         artifactName: 'FSharpCoreArtifacts'
-        downloadPath: '$(Pipeline.Workspace)/FSharpCompiler/artifacts/bin/FSharp.Core'
+        downloadPath: '$(Pipeline. Workspace)/FSharpCompiler/artifacts/bin/FSharp.Core'
 
     - task: DownloadPipelineArtifact@2
-      displayName: Download UseLocalCompiler props
+      displayName:  Download UseLocalCompiler props
       inputs:
         artifactName: 'UseLocalCompilerProps'
         downloadPath: '$(Pipeline.Workspace)/Props'
 
     - pwsh: |
-        Write-Host "Cloning repository: ${{ item.repo }}"
+        Write-Host "Cloning repository:  ${{ item.repo }}"
         git clone --recursive https://github.com/${{ item.repo }}.git $(Pipeline.Workspace)/TestRepo
-        Set-Location $(Pipeline.Workspace)/TestRepo
+        Set-Location $(Pipeline. Workspace)/TestRepo
         
-        Write-Host "Checking out commit: ${{ item.commit }}"
-        git checkout ${{ item.commit }}
+        Write-Host "Checking out commit:  ${{ item.commit }}"
+        git checkout ${{ item. commit }}
         
         Write-Host "Initializing submodules (if any)..."
         git submodule update --init --recursive
         
-        Write-Host "Successfully checked out ${{ item.repo }} at commit ${{ item.commit }}"
+        Write-Host "Successfully checked out ${{ item.repo }} at commit ${{ item. commit }}"
         git log -1 --oneline
         
         Write-Host "Repository structure:"
         Get-ChildItem -Name
         
-        # Check if buildScript is a built-in dotnet command or a file-based script
-        # Note: buildScript comes from the pipeline YAML configuration (testMatrix parameter),
-        # not from external user input, so it's safe to use
-        $buildScript = "${{ item.buildScript }}"
+        $buildScript = '${{ item.buildScript }}'
         if ($buildScript -like "dotnet*") {
-          Write-Host "Build command is a built-in dotnet command: $buildScript"
+          Write-Host "Build command is a built-in dotnet command:  $buildScript"
           Write-Host "Skipping file existence check for built-in command"
         } else {
-          Write-Host "Verifying build script exists: $buildScript"
+          Write-Host "Verifying build script exists:  $buildScript"
           if (Test-Path $buildScript) {
             Write-Host "Build script found: $buildScript"
           } else {
-            Write-Host "Build script not found: $buildScript"
+            Write-Host "Build script not found:  $buildScript"
             Write-Host "Available files in root:"
             Get-ChildItem
             exit 1
           }
         }
-      displayName: Checkout ${{ item.displayName }} at specific commit
+      displayName:  Checkout ${{ item.displayName }} at specific commit
 
     - pwsh: |
-        Set-Location $(Pipeline.Workspace)/TestRepo
+        Set-Location $(Pipeline. Workspace)/TestRepo
         Write-Host "Removing global.json to use latest SDK..."
         if (Test-Path "global.json") {
           Remove-Item "global.json" -Force
@@ -92,24 +89,24 @@ jobs:
       displayName: Remove global.json to use latest SDK
 
     - task: UseDotNet@2
-      displayName: Install .NET SDK 8.0.x for ${{ item.displayName }}
+      displayName:  Install .NET SDK 8.0.x for ${{ item.displayName }}
       inputs:
         packageType: sdk
         version: '8.0.x'
-        installationPath: $(Pipeline.Workspace)/TestRepo/.dotnet
+        installationPath: $(Pipeline.Workspace)/TestRepo/. dotnet
 
     - task: UseDotNet@2
-      displayName: Install .NET SDK 10.0.100 for ${{ item.displayName }}
+      displayName:  Install .NET SDK 10.0.100 for ${{ item.displayName }}
       inputs:
         packageType: sdk
         version: '10.0.100'
-        installationPath: $(Pipeline.Workspace)/TestRepo/.dotnet
+        installationPath: $(Pipeline. Workspace)/TestRepo/.dotnet
 
     - pwsh: |
-        Set-Location $(Pipeline.Workspace)/TestRepo
+        Set-Location $(Pipeline. Workspace)/TestRepo
         
-        Write-Host "Running PrepareRepoForRegressionTesting.fsx..."
-        dotnet fsi $(Build.SourcesDirectory)/eng/scripts/PrepareRepoForRegressionTesting.fsx "$(Pipeline.Workspace)/Props/UseLocalCompiler.Directory.Build.props"
+        Write-Host "Running PrepareRepoForRegressionTesting. fsx..."
+        dotnet fsi $(Build.SourcesDirectory)/eng/scripts/PrepareRepoForRegressionTesting.fsx "$(Pipeline.Workspace)/Props/UseLocalCompiler.Directory. Build.props"
         
         if ($LASTEXITCODE -ne 0) {
           Write-Host "Failed to prepare repository for regression testing"
@@ -132,12 +129,12 @@ jobs:
         Write-Host ""
         Write-Host "F# Core available:"
         if (Test-Path "$(Pipeline.Workspace)/FSharpCompiler/bin/FSharp.Core/Release/netstandard2.0/FSharp.Core.dll") {
-          Write-Host "FSharp.Core.dll found"
+          Write-Host "FSharp.Core. dll found"
         } else {
-          Write-Host "FSharp.Core.dll not found"
+          Write-Host "FSharp.Core. dll not found"
         }
         Write-Host ""
-        Write-Host "Directory.Build.props content:"
+        Write-Host "Directory. Build.props content:"
         Get-Content "Directory.Build.props"
         Write-Host ""
         Write-Host "==========================================="
@@ -147,29 +144,47 @@ jobs:
         Set-Location $(Pipeline.Workspace)/TestRepo
         Write-Host "============================================"
         Write-Host "Starting build for ${{ item.displayName }}"
-        Write-Host "Repository: ${{ item.repo }}"
+        Write-Host "Repository: ${{ item. repo }}"
         Write-Host "Commit: ${{ item.commit }}"
-        Write-Host "Build Script: ${{ item.buildScript }}"
+        Write-Host "Build Script: ${{ item. buildScript }}"
         Write-Host "============================================"
         Write-Host ""
         
         $buildScript = '${{ item.buildScript }}'
+        $errorLogPath = "$(Pipeline.Workspace)/build-errors. log"
+        $fullLogPath = "$(Pipeline.Workspace)/build-full. log"
+        
+        # Function to filter and capture important lines
+        function Process-BuildOutput {
+          param([string]$line)
+          Write-Host $line
+          if ($line -match ':  error ' -or 
+              $line -match ': warning ' -or 
+              $line -match 'FAILED|Failed!' -or
+              $line -match '^\s+X\s+' -or
+              $line -match 'Exception: ' -or
+              $line -match '^\s+at\s+.*\(\)' -or
+              $line -match 'Build FAILED') {
+            Add-Content -Path $errorLogPath -Value $line
+          }
+        }
         
-        # Check if it's a built-in dotnet command or a file-based script
-        # Note: buildScript comes from the pipeline YAML configuration (testMatrix parameter),
-        # not from external user input, so using Invoke-Expression is safe here
         if ($buildScript -like "dotnet*") {
           Write-Host "Executing built-in command: $buildScript"
-          Invoke-Expression $buildScript
+          Invoke-Expression $buildScript 2>&1 | Tee-Object -FilePath $fullLogPath | ForEach-Object {
+            Process-BuildOutput $_
+          }
         } elseif ($IsWindows) {
-          # Execute the provided script on Windows
           Write-Host "Executing file-based script: $buildScript"
-          & ".\$buildScript"
+          & ".\$buildScript" 2>&1 | Tee-Object -FilePath $fullLogPath | ForEach-Object {
+            Process-BuildOutput $_
+          }
         } else {
-          # Execute the provided script on Linux
           Write-Host "Executing file-based script: $buildScript"
           chmod +x "$buildScript"
-          bash -c "./$buildScript"
+          bash -c ". /$buildScript" 2>&1 | Tee-Object -FilePath $fullLogPath | ForEach-Object {
+            Process-BuildOutput $_
+          }
         }
         $exitCode = $LASTEXITCODE
         
@@ -182,36 +197,36 @@ jobs:
         if ($exitCode -ne 0) {
           exit $exitCode
         }
-      displayName: Build ${{ item.displayName }} with local F# compiler
+      displayName:  Build ${{ item.displayName }} with local F# compiler
       env:
-        LocalFSharpCompilerPath: $(Pipeline.Workspace)/FSharpCompiler
+        LocalFSharpCompilerPath: $(Pipeline. Workspace)/FSharpCompiler
         LoadLocalFSharpBuild: 'True'
         LocalFSharpCompilerConfiguration: Release
-      timeoutInMinutes: 45
+      timeoutInMinutes:  45
 
-    - pwsh: |
-        Set-Location $(Pipeline.Workspace)/TestRepo
-        $binlogDir = "$(Pipeline.Workspace)/BinaryLogs"
+    - pwsh:  |
+        Set-Location $(Pipeline. Workspace)/TestRepo
+        $binlogDir = "$(Pipeline. Workspace)/BinaryLogs"
         New-Item -ItemType Directory -Force -Path $binlogDir | Out-Null
 
-        Write-Host "Collecting .binlog files..."
+        Write-Host "Collecting . binlog files..."
         $binlogs = Get-ChildItem -Path "." -Filter "*.binlog" -Recurse -ErrorAction SilentlyContinue
-        if ($binlogs.Count -eq 0) {
-          Write-Host "No .binlog files found"
+        if ($binlogs. Count -eq 0) {
+          Write-Host "No . binlog files found"
         } else {
           foreach ($binlog in $binlogs) {
-            Write-Host "Copying: $($binlog.FullName)"
+            Write-Host "Copying:  $($binlog. FullName)"
             Copy-Item $binlog.FullName -Destination $binlogDir
           }
           Write-Host "Collected $($binlogs.Count) .binlog files"
         }
-      displayName: Collect Binary Logs
+      displayName:  Collect Binary Logs
       condition: always()
       continueOnError: true
 
     - task: PublishPipelineArtifact@1
-      displayName: Publish ${{ item.displayName }} Binary Logs
-      inputs:
+      displayName:  Publish ${{ item.displayName }} Binary Logs
+      inputs: 
         targetPath: '$(Pipeline.Workspace)/BinaryLogs'
         artifactName: '${{ item.displayName }}_BinaryLogs'
         publishLocation: pipeline
@@ -223,9 +238,10 @@ jobs:
         Write-Host ""
         Write-Host "============================================"
         Write-Host "Regression test completed for ${{ item.displayName }}"
-        Write-Host "Repository: ${{ item.repo }}"
+        Write-Host "Repository: ${{ item. repo }}"
         Write-Host "Commit: ${{ item.commit }}"
         Write-Host "Build Script: ${{ item.buildScript }}"
+        
         if ($env:AGENT_JOBSTATUS -eq "Succeeded") {
           Write-Host "Status: SUCCESS"
           Write-Host "The ${{ item.displayName }} library builds successfully with the new F# compiler"
@@ -233,45 +249,73 @@ jobs:
           Write-Host "Status: FAILED"
           Write-Host "The ${{ item.displayName }} library failed to build with the new F# compiler"
           
-          # Build multiline error message with reproduction steps
-          $lines = @(
-            "Regression test FAILED for ${{ item.displayName }} (${{ item.repo }}@${{ item.commit }})",
-            "",
-            "LOCAL REPRODUCTION STEPS (from fsharp repo root):",
-            "==========================================",
-            "# 1. Build the F# compiler",
-            "./build.sh -c Release",
-            "",
-            "# 2. Clone and checkout the failing library",
-            "cd ..",
-            "git clone --recursive https://github.com/${{ item.repo }}.git TestRepo",
-            "cd TestRepo",
-            "git checkout ${{ item.commit }}",
-            "git submodule update --init --recursive",
-            "rm -f global.json",
-            "",
-            "# 3. Prepare the repo for local compiler",
-            "dotnet fsi ../fsharp/eng/scripts/PrepareRepoForRegressionTesting.fsx `"../fsharp/UseLocalCompiler.Directory.Build.props`"",
-            "",
-            "# 4. Build with local compiler",
-            "export LocalFSharpCompilerPath=`$PWD/../fsharp",
-            "export LoadLocalFSharpBuild=True",
-            "export LocalFSharpCompilerConfiguration=Release",
-            "./${{ item.buildScript }}",
-            "=========================================="
-          )
-          
-          # Report using VSO error format - each line separately
-          foreach ($line in $lines) {
-            Write-Host "##[error]$line"
+          $errorLogPath = "$(Pipeline.Workspace)/build-errors.log"
+          if (Test-Path $errorLogPath) {
+            Write-Host ""
+            Write-Host "##[error]============================================"
+            Write-Host "##[error]FAILURE SUMMARY for ${{ item.displayName }}"
+            Write-Host "##[error]============================================"
+            
+            $errors = Get-Content $errorLogPath | Select-Object -First 100
+            $errorCount = 0
+            $warningCount = 0
+            $testFailCount = 0
+            
+            foreach ($line in $errors) {
+              if ($line -match ': error ') {
+                Write-Host "##[error]$line"
+                $errorCount++
+              } elseif ($line -match ': warning ') {
+                Write-Host "##[warning]$line"
+                $warningCount++
+              } elseif ($line -match 'FAILED|Failed! |^\s+X\s+') {
+                Write-Host "##[error][TEST] $line"
+                $testFailCount++
+              } else {
+                Write-Host "##[error]$line"
+              }
+            }
+            
+            $totalLines = (Get-Content $errorLogPath | Measure-Object -Line).Lines
+            if ($totalLines -gt 100) {
+              Write-Host "##[warning]...  and $($totalLines - 100) more lines (see full log or binary logs)"
+            }
+            
+            Write-Host ""
+            Write-Host "##[error]Summary: $errorCount error(s), $warningCount warning(s), $testFailCount test failure(s)"
+            Write-Host "##[error]============================================"
+          } else {
+            Write-Host "##[warning]No error log captured - check full build output above"
           }
           
-          # Also log as VSO issue for Azure DevOps integration
+          Write-Host ""
+          Write-Host "##[section]LOCAL REPRODUCTION STEPS (from fsharp repo root):"
+          Write-Host "# 1. Build the F# compiler"
+          Write-Host "./build.sh -c Release"
+          Write-Host ""
+          Write-Host "# 2. Clone and checkout the failing library"
+          Write-Host "cd . ."
+          Write-Host "git clone --recursive https://github.com/${{ item.repo }}.git TestRepo"
+          Write-Host "cd TestRepo"
+          Write-Host "git checkout ${{ item.commit }}"
+          Write-Host "git submodule update --init --recursive"
+          Write-Host "rm -f global.json"
+          Write-Host ""
+          Write-Host "# 3. Prepare the repo for local compiler"
+          Write-Host "dotnet fsi ../fsharp/eng/scripts/PrepareRepoForRegressionTesting.fsx `"../fsharp/UseLocalCompiler.Directory.Build. props`""
+          Write-Host ""
+          Write-Host "# 4. Build with local compiler"
+          Write-Host "export LocalFSharpCompilerPath=`$PWD/../fsharp"
+          Write-Host "export LoadLocalFSharpBuild=True"
+          Write-Host "export LocalFSharpCompilerConfiguration=Release"
+          Write-Host "${{ item.buildScript }}"
+          
           Write-Host "##vso[task.logissue type=error;sourcepath=azure-pipelines-PR.yml]Regression test failed: ${{ item.displayName }}"
         }
         Write-Host "============================================"
         
+        Write-Host ""
         Write-Host "Binary logs found:"
-        Get-ChildItem "*.binlog" -Recurse -ErrorAction SilentlyContinue | ForEach-Object { Write-Host $_.FullName }
+        Get-ChildItem "*. binlog" -Recurse -ErrorAction SilentlyContinue | ForEach-Object { Write-Host $_. FullName }
       displayName: Report ${{ item.displayName }} test result
       condition: always()

From df496c718da4f1856ce5765e33e678d93d775d1b Mon Sep 17 00:00:00 2001
From: Tomas Grosup <Tomas.Grosup@gmail.com>
Date: Mon, 19 Jan 2026 16:58:36 +0100
Subject: [PATCH 10/14] Fix formatting issues in regression test YAML

---
 eng/templates/regression-test-jobs.yml | 60 +++++++++++++-------------
 1 file changed, 30 insertions(+), 30 deletions(-)

diff --git a/eng/templates/regression-test-jobs.yml b/eng/templates/regression-test-jobs.yml
index 1e9996f1faf..ac1195e3975 100644
--- a/eng/templates/regression-test-jobs.yml
+++ b/eng/templates/regression-test-jobs.yml
@@ -35,7 +35,7 @@ jobs:
       displayName: Download F# Core Artifacts
       inputs:
         artifactName: 'FSharpCoreArtifacts'
-        downloadPath: '$(Pipeline. Workspace)/FSharpCompiler/artifacts/bin/FSharp.Core'
+        downloadPath: '$(Pipeline.Workspace)/FSharpCompiler/artifacts/bin/FSharp.Core'
 
     - task: DownloadPipelineArtifact@2
       displayName:  Download UseLocalCompiler props
@@ -46,15 +46,15 @@ jobs:
     - pwsh: |
         Write-Host "Cloning repository:  ${{ item.repo }}"
         git clone --recursive https://github.com/${{ item.repo }}.git $(Pipeline.Workspace)/TestRepo
-        Set-Location $(Pipeline. Workspace)/TestRepo
+        Set-Location $(Pipeline.Workspace)/TestRepo
         
         Write-Host "Checking out commit:  ${{ item.commit }}"
-        git checkout ${{ item. commit }}
+        git checkout ${{ item.commit }}
         
         Write-Host "Initializing submodules (if any)..."
         git submodule update --init --recursive
         
-        Write-Host "Successfully checked out ${{ item.repo }} at commit ${{ item. commit }}"
+        Write-Host "Successfully checked out ${{ item.repo }} at commit ${{ item.commit }}"
         git log -1 --oneline
         
         Write-Host "Repository structure:"
@@ -78,7 +78,7 @@ jobs:
       displayName:  Checkout ${{ item.displayName }} at specific commit
 
     - pwsh: |
-        Set-Location $(Pipeline. Workspace)/TestRepo
+        Set-Location $(Pipeline.Workspace)/TestRepo
         Write-Host "Removing global.json to use latest SDK..."
         if (Test-Path "global.json") {
           Remove-Item "global.json" -Force
@@ -93,20 +93,20 @@ jobs:
       inputs:
         packageType: sdk
         version: '8.0.x'
-        installationPath: $(Pipeline.Workspace)/TestRepo/. dotnet
+        installationPath: $(Pipeline.Workspace)/TestRepo/.dotnet
 
     - task: UseDotNet@2
       displayName:  Install .NET SDK 10.0.100 for ${{ item.displayName }}
       inputs:
         packageType: sdk
         version: '10.0.100'
-        installationPath: $(Pipeline. Workspace)/TestRepo/.dotnet
+        installationPath: $(Pipeline.Workspace)/TestRepo/.dotnet
 
     - pwsh: |
-        Set-Location $(Pipeline. Workspace)/TestRepo
+        Set-Location $(Pipeline.Workspace)/TestRepo
         
-        Write-Host "Running PrepareRepoForRegressionTesting. fsx..."
-        dotnet fsi $(Build.SourcesDirectory)/eng/scripts/PrepareRepoForRegressionTesting.fsx "$(Pipeline.Workspace)/Props/UseLocalCompiler.Directory. Build.props"
+        Write-Host "Running PrepareRepoForRegressionTesting.fsx..."
+        dotnet fsi $(Build.SourcesDirectory)/eng/scripts/PrepareRepoForRegressionTesting.fsx "$(Pipeline.Workspace)/Props/UseLocalCompiler.Directory.Build.props"
         
         if ($LASTEXITCODE -ne 0) {
           Write-Host "Failed to prepare repository for regression testing"
@@ -129,12 +129,12 @@ jobs:
         Write-Host ""
         Write-Host "F# Core available:"
         if (Test-Path "$(Pipeline.Workspace)/FSharpCompiler/bin/FSharp.Core/Release/netstandard2.0/FSharp.Core.dll") {
-          Write-Host "FSharp.Core. dll found"
+          Write-Host "FSharp.Core.dll found"
         } else {
-          Write-Host "FSharp.Core. dll not found"
+          Write-Host "FSharp.Core.dll not found"
         }
         Write-Host ""
-        Write-Host "Directory. Build.props content:"
+        Write-Host "Directory.Build.props content:"
         Get-Content "Directory.Build.props"
         Write-Host ""
         Write-Host "==========================================="
@@ -144,15 +144,15 @@ jobs:
         Set-Location $(Pipeline.Workspace)/TestRepo
         Write-Host "============================================"
         Write-Host "Starting build for ${{ item.displayName }}"
-        Write-Host "Repository: ${{ item. repo }}"
+        Write-Host "Repository: ${{ item.repo }}"
         Write-Host "Commit: ${{ item.commit }}"
-        Write-Host "Build Script: ${{ item. buildScript }}"
+        Write-Host "Build Script: ${{ item.buildScript }}"
         Write-Host "============================================"
         Write-Host ""
         
         $buildScript = '${{ item.buildScript }}'
-        $errorLogPath = "$(Pipeline.Workspace)/build-errors. log"
-        $fullLogPath = "$(Pipeline.Workspace)/build-full. log"
+        $errorLogPath = "$(Pipeline.Workspace)/build-errors.log"
+        $fullLogPath = "$(Pipeline.Workspace)/build-full.log"
         
         # Function to filter and capture important lines
         function Process-BuildOutput {
@@ -182,7 +182,7 @@ jobs:
         } else {
           Write-Host "Executing file-based script: $buildScript"
           chmod +x "$buildScript"
-          bash -c ". /$buildScript" 2>&1 | Tee-Object -FilePath $fullLogPath | ForEach-Object {
+          bash -c "./$buildScript" 2>&1 | Tee-Object -FilePath $fullLogPath | ForEach-Object {
             Process-BuildOutput $_
           }
         }
@@ -199,23 +199,23 @@ jobs:
         }
       displayName:  Build ${{ item.displayName }} with local F# compiler
       env:
-        LocalFSharpCompilerPath: $(Pipeline. Workspace)/FSharpCompiler
+        LocalFSharpCompilerPath: $(Pipeline.Workspace)/FSharpCompiler
         LoadLocalFSharpBuild: 'True'
         LocalFSharpCompilerConfiguration: Release
       timeoutInMinutes:  45
 
     - pwsh:  |
-        Set-Location $(Pipeline. Workspace)/TestRepo
-        $binlogDir = "$(Pipeline. Workspace)/BinaryLogs"
+        Set-Location $(Pipeline.Workspace)/TestRepo
+        $binlogDir = "$(Pipeline.Workspace)/BinaryLogs"
         New-Item -ItemType Directory -Force -Path $binlogDir | Out-Null
 
-        Write-Host "Collecting . binlog files..."
+        Write-Host "Collecting .binlog files..."
         $binlogs = Get-ChildItem -Path "." -Filter "*.binlog" -Recurse -ErrorAction SilentlyContinue
-        if ($binlogs. Count -eq 0) {
-          Write-Host "No . binlog files found"
+        if ($binlogs.Count -eq 0) {
+          Write-Host "No .binlog files found"
         } else {
           foreach ($binlog in $binlogs) {
-            Write-Host "Copying:  $($binlog. FullName)"
+            Write-Host "Copying:  $($binlog.FullName)"
             Copy-Item $binlog.FullName -Destination $binlogDir
           }
           Write-Host "Collected $($binlogs.Count) .binlog files"
@@ -238,7 +238,7 @@ jobs:
         Write-Host ""
         Write-Host "============================================"
         Write-Host "Regression test completed for ${{ item.displayName }}"
-        Write-Host "Repository: ${{ item. repo }}"
+        Write-Host "Repository: ${{ item.repo }}"
         Write-Host "Commit: ${{ item.commit }}"
         Write-Host "Build Script: ${{ item.buildScript }}"
         
@@ -278,7 +278,7 @@ jobs:
             
             $totalLines = (Get-Content $errorLogPath | Measure-Object -Line).Lines
             if ($totalLines -gt 100) {
-              Write-Host "##[warning]...  and $($totalLines - 100) more lines (see full log or binary logs)"
+              Write-Host "##[warning]... and $($totalLines - 100) more lines (see full log or binary logs)"
             }
             
             Write-Host ""
@@ -294,7 +294,7 @@ jobs:
           Write-Host "./build.sh -c Release"
           Write-Host ""
           Write-Host "# 2. Clone and checkout the failing library"
-          Write-Host "cd . ."
+          Write-Host "cd .."
           Write-Host "git clone --recursive https://github.com/${{ item.repo }}.git TestRepo"
           Write-Host "cd TestRepo"
           Write-Host "git checkout ${{ item.commit }}"
@@ -302,7 +302,7 @@ jobs:
           Write-Host "rm -f global.json"
           Write-Host ""
           Write-Host "# 3. Prepare the repo for local compiler"
-          Write-Host "dotnet fsi ../fsharp/eng/scripts/PrepareRepoForRegressionTesting.fsx `"../fsharp/UseLocalCompiler.Directory.Build. props`""
+          Write-Host "dotnet fsi ../fsharp/eng/scripts/PrepareRepoForRegressionTesting.fsx `"../fsharp/UseLocalCompiler.Directory.Build.props`""
           Write-Host ""
           Write-Host "# 4. Build with local compiler"
           Write-Host "export LocalFSharpCompilerPath=`$PWD/../fsharp"
@@ -316,6 +316,6 @@ jobs:
         
         Write-Host ""
         Write-Host "Binary logs found:"
-        Get-ChildItem "*. binlog" -Recurse -ErrorAction SilentlyContinue | ForEach-Object { Write-Host $_. FullName }
+        Get-ChildItem "*.binlog" -Recurse -ErrorAction SilentlyContinue | ForEach-Object { Write-Host $_.FullName }
       displayName: Report ${{ item.displayName }} test result
       condition: always()

From e1eb4bfbad410029607478649bc190cec5d21d5e Mon Sep 17 00:00:00 2001
From: Tomas Grosup <Tomas.Grosup@gmail.com>
Date: Tue, 20 Jan 2026 09:46:54 +0100
Subject: [PATCH 11/14] Refactor script execution for cross-platform
 compatibility

---
 eng/templates/regression-test-jobs.yml | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/eng/templates/regression-test-jobs.yml b/eng/templates/regression-test-jobs.yml
index ac1195e3975..caf75e607b3 100644
--- a/eng/templates/regression-test-jobs.yml
+++ b/eng/templates/regression-test-jobs.yml
@@ -170,9 +170,15 @@ jobs:
         }
         
         if ($buildScript -like "dotnet*") {
-          Write-Host "Executing built-in command: $buildScript"
-          Invoke-Expression $buildScript 2>&1 | Tee-Object -FilePath $fullLogPath | ForEach-Object {
-            Process-BuildOutput $_
+          Write-Host "Executing built-in command:  $buildScript"
+          if ($IsWindows) {
+            cmd /c $buildScript 2>&1 | Tee-Object -FilePath $fullLogPath | ForEach-Object {
+              Process-BuildOutput $_
+            }
+          } else {
+            bash -c "$buildScript" 2>&1 | Tee-Object -FilePath $fullLogPath | ForEach-Object {
+              Process-BuildOutput $_
+            }
           }
         } elseif ($IsWindows) {
           Write-Host "Executing file-based script: $buildScript"
@@ -182,7 +188,7 @@ jobs:
         } else {
           Write-Host "Executing file-based script: $buildScript"
           chmod +x "$buildScript"
-          bash -c "./$buildScript" 2>&1 | Tee-Object -FilePath $fullLogPath | ForEach-Object {
+          bash -c ". /$buildScript" 2>&1 | Tee-Object -FilePath $fullLogPath | ForEach-Object {
             Process-BuildOutput $_
           }
         }

From 1beb01829676f1bec3ccb42a5b72ee42d7b59f2c Mon Sep 17 00:00:00 2001
From: Tomas Grosup <Tomas.Grosup@gmail.com>
Date: Tue, 20 Jan 2026 11:29:39 +0100
Subject: [PATCH 12/14] Fix script execution and extend timeout to 120 minutes

Updated script execution command and increased timeout.
---
 eng/templates/regression-test-jobs.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/eng/templates/regression-test-jobs.yml b/eng/templates/regression-test-jobs.yml
index caf75e607b3..4bb37c2c7d5 100644
--- a/eng/templates/regression-test-jobs.yml
+++ b/eng/templates/regression-test-jobs.yml
@@ -188,7 +188,7 @@ jobs:
         } else {
           Write-Host "Executing file-based script: $buildScript"
           chmod +x "$buildScript"
-          bash -c ". /$buildScript" 2>&1 | Tee-Object -FilePath $fullLogPath | ForEach-Object {
+          bash -c "./$buildScript" 2>&1 | Tee-Object -FilePath $fullLogPath | ForEach-Object {
             Process-BuildOutput $_
           }
         }
@@ -208,7 +208,7 @@ jobs:
         LocalFSharpCompilerPath: $(Pipeline.Workspace)/FSharpCompiler
         LoadLocalFSharpBuild: 'True'
         LocalFSharpCompilerConfiguration: Release
-      timeoutInMinutes:  45
+      timeoutInMinutes:  120
 
     - pwsh:  |
         Set-Location $(Pipeline.Workspace)/TestRepo

From 1e47dd8569b5fc83259a8e29639b870dcace482c Mon Sep 17 00:00:00 2001
From: Tomas Grosup <Tomas.Grosup@gmail.com>
Date: Tue, 20 Jan 2026 17:00:32 +0100
Subject: [PATCH 13/14] Apply suggestion from @T-Gro

---
 azure-pipelines-PR.yml | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/azure-pipelines-PR.yml b/azure-pipelines-PR.yml
index 2ac87e6157e..a08d88e3a1a 100644
--- a/azure-pipelines-PR.yml
+++ b/azure-pipelines-PR.yml
@@ -921,8 +921,12 @@ stages:
          # remove this before merging
         - repo: fsprojects/FSharpPlus
           commit: 2648efe
-          buildScript: dotnet msbuild build.proj -t:Build;Pack;Test;AllDocs
-          displayName: FsharpPlus_NET10_FullTestSuite 
+          buildScript: dotnet msbuild build.proj -t:Build;Test
+          displayName: FsharpPlus_NET10_Test
+        - repo: fsprojects/FSharpPlus
+          commit: 2648efe
+          buildScript: dotnet msbuild build.proj -t:Build;AllDocs
+          displayName: FsharpPlus_NET10_Docs
         - repo: fsprojects/FSharpPlus
           commit: 2648efe
           buildScript: build.sh

From 6fbc3e1a4c07c2c41950b1dc59d610f7605fcb58 Mon Sep 17 00:00:00 2001
From: Adam Boniecki <20281641+abonie@users.noreply.github.com>
Date: Thu, 22 Jan 2026 17:29:49 +0100
Subject: [PATCH 14/14] Delete EXECUTIVE_SUMMARY.md

---
 EXECUTIVE_SUMMARY.md | 77 --------------------------------------------
 1 file changed, 77 deletions(-)
 delete mode 100644 EXECUTIVE_SUMMARY.md

diff --git a/EXECUTIVE_SUMMARY.md b/EXECUTIVE_SUMMARY.md
deleted file mode 100644
index 9937985ccc9..00000000000
--- a/EXECUTIVE_SUMMARY.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# FSharpPlus curryN Regression Fix - Executive Summary
-
-## The Bug
-
-FSharpPlus `curryN` pattern worked in F# SDK 8.0 but failed in SDK 9.0+ with error FS0030 (value restriction):
-
-```fsharp
-let _x1 = curryN f1 100  // SDK 8: OK | SDK 9+: FS0030 error
-```
-
-## Root Cause
-
-In `CheckDeclarations.fs`, the `ApplyDefaults` function processes unsolved type variables at the end of type checking. The existing code only solved typars with `StaticReq <> None`:
-
-```fsharp
-if (tp.StaticReq <> TyparStaticReq.None) then
-    ChooseTyparSolutionAndSolve cenv.css denvAtEnd tp
-```
-
-**The problem**: Some SRTP (Statically Resolved Type Parameter) typars have `MayResolveMember` constraints but `StaticReq=None`. These typars were being skipped, leaving them unsolved. When `CheckValueRestriction` ran next, it found unsolved typars and reported FS0030.
-
-## Why This Is a Bug
-
-The `ApplyDefaults` code checks `StaticReq <> None` to identify SRTP typars that need solving. However, a typar may participate in an SRTP constraint (having a `MayResolveMember` constraint) without having `StaticReq` set. This can happen when:
-
-1. The typar is the **result type** of an SRTP method call, not the head type
-2. The typar is constrained through SRTP constraints but isn't directly marked with `^`
-
-**Key insight from instrumentation:**
-```
-  - ? (StaticReq=None, Constraints=[MayResolveMember, CoercesTo])  ← HAS SRTP CONSTRAINT!
-[ApplyDefaults] After processing: 17 still unsolved  ← SRTP typar SKIPPED because StaticReq=None
-```
-
-The condition `tp.StaticReq <> None` was too narrow - it missed typars that have SRTP constraints but no explicit static requirement.
-
-## Regression Analysis - Git Blame Evidence
-
-**Root Cause: PR #15181 (commit `b73be1584`) - Nullness Checking Feature**
-
-The regression was introduced by `FreshenTypar` added in PR #15181:
-
-```fsharp
-// src/Compiler/Checking/NameResolution.fs:1600-1604
-let FreshenTypar (g: TcGlobals) rigid (tp: Typar) =
-    let clearStaticReq = g.langVersion.SupportsFeature LanguageFeature.InterfacesWithAbstractStaticMembers
-    let staticReq = if clearStaticReq then TyparStaticReq.None else tp.StaticReq  // ← BUG!
-    ...
-```
-
-**The Mechanism:**
-
-1. **SDK 8**: `FreshenTypar` did not exist. When typars were freshened, `StaticReq` was preserved from the original typar.
-
-2. **SDK 9+**: When `InterfacesWithAbstractStaticMembers` is enabled (always on), `FreshenTypar` **clears `StaticReq` to `None`** unconditionally.
-
-3. **Effect**: SRTP typars still have `MayResolveMember` constraints, but lose their `StaticReq` marker.
-
-4. **Consequence**: `ApplyDefaults` checks `if tp.StaticReq <> None` → returns false → typar never solved → FS0030 error.
-
-**The fix** adds an alternative check for `MayResolveMember` constraints directly, making `ApplyDefaults` robust against this `StaticReq` clearing.
-
-## The Fix
-
-Added a check for `MayResolveMember` constraints in addition to `StaticReq`:
-
-```fsharp
-let hasSRTPConstraint = tp.Constraints |> List.exists (function TyparConstraint.MayResolveMember _ -> true | _ -> false)
-if (tp.StaticReq <> TyparStaticReq.None) || hasSRTPConstraint then
-    ChooseTyparSolutionAndSolve cenv.css denvAtEnd tp
-```
-
-## Verification
-
-- ✅ New curryN-style regression test passes
-- ✅ FSharpPlus `curryN` pattern compiles without type annotations
-- ✅ No regressions introduced in SRTP-related tests
