diff --git a/src/BuiltInTools/DotNetDeltaApplier/Microsoft.Extensions.DotNetDeltaApplier.csproj b/src/BuiltInTools/DotNetDeltaApplier/Microsoft.Extensions.DotNetDeltaApplier.csproj
index 9dd0d32e7fdb..47b120b6375e 100644
--- a/src/BuiltInTools/DotNetDeltaApplier/Microsoft.Extensions.DotNetDeltaApplier.csproj
+++ b/src/BuiltInTools/DotNetDeltaApplier/Microsoft.Extensions.DotNetDeltaApplier.csproj
@@ -6,8 +6,10 @@
     <!--
       dotnet-watch may inject this assembly to .NET 6.0+ app, so we can't target a newer version.
       At the same time source build requires us to not target 6.0, so we fall back to netstandard.
+
+      When updating these also update ProjectReferences in dotnet-watch.csproj.
      -->
-    <TargetFramework>netstandard2.1</TargetFramework>
+    <TargetFrameworks>netstandard2.1;net10.0</TargetFrameworks>
     <StrongNameKeyId>MicrosoftAspNetCore</StrongNameKeyId>
 
     <!-- NuGet -->
diff --git a/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs b/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs
index 59787ed4f47b..bff71aeaee23 100644
--- a/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs
+++ b/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs
@@ -13,7 +13,11 @@ internal sealed class StartupHook
     private const int ConnectionTimeoutMS = 5000;
 
     private static readonly bool s_logToStandardOutput = Environment.GetEnvironmentVariable(AgentEnvironmentVariables.HotReloadDeltaClientLogMessages) == "1";
-    private static readonly string s_namedPipeName = Environment.GetEnvironmentVariable(AgentEnvironmentVariables.DotNetWatchHotReloadNamedPipeName);
+    private static readonly string? s_namedPipeName = Environment.GetEnvironmentVariable(AgentEnvironmentVariables.DotNetWatchHotReloadNamedPipeName);
+
+#if NET10_0_OR_GREATER
+    private static PosixSignalRegistration? s_signalRegistration;
+#endif
 
     /// <summary>
     /// Invoked by the runtime when the containing assembly is listed in DOTNET_STARTUP_HOOKS.
@@ -22,12 +26,18 @@ public static void Initialize()
     {
         var processPath = Environment.GetCommandLineArgs().FirstOrDefault();
 
-        Log($"Loaded into process: {processPath}");
+        Log($"Loaded into process: {processPath} ({typeof(StartupHook).Assembly.Location})");
 
         HotReloadAgent.ClearHotReloadEnvironmentVariables(typeof(StartupHook));
 
         Log($"Connecting to hot-reload server");
 
+        if (s_namedPipeName == null)
+        {
+            Log($"Environment variable {AgentEnvironmentVariables.DotNetWatchHotReloadNamedPipeName} has no value");
+            return;
+        }
+
         // Connect to the pipe synchronously.
         //
         // If a debugger is attached and there is a breakpoint in the startup code connecting asynchronously would
@@ -48,22 +58,50 @@ public static void Initialize()
             return;
         }
 
+        RegisterPosixSignalHandlers();
+
         var agent = new HotReloadAgent();
         try
         {
             // block until initialization completes:
             InitializeAsync(pipeClient, agent, CancellationToken.None).GetAwaiter().GetResult();
 
+#pragma warning disable CA2025 // Ensure tasks using 'IDisposable' instances complete before the instances are disposed
             // fire and forget:
             _ = ReceiveAndApplyUpdatesAsync(pipeClient, agent, initialUpdates: false, CancellationToken.None);
+#pragma warning restore
         }
         catch (Exception ex)
         {
             Log(ex.Message);
             pipeClient.Dispose();
+            agent.Dispose();
         }
     }
 
+    private static void RegisterPosixSignalHandlers()
+    {
+#if NET10_0_OR_GREATER
+        // Register a handler for SIGTERM to allow graceful shutdown of the application on Unix.
+        // See https://github.com/dotnet/docs/issues/46226.
+
+        // Note: registered handlers are executed in reverse order of their registration.
+        // Since the startup hook is executed before any code of the application, it is the first handler registered and thus the last to run.
+
+        s_signalRegistration = PosixSignalRegistration.Create(PosixSignal.SIGTERM, context =>
+        {
+            Log($"SIGTERM received. Cancel={context.Cancel}");
+
+            if (!context.Cancel)
+            {
+                Environment.Exit(0);
+            }
+        });
+
+        Log("Posix signal handlers registered.");
+#endif
+    }
+
     private static async ValueTask InitializeAsync(NamedPipeClientStream pipeClient, HotReloadAgent agent, CancellationToken cancellationToken)
     {
         agent.Reporter.Report("Writing capabilities: " + agent.Capabilities, AgentMessageSeverity.Verbose);
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyAppModel.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyAppModel.cs
index b666406f2a91..dd2da0750c4f 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyAppModel.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyAppModel.cs
@@ -5,30 +5,23 @@
 
 namespace Microsoft.DotNet.Watch;
 
-internal abstract partial class HotReloadAppModel
+/// <summary>
+/// Blazor client-only WebAssembly app.
+/// </summary>
+internal sealed class BlazorWebAssemblyAppModel(ProjectGraphNode clientProject)
+    // Blazor WASM does not need agent injected as all changes are applied in the browser, the process being launched is a dev server.
+    : HotReloadAppModel(agentInjectionProject: null)
 {
-    /// <summary>
-    /// Blazor client-only WebAssembly app.
-    /// </summary>
-    internal sealed class BlazorWebAssemblyAppModel(ProjectGraphNode clientProject) : HotReloadAppModel
-    {
-        public override bool RequiresBrowserRefresh => true;
-
-        /// <summary>
-        /// Blazor WASM does not need dotnet applier as all changes are applied in the browser,
-        /// the process being launched is a dev server.
-        /// </summary>
-        public override bool InjectDeltaApplier => false;
+    public override bool RequiresBrowserRefresh => true;
 
-        public override DeltaApplier? CreateDeltaApplier(BrowserRefreshServer? browserRefreshServer, IReporter processReporter)
+    public override DeltaApplier? CreateDeltaApplier(BrowserRefreshServer? browserRefreshServer, IReporter processReporter)
+    {
+        if (browserRefreshServer == null)
         {
-            if (browserRefreshServer == null)
-            {
-                // error has been reported earlier
-                return null;
-            }
-
-            return new BlazorWebAssemblyDeltaApplier(processReporter, browserRefreshServer, clientProject);
+            // error has been reported earlier
+            return null;
         }
+
+        return new BlazorWebAssemblyDeltaApplier(processReporter, browserRefreshServer, clientProject);
     }
 }
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyHostedAppModel.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyHostedAppModel.cs
index 94f8a467b408..c361deb0d88c 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyHostedAppModel.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyHostedAppModel.cs
@@ -5,26 +5,24 @@
 
 namespace Microsoft.DotNet.Watch;
 
-internal abstract partial class HotReloadAppModel
+/// <summary>
+/// Blazor WebAssembly app hosted by an ASP.NET Core app.
+/// App has a client and server projects and deltas are applied to both processes.
+/// Agent is injected into the server process. The client process is updated via WebSocketScriptInjection.js injected into the browser.
+/// </summary>
+internal sealed class BlazorWebAssemblyHostedAppModel(ProjectGraphNode clientProject, ProjectGraphNode serverProject)
+    : HotReloadAppModel(agentInjectionProject: serverProject)
 {
-    /// <summary>
-    /// Blazor WebAssembly app hosted by an ASP.NET Core app.
-    /// App has a client and server projects and deltas are applied to both processes.
-    /// </summary>
-    internal sealed class BlazorWebAssemblyHostedAppModel(ProjectGraphNode clientProject) : HotReloadAppModel
-    {
-        public override bool RequiresBrowserRefresh => true;
-        public override bool InjectDeltaApplier => true;
+    public override bool RequiresBrowserRefresh => true;
 
-        public override DeltaApplier? CreateDeltaApplier(BrowserRefreshServer? browserRefreshServer, IReporter processReporter)
+    public override DeltaApplier? CreateDeltaApplier(BrowserRefreshServer? browserRefreshServer, IReporter processReporter)
+    {
+        if (browserRefreshServer == null)
         {
-            if (browserRefreshServer == null)
-            {
-                // error has been reported earlier
-                return null;
-            }
-
-            return new BlazorWebAssemblyHostedDeltaApplier(processReporter, browserRefreshServer, clientProject);
+            // error has been reported earlier
+            return null;
         }
+
+        return new BlazorWebAssemblyHostedDeltaApplier(processReporter, browserRefreshServer, clientProject);
     }
 }
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/AppModels/DefaultAppModel.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/DefaultAppModel.cs
index b104de3c263a..8cd7d5eb92c9 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/AppModels/DefaultAppModel.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/DefaultAppModel.cs
@@ -1,17 +1,17 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using Microsoft.Build.Graph;
+
 namespace Microsoft.DotNet.Watch;
 
 /// <summary>
 /// Default model.
 /// </summary>
-internal sealed class DefaultAppModel : HotReloadAppModel
+internal sealed class DefaultAppModel(ProjectGraphNode project)
+    : HotReloadAppModel(agentInjectionProject: project)
 {
-    public static readonly DefaultAppModel Instance = new();
-
     public override bool RequiresBrowserRefresh => false;
-    public override bool InjectDeltaApplier => true;
 
     public override DeltaApplier? CreateDeltaApplier(BrowserRefreshServer? browserRefreshServer, IReporter processReporter)
         => new DefaultDeltaApplier(processReporter);
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/AppModels/DeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/DeltaApplier.cs
index 852c9295e949..4fb5e4b9d6fe 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/AppModels/DeltaApplier.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/DeltaApplier.cs
@@ -12,8 +12,6 @@ internal abstract class DeltaApplier(IReporter reporter) : IDisposable
     {
         public readonly IReporter Reporter = reporter;
 
-        public static readonly string StartupHookPath = Path.Combine(AppContext.BaseDirectory, "hotreload", "Microsoft.Extensions.DotNetDeltaApplier.dll");
-
         public abstract void CreateConnection(string namedPipeName, CancellationToken cancellationToken);
 
         /// <summary>
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/AppModels/HotReloadAppModel.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/HotReloadAppModel.cs
index 595c0a52b4a2..2d1788425e70 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/AppModels/HotReloadAppModel.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/HotReloadAppModel.cs
@@ -1,21 +1,38 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-using Microsoft.Build.Execution;
+using System.Diagnostics.CodeAnalysis;
 using Microsoft.Build.Graph;
 
 namespace Microsoft.DotNet.Watch;
 
-internal abstract partial class HotReloadAppModel
+internal abstract partial class HotReloadAppModel(ProjectGraphNode? agentInjectionProject)
 {
     public abstract bool RequiresBrowserRefresh { get; }
 
+    public abstract DeltaApplier? CreateDeltaApplier(BrowserRefreshServer? browserRefreshServer, IReporter processReporter);
+
     /// <summary>
-    /// True to inject delta applier to the process.
+    /// Returns true and the path to the client agent implementation binary if the application needs the agent to be injected.
     /// </summary>
-    public abstract bool InjectDeltaApplier { get; }
+    public bool TryGetStartupHookPath([NotNullWhen(true)] out string? path)
+    {
+        if (agentInjectionProject == null)
+        {
+            path = null;
+            return false;
+        }
 
-    public abstract DeltaApplier? CreateDeltaApplier(BrowserRefreshServer? browserRefreshServer, IReporter processReporter);
+        var hookTargetFramework = agentInjectionProject.GetTargetFramework() switch
+        {
+            // Note: Hot Reload is only supported on net6.0+
+            "net6.0" or "net7.0" or "net8.0" or "net9.0" => "netstandard2.1",
+            _ => "net10.0",
+        };
+
+        path = Path.Combine(AppContext.BaseDirectory, "hotreload", hookTargetFramework, "Microsoft.Extensions.DotNetDeltaApplier.dll");
+        return true;
+    }
 
     public static HotReloadAppModel InferFromProject(ProjectGraphNode projectNode, IReporter reporter)
     {
@@ -24,7 +41,7 @@ public static HotReloadAppModel InferFromProject(ProjectGraphNode projectNode, I
             var queue = new Queue<ProjectGraphNode>();
             queue.Enqueue(projectNode);
 
-            ProjectInstance? aspnetCoreProject = null;
+            ProjectGraphNode? aspnetCoreProject = null;
 
             var visited = new HashSet<ProjectGraphNode>();
 
@@ -37,17 +54,17 @@ public static HotReloadAppModel InferFromProject(ProjectGraphNode projectNode, I
                 {
                     if (item.EvaluatedInclude == "AspNetCore")
                     {
-                        aspnetCoreProject = currentNode.ProjectInstance;
+                        aspnetCoreProject = currentNode;
                         break;
                     }
 
                     if (item.EvaluatedInclude == "WebAssembly")
                     {
                         // We saw a previous project that was AspNetCore. This must be a blazor hosted app.
-                        if (aspnetCoreProject is not null && aspnetCoreProject != currentNode.ProjectInstance)
+                        if (aspnetCoreProject is not null && aspnetCoreProject.ProjectInstance != currentNode.ProjectInstance)
                         {
-                            reporter.Verbose($"HotReloadProfile: BlazorHosted. {aspnetCoreProject.FullPath} references BlazorWebAssembly project {currentNode.ProjectInstance.FullPath}.", emoji: "🔥");
-                            return new BlazorWebAssemblyHostedAppModel(clientProject: currentNode);
+                            reporter.Verbose($"HotReloadProfile: BlazorHosted. {aspnetCoreProject.ProjectInstance.FullPath} references BlazorWebAssembly project {currentNode.ProjectInstance.FullPath}.", emoji: "🔥");
+                            return new BlazorWebAssemblyHostedAppModel(clientProject: currentNode, serverProject: aspnetCoreProject);
                         }
 
                         reporter.Verbose("HotReloadProfile: BlazorWebAssembly.", emoji: "🔥");
@@ -66,6 +83,6 @@ public static HotReloadAppModel InferFromProject(ProjectGraphNode projectNode, I
         }
 
         reporter.Verbose("HotReloadProfile: Default.", emoji: "🔥");
-        return DefaultAppModel.Instance;
+        return new DefaultAppModel(projectNode);
     }
 }
diff --git a/src/BuiltInTools/dotnet-watch/Process/ProcessRunner.cs b/src/BuiltInTools/dotnet-watch/Process/ProcessRunner.cs
index bd0ff20a08b3..a69bd465a7ef 100644
--- a/src/BuiltInTools/dotnet-watch/Process/ProcessRunner.cs
+++ b/src/BuiltInTools/dotnet-watch/Process/ProcessRunner.cs
@@ -82,7 +82,7 @@ public async Task<int> RunAsync(ProcessSpec processSpec, IReporter reporter, boo
             {
                 try
                 {
-                    _ = await WaitForExitAsync(process, timeout: null, processTerminationToken);
+                    await process.WaitForExitAsync(processTerminationToken);
                 }
                 catch (OperationCanceledException)
                 {
@@ -219,7 +219,7 @@ private async ValueTask TerminateProcessAsync(Process process, ProcessState stat
                     // Ctrl+C hasn't been sent, force termination.
                     // We don't have means to terminate gracefully on Windows (https://github.com/dotnet/runtime/issues/109432)
                     TerminateProcess(process, state, reporter, force: true);
-                    _ = await WaitForExitAsync(process, timeout: null, cancellationToken);
+                    _ = await WaitForExitAsync(process, state, timeout: null, reporter, cancellationToken);
 
                     return;
                 }
@@ -231,42 +231,70 @@ private async ValueTask TerminateProcessAsync(Process process, ProcessState stat
             }
 
             // Ctlr+C/SIGTERM has been sent, wait for the process to exit gracefully.
-            if (!await WaitForExitAsync(process, processCleanupTimeout, cancellationToken))
+            if (processCleanupTimeout.Milliseconds == 0 ||
+                !await WaitForExitAsync(process, state, processCleanupTimeout, reporter, cancellationToken))
             {
                 // Force termination if the process is still running after the timeout.
                 TerminateProcess(process, state, reporter, force: true);
 
-                _ = await WaitForExitAsync(process, timeout: null, cancellationToken);
+                _ = await WaitForExitAsync(process, state, timeout: null, reporter, cancellationToken);
             }
         }
 
-        private static async ValueTask<bool> WaitForExitAsync(Process process, TimeSpan? timeout, CancellationToken cancellationToken)
+        private static async ValueTask<bool> WaitForExitAsync(Process process, ProcessState state, TimeSpan? timeout, IReporter reporter, CancellationToken cancellationToken)
         {
+            // On Linux simple call WaitForExitAsync does not work reliably (it may hang).
+            // As a workaround we poll for HasExited.
+            // See also https://github.com/dotnet/runtime/issues/109434.
+
             var task = process.WaitForExitAsync(cancellationToken);
 
-            if (timeout.HasValue)
+            if (timeout is { } timeoutValue)
             {
                 try
                 {
-                    await task.WaitAsync(timeout.Value, cancellationToken);
+                    reporter.Verbose($"Waiting for process {state.ProcessId} to exit within {timeoutValue.TotalSeconds}s.");
+                    await task.WaitAsync(timeoutValue, cancellationToken);
                 }
                 catch (TimeoutException)
                 {
-                    return false;
+                    try
+                    {
+                        return process.HasExited;
+                    }
+                    catch
+                    {
+                        return false;
+                    }
                 }
             }
             else
             {
-                await task;
-            }
+                int i = 1;
+                while (true)
+                {
+                    try
+                    {
+                        if (process.HasExited)
+                        {
+                            return true;
+                        }
+                    }
+                    catch
+                    {
+                    }
 
-            // ensures that all process output has been reported:
-            try
-            {
-                process.WaitForExit();
-            }
-            catch
-            {
+                    reporter.Verbose($"Waiting for process {state.ProcessId} to exit ({i++}).");
+
+                    try
+                    {
+                        await task.WaitAsync(TimeSpan.FromSeconds(1), cancellationToken);
+                        break;
+                    }
+                    catch (TimeoutException)
+                    {
+                    }
+                }
             }
 
             return true;
diff --git a/src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs b/src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs
index 288bf04b4d5b..f91ae9cc3447 100644
--- a/src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs
+++ b/src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs
@@ -77,10 +77,10 @@ public EnvironmentOptions EnvironmentOptions
         // https://github.com/dotnet/runtime/blob/342936c5a88653f0f622e9d6cb727a0e59279b31/src/mono/browser/runtime/loader/config.ts#L330
         environmentBuilder.SetVariable(EnvironmentVariables.Names.DotNetModifiableAssemblies, "debug");
 
-        if (appModel.InjectDeltaApplier)
+        if (appModel.TryGetStartupHookPath(out var startupHookPath))
         {
             // HotReload startup hook should be loaded before any other startup hooks:
-            environmentBuilder.DotNetStartupHooks.Insert(0, DeltaApplier.StartupHookPath);
+            environmentBuilder.DotNetStartupHooks.Insert(0, startupHookPath);
 
             environmentBuilder.SetVariable(EnvironmentVariables.Names.DotNetWatchHotReloadNamedPipeName, namedPipeName);
 
diff --git a/src/BuiltInTools/dotnet-watch/Watch/DotNetWatcher.cs b/src/BuiltInTools/dotnet-watch/Watch/DotNetWatcher.cs
index b7901c81db05..11069f4450c6 100644
--- a/src/BuiltInTools/dotnet-watch/Watch/DotNetWatcher.cs
+++ b/src/BuiltInTools/dotnet-watch/Watch/DotNetWatcher.cs
@@ -62,7 +62,7 @@ public static async Task WatchAsync(DotNetWatchContext context, CancellationToke
                 };
 
                 var browserRefreshServer = (projectRootNode != null)
-                    ? await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectRootNode, processSpec, environmentBuilder, context.RootProjectOptions, DefaultAppModel.Instance, shutdownCancellationToken)
+                    ? await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectRootNode, processSpec, environmentBuilder, context.RootProjectOptions, new DefaultAppModel(projectRootNode), shutdownCancellationToken)
                     : null;
 
                 environmentBuilder.SetProcessEnvironmentVariables(processSpec);
diff --git a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
index 479a1f8ae35e..5a50b3ee0878 100644
--- a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
+++ b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <Import Project="..\AspireService\Microsoft.WebTools.AspireService.projitems" Label="Shared" />
   <Import Project="..\HotReloadAgent.PipeRpc\Microsoft.DotNet.HotReload.Agent.PipeRpc.projitems" Label="Shared" />
@@ -50,8 +50,27 @@
   <ItemGroup>
     <ProjectReference Include="$(RepoRoot)\src\Cli\dotnet\dotnet.csproj" />
     <ProjectReference Include="..\BrowserRefresh\Microsoft.AspNetCore.Watch.BrowserRefresh.csproj" PrivateAssets="All" ReferenceOutputAssembly="false" SkipGetTargetFrameworkProperties="true" UndefineProperties="TargetFramework;TargetFrameworks" OutputItemType="Content" TargetPath="middleware\Microsoft.AspNetCore.Watch.BrowserRefresh.dll" CopyToOutputDirectory="PreserveNewest" />
-    <ProjectReference Include="..\DotNetDeltaApplier\Microsoft.Extensions.DotNetDeltaApplier.csproj" PrivateAssets="All" ReferenceOutputAssembly="false" SkipGetTargetFrameworkProperties="true" UndefineProperties="TargetFramework;TargetFrameworks" OutputItemType="Content" TargetPath="hotreload\Microsoft.Extensions.DotNetDeltaApplier.dll" CopyToOutputDirectory="PreserveNewest" />
     <ProjectReference Include="..\DotNetWatchTasks\DotNetWatchTasks.csproj" PrivateAssets="All" ReferenceOutputAssembly="false" SkipGetTargetFrameworkProperties="true" UndefineProperties="TargetFramework;TargetFrameworks" OutputItemType="Content" CopyToOutputDirectory="PreserveNewest" />
+
+    <ProjectReference Include="..\DotNetDeltaApplier\Microsoft.Extensions.DotNetDeltaApplier.csproj">
+      <PrivateAssets>all</PrivateAssets>
+      <OutputItemType>Content</OutputItemType>
+      <SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+      <SetTargetFramework>TargetFramework=net10.0</SetTargetFramework>
+      <TargetPath>hotreload\net10.0\Microsoft.Extensions.DotNetDeltaApplier.dll</TargetPath>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
+    </ProjectReference>
+
+    <ProjectReference Include="..\DotNetDeltaApplier\Microsoft.Extensions.DotNetDeltaApplier.csproj">
+      <PrivateAssets>all</PrivateAssets>
+      <OutputItemType>Content</OutputItemType>
+      <SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+      <SetTargetFramework>TargetFramework=netstandard2.1</SetTargetFramework>
+      <TargetPath>hotreload\netstandard2.1\Microsoft.Extensions.DotNetDeltaApplier.dll</TargetPath>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </ProjectReference>
   </ItemGroup>
 
   <!-- Publish dotnet-watch files to the redist testhost folder so that in innerloop, redist doesn't need to be built again. -->
diff --git a/test/dotnet-watch.Tests/Aspire/AspireServiceFactoryTests.cs b/test/dotnet-watch.Tests/Aspire/AspireServiceFactoryTests.cs
index 18387ff5af9b..5e5c721e9f0d 100644
--- a/test/dotnet-watch.Tests/Aspire/AspireServiceFactoryTests.cs
+++ b/test/dotnet-watch.Tests/Aspire/AspireServiceFactoryTests.cs
@@ -7,7 +7,7 @@ namespace Microsoft.DotNet.Watch.UnitTests;
 
 public class AspireServiceFactoryTests
 {
-    [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+    [Fact]
     public void GetRunCommandArguments_Empty()
     {
         var request = new ProjectLaunchRequest()
@@ -24,7 +24,7 @@ public void GetRunCommandArguments_Empty()
         AssertEx.SequenceEqual(["--project", "a.csproj"], args);
     }
 
-    [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+    [Fact]
     public void GetRunCommandArguments_DisableLaunchProfile()
     {
         var request = new ProjectLaunchRequest()
@@ -41,7 +41,7 @@ public void GetRunCommandArguments_DisableLaunchProfile()
         AssertEx.SequenceEqual(["--project", "a.csproj", "--no-launch-profile" ], args);
     }
 
-    [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+    [Theory]
     [InlineData("")]
     [InlineData(null)]
     public void GetRunCommandArguments_NoLaunchProfile_HostProfile(string? launchProfile)
@@ -60,7 +60,7 @@ public void GetRunCommandArguments_NoLaunchProfile_HostProfile(string? launchPro
         AssertEx.SequenceEqual(["--project", "a.csproj", "--launch-profile", "H"], args);
     }
 
-    [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+    [Theory]
     [InlineData("")]
     [InlineData(null)]
     public void GetRunCommandArguments_DisableLaunchProfile_HostProfile(string? launchProfile)
@@ -79,7 +79,7 @@ public void GetRunCommandArguments_DisableLaunchProfile_HostProfile(string? laun
         AssertEx.SequenceEqual(["--project", "a.csproj", "--no-launch-profile"], args);
     }
 
-    [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+    [Theory]
     [InlineData("")]
     [InlineData(null)]
     public void GetRunCommandArguments_NoLaunchProfile_NoHostProfile(string? launchProfile)
@@ -97,7 +97,7 @@ public void GetRunCommandArguments_NoLaunchProfile_NoHostProfile(string? launchP
 
         AssertEx.SequenceEqual(["--project", "a.csproj"], args);
     }
-    [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+    [Fact]
     public void GetRunCommandArguments_LaunchProfile_NoArgs()
     {
         var request = new ProjectLaunchRequest()
@@ -114,7 +114,7 @@ public void GetRunCommandArguments_LaunchProfile_NoArgs()
         AssertEx.SequenceEqual(["--project", "a.csproj", "--launch-profile", "P"], args);
     }
 
-    [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+    [Fact]
     public void GetRunCommandArguments_LaunchProfile_EmptyArgs()
     {
         var request = new ProjectLaunchRequest()
@@ -131,7 +131,7 @@ public void GetRunCommandArguments_LaunchProfile_EmptyArgs()
         AssertEx.SequenceEqual(["--project", "a.csproj", "--launch-profile", "P", "--no-launch-profile-arguments"], args);
     }
 
-    [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+    [Fact]
     public void GetRunCommandArguments_LaunchProfile_NonEmptyArgs()
     {
         var request = new ProjectLaunchRequest()
diff --git a/test/dotnet-watch.Tests/Browser/BrowserConnectorTests.cs b/test/dotnet-watch.Tests/Browser/BrowserConnectorTests.cs
index e6a395ae434c..6f1a08391dfc 100644
--- a/test/dotnet-watch.Tests/Browser/BrowserConnectorTests.cs
+++ b/test/dotnet-watch.Tests/Browser/BrowserConnectorTests.cs
@@ -5,7 +5,7 @@ namespace Microsoft.DotNet.Watch.UnitTests;
 
 public class BrowserConnectorTests
 {
-    [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+    [Theory]
     [InlineData(null, "https://localhost:1234", "https://localhost:1234")]
     [InlineData(null, "https://localhost:1234/", "https://localhost:1234/")]
     [InlineData("", "https://localhost:1234", "https://localhost:1234")]
diff --git a/test/dotnet-watch.Tests/Browser/BrowserLaunchTests.cs b/test/dotnet-watch.Tests/Browser/BrowserLaunchTests.cs
index 489e45520b2c..569c2b8d07ac 100644
--- a/test/dotnet-watch.Tests/Browser/BrowserLaunchTests.cs
+++ b/test/dotnet-watch.Tests/Browser/BrowserLaunchTests.cs
@@ -12,7 +12,7 @@ public BrowserLaunchTests(ITestOutputHelper logger)
         {
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task LaunchesBrowserOnStart()
         {
             var testAsset = TestAssets.CopyTestAsset(AppName)
@@ -30,7 +30,7 @@ public async Task LaunchesBrowserOnStart()
             Assert.Contains(App.Process.Output, line => line.Contains("dotnet watch ⌚ Launching browser: https://localhost:5001"));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task UsesBrowserSpecifiedInEnvironment()
         {
             var testAsset = TestAssets.CopyTestAsset(AppName)
diff --git a/test/dotnet-watch.Tests/Build/FileSetSerializerTests.cs b/test/dotnet-watch.Tests/Build/FileSetSerializerTests.cs
index 5b93ab499e36..77bd1b8abe68 100644
--- a/test/dotnet-watch.Tests/Build/FileSetSerializerTests.cs
+++ b/test/dotnet-watch.Tests/Build/FileSetSerializerTests.cs
@@ -36,7 +36,7 @@ private static string Serialize(MSBuildFileSetResult fileSetResult, Stream strea
         return reader.ReadToEnd();
     }
 
-    [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+    [Fact]
     public async Task Roundtrip()
     {
         var result1 = new MSBuildFileSetResult()
@@ -101,7 +101,7 @@ public async Task Roundtrip()
             """.Replace("\r\n", "\n"), serialized1.Replace("\r\n", "\n"));
     }
 
-    [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+    [Fact]
     public async Task Task()
     {
         var dir = _testAssetManager.CreateTestDirectory().Path;
diff --git a/test/dotnet-watch.Tests/Build/MsBuildFileSetFactoryTest.cs b/test/dotnet-watch.Tests/Build/MsBuildFileSetFactoryTest.cs
index edbf77069f63..0088f35d5db2 100644
--- a/test/dotnet-watch.Tests/Build/MsBuildFileSetFactoryTest.cs
+++ b/test/dotnet-watch.Tests/Build/MsBuildFileSetFactoryTest.cs
@@ -19,7 +19,7 @@ private static IEnumerable<string> Inspect(string rootDir, IReadOnlyDictionary<s
             .OrderBy(entry => entry.Key)
             .Select(entry => $"{InspectPath(entry.Key, rootDir)}: [{string.Join(", ", entry.Value.ContainingProjectPaths.Select(p => InspectPath(p, rootDir)))}]");
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task FindsCustomWatchItems()
         {
             var project = _testAssets.CreateTestProject(new TestProject("Project1")
@@ -51,7 +51,7 @@ public async Task FindsCustomWatchItems()
             );
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task ExcludesDefaultItemsWithWatchFalseMetadata()
         {
             var project = _testAssets.CreateTestProject(new TestProject("Project1")
@@ -85,7 +85,7 @@ public async Task ExcludesDefaultItemsWithWatchFalseMetadata()
             );
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task SingleTfm()
         {
             var project = _testAssets.CreateTestProject(new TestProject("Project1")
@@ -118,7 +118,7 @@ public async Task SingleTfm()
             );
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task MultiTfm()
         {
             var project = _testAssets.CreateTestProject(new TestProject("Project1")
@@ -154,7 +154,7 @@ public async Task MultiTfm()
             );
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task IncludesContentFiles()
         {
             var testDir = _testAssets.CreateTestDirectory();
@@ -187,7 +187,7 @@ public async Task IncludesContentFiles()
             );
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task IncludesContentFilesFromRCL()
         {
             var testDir = _testAssets.CreateTestDirectory();
@@ -239,7 +239,7 @@ public async Task IncludesContentFilesFromRCL()
             );
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task ProjectReferences_OneLevel()
         {
             var project2 = _testAssets.CreateTestProject(new TestProject("Project2")
@@ -268,7 +268,7 @@ public async Task ProjectReferences_OneLevel()
             );
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task TransitiveProjectReferences_TwoLevels()
         {
             var project3 = _testAssets.CreateTestProject(new TestProject("Project3")
@@ -307,7 +307,7 @@ public async Task TransitiveProjectReferences_TwoLevels()
             Assert.All(result.Files.Values, f => Assert.False(f.IsStaticFile, $"File {f.FilePath} should not be a static file."));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task ProjectReferences_Graph()
         {
             // A->B,F,W(Watch=False)
@@ -364,7 +364,7 @@ public async Task ProjectReferences_Graph()
                 _reporter.Messages.Where(l => l.text.Contains("Collecting watch items from")).Select(l => l.text.Trim()).Order());
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task MsbuildOutput()
         {
             var project2 = _testAssets.CreateTestProject(new TestProject("Project2")
diff --git a/test/dotnet-watch.Tests/CommandLine/CommandLineOptionsTests.cs b/test/dotnet-watch.Tests/CommandLine/CommandLineOptionsTests.cs
index 00c3223a8792..30d70643855a 100644
--- a/test/dotnet-watch.Tests/CommandLine/CommandLineOptionsTests.cs
+++ b/test/dotnet-watch.Tests/CommandLine/CommandLineOptionsTests.cs
@@ -39,7 +39,7 @@ private void VerifyErrors(string[] args, params string[] expectedErrors)
             Assert.NotEqual(0, errorCode);
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData([new[] { "-h" }])]
         [InlineData([new[] { "-?" }])]
         [InlineData([new[] { "--help" }])]
@@ -55,7 +55,7 @@ public void HelpArgs(string[] args)
             Assert.Contains("Usage:", output.ToString());
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData("-p:P=V", "P", "V")]
         [InlineData("-p:P==", "P", "=")]
         [InlineData("-p:P=A=B", "P", "A=B")]
@@ -67,7 +67,7 @@ public void BuildProperties_Valid(string argValue, string name, string value)
             AssertEx.SequenceEqual([(name, value)], properties);
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData("P")]
         [InlineData("=P3")]
         [InlineData("=")]
@@ -78,7 +78,7 @@ public void BuildProperties_Invalid(string argValue)
             AssertEx.SequenceEqual([], properties);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void ImplicitCommand()
         {
             var options = VerifyOptions([]);
@@ -86,7 +86,7 @@ public void ImplicitCommand()
             AssertEx.SequenceEqual([InteractiveOption], options.CommandArguments);
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData("add")]
         [InlineData("build")]
         [InlineData("build-server")]
@@ -119,7 +119,7 @@ public void ExplicitCommand(string command)
             Assert.Empty(args);
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [CombinatorialData]
         public void WatchOptions_NotPassedThrough_BeforeCommand(
             [CombinatorialValues("--quiet", "--verbose", "--no-hot-reload", "--non-interactive")] string option,
@@ -130,7 +130,7 @@ public void WatchOptions_NotPassedThrough_BeforeCommand(
             AssertEx.SequenceEqual(option == "--non-interactive" ? [] : [InteractiveOption], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void RunOptions_LaunchProfile_Watch()
         {
             var options = VerifyOptions(["-lp", "P", "run"]);
@@ -139,7 +139,7 @@ public void RunOptions_LaunchProfile_Watch()
             AssertEx.SequenceEqual(["-lp", "P", InteractiveOption], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void RunOptions_LaunchProfile_Run()
         {
             var options = VerifyOptions(["run", "-lp", "P"]);
@@ -148,14 +148,14 @@ public void RunOptions_LaunchProfile_Run()
             AssertEx.SequenceEqual(["-lp", "P", InteractiveOption], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void RunOptions_LaunchProfile_Both()
         {
             VerifyErrors(["-lp", "P1", "run", "-lp", "P2"],
                 "error ❌ Option '-lp' expects a single argument but 2 were provided.");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void RunOptions_NoProfile_Watch()
         {
             var options = VerifyOptions(["--no-launch-profile", "run"]);
@@ -165,7 +165,7 @@ public void RunOptions_NoProfile_Watch()
             AssertEx.SequenceEqual(["--no-launch-profile", InteractiveOption], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void RunOptions_NoProfile_Run()
         {
             var options = VerifyOptions(["run", "--no-launch-profile"]);
@@ -175,7 +175,7 @@ public void RunOptions_NoProfile_Run()
             AssertEx.SequenceEqual(["--no-launch-profile", InteractiveOption], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void RunOptions_NoProfile_Both()
         {
             var options = VerifyOptions(["--no-launch-profile", "run", "--no-launch-profile"]);
@@ -185,7 +185,7 @@ public void RunOptions_NoProfile_Both()
             AssertEx.SequenceEqual(["--no-launch-profile", InteractiveOption], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void RemainingOptions()
         {
             var options = VerifyOptions(["-watchArg", "--verbose", "run", "-runArg"]);
@@ -195,7 +195,7 @@ public void RemainingOptions()
             AssertEx.SequenceEqual([InteractiveOption, "-watchArg", "-runArg"], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void UnknownOption()
         {
             var options = VerifyOptions(["--verbose", "--unknown", "x", "y", "run", "--project", "p"]);
@@ -205,7 +205,7 @@ public void UnknownOption()
             AssertEx.SequenceEqual(["--project", "p", InteractiveOption, "--unknown", "x", "y"], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void RemainingOptionsDashDash()
         {
             var options = VerifyOptions(["-watchArg", "--", "--verbose", "run", "-runArg"]);
@@ -215,7 +215,7 @@ public void RemainingOptionsDashDash()
             AssertEx.SequenceEqual([InteractiveOption, "-watchArg", "--", "--verbose", "run", "-runArg",], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void RemainingOptionsDashDashRun()
         {
             var options = VerifyOptions(["--", "run"]);
@@ -225,7 +225,7 @@ public void RemainingOptionsDashDashRun()
             AssertEx.SequenceEqual([InteractiveOption, "--", "run"], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void NoOptionsAfterDashDash()
         {
             var options = VerifyOptions(["--"]);
@@ -240,7 +240,7 @@ public void NoOptionsAfterDashDash()
         /// Therfore, it has to also be ignored by `dotnet run`,
         /// otherwise the TFMs would be inconsistent between `dotnet watch` and `dotnet run`.
         /// </summary>
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void ParsedNonWatchOptionsAfterDashDash_Framework()
         {
             var options = VerifyOptions(["--", "-f", "TFM"]);
@@ -249,7 +249,7 @@ public void ParsedNonWatchOptionsAfterDashDash_Framework()
             AssertEx.SequenceEqual([InteractiveOption, "--", "-f", "TFM"], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void ParsedNonWatchOptionsAfterDashDash_Project()
         {
             var options = VerifyOptions(["--", "--project", "proj"]);
@@ -258,7 +258,7 @@ public void ParsedNonWatchOptionsAfterDashDash_Project()
             AssertEx.SequenceEqual([InteractiveOption, "--", "--project", "proj"], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void ParsedNonWatchOptionsAfterDashDash_NoLaunchProfile()
         {
             var options = VerifyOptions(["--", "--no-launch-profile"]);
@@ -267,7 +267,7 @@ public void ParsedNonWatchOptionsAfterDashDash_NoLaunchProfile()
             AssertEx.SequenceEqual([InteractiveOption, "--", "--no-launch-profile"], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void ParsedNonWatchOptionsAfterDashDash_LaunchProfile()
         {
             var options = VerifyOptions(["--", "--launch-profile", "p"]);
@@ -276,7 +276,7 @@ public void ParsedNonWatchOptionsAfterDashDash_LaunchProfile()
             AssertEx.SequenceEqual([InteractiveOption, "--", "--launch-profile", "p"], options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void ParsedNonWatchOptionsAfterDashDash_Property()
         {
             var options = VerifyOptions(["--", "--property", "x=1"]);
@@ -285,7 +285,7 @@ public void ParsedNonWatchOptionsAfterDashDash_Property()
             AssertEx.SequenceEqual([InteractiveOption, "--", "--property", "x=1"], options.CommandArguments);
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [CombinatorialData]
         public void OptionsSpecifiedBeforeOrAfterRun(bool afterRun)
         {
@@ -307,7 +307,7 @@ public enum ArgPosition
             Both
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [CombinatorialData]
         public void OptionDuplicates_Allowed_Bool(
             ArgPosition position,
@@ -342,7 +342,7 @@ public void OptionDuplicates_Allowed_Bool(
             });
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void MultiplePropertyValues()
         {
             var options = VerifyOptions(["--property", "P1=V1", "run", "--property", "P2=V2"]);
@@ -352,7 +352,7 @@ public void MultiplePropertyValues()
             AssertEx.SequenceEqual(["--property:P1=V1", "--property:P2=V2", InteractiveOption], options.CommandArguments);
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData("--project")]
         [InlineData("--framework")]
         public void OptionDuplicates_NotAllowed(string option)
@@ -361,7 +361,7 @@ public void OptionDuplicates_NotAllowed(string option)
                 $"error ❌ Option '{option}' expects a single argument but 2 were provided.");
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData(new[] { "--unrecognized-arg" }, new[] { InteractiveOption, "--unrecognized-arg" })]
         [InlineData(new[] { "run" }, new string[] { InteractiveOption })]
         [InlineData(new[] { "run", "--", "runarg" }, new[] { InteractiveOption, "--", "runarg" })]
@@ -376,14 +376,14 @@ public void ParsesRemainingArgs(string[] args, string[] expected)
             Assert.Equal(expected, options.CommandArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void CannotHaveQuietAndVerbose()
         {
             VerifyErrors(["--quiet", "--verbose"],
                 $"error ❌ {Resources.Error_QuietAndVerboseSpecified}");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void ShortFormForProjectArgumentPrintsWarning()
         {
             var options = VerifyOptions(["-p", "MyProject.csproj"],
@@ -392,14 +392,14 @@ public void ShortFormForProjectArgumentPrintsWarning()
             Assert.Equal("MyProject.csproj", options.ProjectPath);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void LongFormForProjectArgumentWorks()
         {
             var options = VerifyOptions(["--project", "MyProject.csproj"]);
             Assert.Equal("MyProject.csproj", options.ProjectPath);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void LongFormForLaunchProfileArgumentWorks()
         {
             var options = VerifyOptions(["--launch-profile", "CustomLaunchProfile"]);
@@ -407,7 +407,7 @@ public void LongFormForLaunchProfileArgumentWorks()
             Assert.Equal("CustomLaunchProfile", options.LaunchProfileName);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void ShortFormForLaunchProfileArgumentWorks()
         {
             var options = VerifyOptions(["-lp", "CustomLaunchProfile"]);
@@ -419,7 +419,7 @@ public void ShortFormForLaunchProfileArgumentWorks()
         /// <summary>
         /// Validates that options that the "run" command forwards to "build" command are forwarded by dotnet-watch.
         /// </summary>
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData(new[] { "--configuration", "release" }, new[] { "-property:Configuration=release", NugetInteractiveProperty })]
         [InlineData(new[] { "--framework", "net9.0" }, new[] { "-property:TargetFramework=net9.0", NugetInteractiveProperty })]
         [InlineData(new[] { "--runtime", "arm64" }, new[] { "-property:RuntimeIdentifier=arm64", "-property:_CommandLineDefinedRuntimeIdentifier=true", NugetInteractiveProperty })]
@@ -438,7 +438,7 @@ public void ForwardedBuildOptions(string[] args, string[] buildArgs)
             AssertEx.SequenceEqual(buildArgs, options.BuildArguments);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void ForwardedBuildOptions_ArtifactsPath()
         {
             var path = TestContext.Current.TestAssetsDirectory;
diff --git a/test/dotnet-watch.Tests/CommandLine/EnvironmentVariablesBuilderTests.cs b/test/dotnet-watch.Tests/CommandLine/EnvironmentVariablesBuilderTests.cs
index d9f8876e9133..509cadb17854 100644
--- a/test/dotnet-watch.Tests/CommandLine/EnvironmentVariablesBuilderTests.cs
+++ b/test/dotnet-watch.Tests/CommandLine/EnvironmentVariablesBuilderTests.cs
@@ -5,7 +5,7 @@ namespace Microsoft.DotNet.Watch.UnitTests
 {
     public class EnvironmentVariablesBuilderTests
     {
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void Value()
         {
             var builder = new EnvironmentVariablesBuilder();
@@ -20,7 +20,7 @@ public void Value()
             ], env);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void MultipleValues()
         {
             var builder = new EnvironmentVariablesBuilder();
diff --git a/test/dotnet-watch.Tests/CommandLine/LaunchSettingsTests.cs b/test/dotnet-watch.Tests/CommandLine/LaunchSettingsTests.cs
index 85366609b5a1..22d227944919 100644
--- a/test/dotnet-watch.Tests/CommandLine/LaunchSettingsTests.cs
+++ b/test/dotnet-watch.Tests/CommandLine/LaunchSettingsTests.cs
@@ -16,7 +16,7 @@ public DotNetWatcherTests(ITestOutputHelper logger)
         {
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task RunsWithDotnetWatchEnvVariable()
         {
             Assert.True(string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DOTNET_WATCH")), "DOTNET_WATCH cannot be set already when this test is running");
@@ -28,7 +28,7 @@ public async Task RunsWithDotnetWatchEnvVariable()
             Assert.Equal("1", await App.AssertOutputLineStartsWith("DOTNET_WATCH = "));
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [CombinatorialData]
         public async Task RunsWithDotnetLaunchProfileEnvVariableWhenNotExplicitlySpecified(bool hotReload)
         {
@@ -46,7 +46,7 @@ public async Task RunsWithDotnetLaunchProfileEnvVariableWhenNotExplicitlySpecifi
             Assert.Equal("<<<First>>>", await App.AssertOutputLineStartsWith("DOTNET_LAUNCH_PROFILE = "));
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [CombinatorialData]
         public async Task RunsWithDotnetLaunchProfileEnvVariableWhenExplicitlySpecified(bool hotReload)
         {
@@ -66,7 +66,7 @@ public async Task RunsWithDotnetLaunchProfileEnvVariableWhenExplicitlySpecified(
             Assert.Equal("<<<Second>>>", await App.AssertOutputLineStartsWith("DOTNET_LAUNCH_PROFILE = "));
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [CombinatorialData]
         public async Task RunsWithDotnetLaunchProfileEnvVariableWhenExplicitlySpecifiedButNotPresentIsEmpty(bool hotReload)
         {
@@ -84,7 +84,7 @@ public async Task RunsWithDotnetLaunchProfileEnvVariableWhenExplicitlySpecifiedB
             Assert.Equal("<<<First>>>", await App.AssertOutputLineStartsWith("DOTNET_LAUNCH_PROFILE = "));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task RunsWithIterationEnvVariable()
         {
             var testAsset = TestAssets.CopyTestAsset(AppName)
@@ -110,7 +110,7 @@ public async Task RunsWithIterationEnvVariable()
             Assert.Equal(2, int.Parse(value, CultureInfo.InvariantCulture));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task Run_WithHotReloadEnabled_ReadsLaunchSettings()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchAppWithLaunchSettings")
@@ -121,7 +121,7 @@ public async Task Run_WithHotReloadEnabled_ReadsLaunchSettings()
             await App.AssertOutputLineEquals("Environment: Development");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task Run_WithHotReloadEnabled_ReadsLaunchSettings_WhenUsingProjectOption()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchAppWithLaunchSettings")
diff --git a/test/dotnet-watch.Tests/CommandLine/ProgramTests.cs b/test/dotnet-watch.Tests/CommandLine/ProgramTests.cs
index 8d36b33c2093..5bf0907bbe5d 100644
--- a/test/dotnet-watch.Tests/CommandLine/ProgramTests.cs
+++ b/test/dotnet-watch.Tests/CommandLine/ProgramTests.cs
@@ -7,7 +7,7 @@ namespace Microsoft.DotNet.Watch.UnitTests
 {
     public class ProgramTests(ITestOutputHelper logger) : DotNetWatchTestBase(logger)
     {
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task ConsoleCancelKey()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchKitchenSink")
@@ -41,7 +41,7 @@ public async Task ConsoleCancelKey()
             await shutdownRequested.WaitAsync();
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData(new[] { "--no-hot-reload", "run" }, "")]
         [InlineData(new[] { "--no-hot-reload", "run", "args" }, "args")]
         [InlineData(new[] { "--no-hot-reload", "--", "run", "args" }, "run,args")]
@@ -63,7 +63,7 @@ public async Task Arguments(string[] arguments, string expectedApplicationArgs)
             Assert.Equal(expectedApplicationArgs, await App.AssertOutputLineStartsWith("Arguments = "));
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData(new[] { "--no-hot-reload", "--", "run", "args" }, "Argument Specified in Props,run,args")]
         [InlineData(new[] { "--", "run", "args" }, "Argument Specified in Props,run,args")]
         // if arguments specified on command line the ones from launch profile are ignored
@@ -80,7 +80,7 @@ public async Task Arguments_HostArguments(string[] arguments, string expectedApp
             AssertEx.Equal(expectedApplicationArgs, await App.AssertOutputLineStartsWith("Arguments = "));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task RunArguments_NoHotReload()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchHotReloadAppMultiTfm")
@@ -114,7 +114,7 @@ public async Task RunArguments_NoHotReload()
             Assert.DoesNotContain(App.Process.Output, l => l.Contains("Working directory:"));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task RunArguments_HotReload()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchHotReloadAppMultiTfm")
@@ -145,7 +145,7 @@ public async Task RunArguments_HotReload()
             Assert.Contains(App.Process.Output, l => l.Contains("Hot reload enabled."));
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData("P1", "argP1")]
         [InlineData("P and Q and \"R\"", "argPQR")]
         public async Task ArgumentsFromLaunchSettings_Watch(string profileName, string expectedArgs)
@@ -167,7 +167,7 @@ public async Task ArgumentsFromLaunchSettings_Watch(string profileName, string e
             Assert.Contains(App.Process.Output, l => l.Contains("Hot Reload disabled by command line switch."));
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData("P1", "argP1")]
         [InlineData("P and Q and \"R\"", "argPQR")]
         public async Task ArgumentsFromLaunchSettings_HotReload(string profileName, string expectedArgs)
@@ -187,7 +187,7 @@ public async Task ArgumentsFromLaunchSettings_HotReload(string profileName, stri
             Assert.Contains(App.Process.Output, l => l.Contains($"Found named launch profile '{profileName}'."));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task TestCommand()
         {
             var testAsset = TestAssets.CopyTestAsset("XunitCore")
@@ -212,7 +212,7 @@ public async Task TestCommand()
             App.AssertOutputContains("    TestNamespace.VSTestXunitTests.VSTestXunitPassTest2");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task TestCommand_MultiTargeting()
         {
             var testAsset = TestAssets.CopyTestAsset("XunitMulti")
@@ -224,7 +224,7 @@ public async Task TestCommand_MultiTargeting()
             await App.AssertOutputLineEquals("    TestNamespace.VSTestXunitTests.VSTestXunitFailTestNetCoreApp");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task BuildCommand()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchNoDepsApp")
@@ -242,7 +242,7 @@ public async Task BuildCommand()
             App.AssertOutputContains("warning : The value of property is '123'");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task MSBuildCommand()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchNoDepsApp")
@@ -260,7 +260,7 @@ public async Task MSBuildCommand()
             App.AssertOutputContains("warning : The value of property is '123'");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task PackCommand()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchNoDepsApp")
@@ -280,7 +280,7 @@ public async Task PackCommand()
             App.AssertOutputContains($"Successfully created package '{packagePath}'");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task PublishCommand()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchNoDepsApp")
@@ -299,7 +299,7 @@ public async Task PublishCommand()
             App.AssertOutputContains(Path.Combine("Release", ToolsetInfo.CurrentTargetFramework, "publish"));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task FormatCommand()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchNoDepsApp")
@@ -317,7 +317,7 @@ public async Task FormatCommand()
             App.AssertOutputContains("Format complete in");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task ProjectGraphLoadFailure()
         {
             var testAsset = TestAssets
diff --git a/test/dotnet-watch.Tests/ConsoleReporterTests.cs b/test/dotnet-watch.Tests/ConsoleReporterTests.cs
index c9008f820a5a..f87855e87799 100644
--- a/test/dotnet-watch.Tests/ConsoleReporterTests.cs
+++ b/test/dotnet-watch.Tests/ConsoleReporterTests.cs
@@ -9,7 +9,7 @@ public class ReporterTests
     {
         private static readonly string EOL = Environment.NewLine;
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData(true)]
         [InlineData(false)]
         public void WritesToStandardStreams(bool suppressEmojis)
@@ -35,7 +35,7 @@ public void WritesToStandardStreams(bool suppressEmojis)
             testConsole.Clear();
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData(true)]
         [InlineData(false)]
         public void WritesToStandardStreamsWithCustomEmojis(bool suppressEmojis)
diff --git a/test/dotnet-watch.Tests/FileWatcher/FileWatcherTests.cs b/test/dotnet-watch.Tests/FileWatcher/FileWatcherTests.cs
index b3ae38eb31c9..980a19373dcb 100644
--- a/test/dotnet-watch.Tests/FileWatcher/FileWatcherTests.cs
+++ b/test/dotnet-watch.Tests/FileWatcher/FileWatcherTests.cs
@@ -228,7 +228,7 @@ await TestOperation(
                 () => File.WriteAllText(file, string.Empty));
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [CombinatorialData]
         public async Task MoveFile(bool usePolling)
         {
diff --git a/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs b/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs
index 7a8451716636..d366a921bc6e 100644
--- a/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs
+++ b/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs
@@ -9,7 +9,7 @@ namespace Microsoft.DotNet.Watch.UnitTests
 {
     public class ApplyDeltaTests(ITestOutputHelper logger) : DotNetWatchTestBase(logger)
     {
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task AddSourceFile()
         {
             Log("AddSourceFile started");
@@ -46,7 +46,7 @@ public static void Print()
             await App.AssertOutputLineStartsWith("Changed!");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task ChangeFileInDependency()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchAppWithProjectDeps")
@@ -71,7 +71,7 @@ public static void Print()
             await App.AssertOutputLineStartsWith("Changed!");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Fact]
         public async Task ProjectChange_UpdateDirectoryBuildPropsThenUpdateSource()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchAppWithProjectDeps")
@@ -109,7 +109,7 @@ public static unsafe void Print()
             await App.WaitUntilOutputContains($"dotnet watch 🔥 [App.WithDeps ({ToolsetInfo.CurrentTargetFramework})] Hot reload succeeded.");
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [CombinatorialData]
         public async Task ProjectChange_Update(bool isDirectoryProps)
         {
@@ -236,7 +236,7 @@ public static void Print()
             await App.AssertOutputLineStartsWith("BUILD_CONST_IN_PROPS not set");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Fact]
         public async Task DefaultItemExcludes_DefaultItemsEnabled()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchHotReloadApp")
@@ -268,7 +268,7 @@ class X;
             await App.WaitUntilOutputContains($"dotnet watch ⌚ Ignoring change in excluded file '{appDataFilePath}': Add. Path matches DefaultItemExcludes glob 'AppData/**/*.*' set in '{testAsset.Path}'.");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Fact]
         public async Task DefaultItemExcludes_DefaultItemsDisabled()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchHotReloadApp")
@@ -310,7 +310,7 @@ public async Task DefaultItemExcludes_DefaultItemsDisabled()
             await App.WaitUntilOutputContains($"dotnet watch ⌚ Ignoring change in output directory: Add '{objDirFilePath}'");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows, Skip = "https://github.com/dotnet/sdk/issues/49542")] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact(Skip = "https://github.com/dotnet/sdk/issues/49542")]
         public async Task ProjectChange_GlobalUsings()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchHotReloadApp")
@@ -347,7 +347,7 @@ public async Task ProjectChange_GlobalUsings()
             App.AssertOutputContains(MessageDescriptor.ReEvaluationCompleted);
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [CombinatorialData]
         public async Task AutoRestartOnRudeEdit(bool nonInteractive)
         {
@@ -391,7 +391,7 @@ public async Task AutoRestartOnRudeEdit(bool nonInteractive)
             await App.AssertOutputLineStartsWith($"dotnet watch 🔥 [WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Hot reload succeeded.");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Fact]
         public async Task AutoRestartOnRudeEditAfterRestartPrompt()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchHotReloadApp")
@@ -432,7 +432,7 @@ public async Task AutoRestartOnRudeEditAfterRestartPrompt()
             App.AssertOutputContains($"[WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Launched");
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [CombinatorialData]
         public async Task AutoRestartOnNoEffectEdit(bool nonInteractive)
         {
@@ -480,7 +480,7 @@ public async Task AutoRestartOnNoEffectEdit(bool nonInteractive)
         /// <summary>
         /// Unchanged project doesn't build. Wait for source change and rebuild.
         /// </summary>
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task BaselineCompilationError()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchNoDepsApp")
@@ -503,7 +503,7 @@ public async Task BaselineCompilationError()
             await App.AssertOutputLineStartsWith("<Updated>", failure: _ => false);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task ChangeFileInFSharpProject()
         {
             var testAsset = TestAssets.CopyTestAsset("FSharpTestAppSimple")
@@ -518,7 +518,7 @@ public async Task ChangeFileInFSharpProject()
             await App.AssertOutputLineStartsWith("<Updated>");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task ChangeFileInFSharpProjectWithLoop()
         {
             var testAsset = TestAssets.CopyTestAsset("FSharpTestAppSimple")
@@ -588,7 +588,7 @@ public static void Print()
             await App.AssertOutputLineStartsWith("Updated types: Printer");
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task MetadataUpdateHandler_NoActions()
         {
             var testAsset = TestAssets.CopyTestAsset("WatchHotReloadApp")
@@ -620,7 +620,7 @@ await App.WaitUntilOutputContains(
                 $"dotnet watch ⚠ [WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Expected to find a static method 'ClearCache', 'UpdateApplication' or 'UpdateContent' on type 'AppUpdateHandler, WatchHotReloadApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' but neither exists.");
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [CombinatorialData]
         public async Task MetadataUpdateHandler_Exception(bool verbose)
         {
@@ -923,7 +923,7 @@ public static void Print()
             await App.AssertOutputLineStartsWith("Updated types: Printer");
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData(true, Skip = "https://github.com/dotnet/sdk/issues/43320")]
         [InlineData(false)]
         public async Task RenameSourceFile(bool useMove)
@@ -975,7 +975,7 @@ public static void PrintFileName([CallerFilePathAttribute] string filePath = nul
             await App.AssertOutputLineStartsWith("> Renamed.cs");
         }
 
-        [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+        [Theory]
         [InlineData(true, Skip = "https://github.com/dotnet/sdk/issues/43320")]
         [InlineData(false)]
         public async Task RenameDirectory(bool useMove)
diff --git a/test/dotnet-watch.Tests/HotReload/CompilationHandlerTests.cs b/test/dotnet-watch.Tests/HotReload/CompilationHandlerTests.cs
index f0ff0f72d9cb..615d62766238 100644
--- a/test/dotnet-watch.Tests/HotReload/CompilationHandlerTests.cs
+++ b/test/dotnet-watch.Tests/HotReload/CompilationHandlerTests.cs
@@ -5,7 +5,7 @@ namespace Microsoft.DotNet.Watch.UnitTests;
 
 public class CompilationHandlerTests(ITestOutputHelper logger) : DotNetWatchTestBase(logger)
 {
-    [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+    [Fact]
     public async Task ReferenceOutputAssembly_False()
     {
         var testAsset = TestAssets.CopyTestAsset("WatchAppMultiProc")
diff --git a/test/dotnet-watch.Tests/HotReload/HotReloadDotNetWatcherTests.cs b/test/dotnet-watch.Tests/HotReload/HotReloadDotNetWatcherTests.cs
index fa9680cb86f5..ac47a4ef9943 100644
--- a/test/dotnet-watch.Tests/HotReload/HotReloadDotNetWatcherTests.cs
+++ b/test/dotnet-watch.Tests/HotReload/HotReloadDotNetWatcherTests.cs
@@ -5,7 +5,7 @@ namespace Microsoft.DotNet.Watch.UnitTests;
 
 public class HotReloadDotNetWatcherTests
 {
-    [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+    [Theory]
     [InlineData(new[] { ChangeKind.Update }, new[] { ChangeKind.Update })]
     [InlineData(new[] { ChangeKind.Add }, new[] { ChangeKind.Add })]
     [InlineData(new[] { ChangeKind.Delete }, new[] { ChangeKind.Delete })]
diff --git a/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs b/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs
index db768163c52c..50f4bdb8687f 100644
--- a/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs
+++ b/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs
@@ -133,7 +133,7 @@ private RunningWatcher StartWatcher(TestAsset testAsset, string[] args, string w
         return new RunningWatcher(this, watcher, watchTask, reporter, console, serviceHolder, shutdownSource);
     }
 
-    [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+    [Theory]
     [CombinatorialData]
     public async Task UpdateAndRudeEdit(TriggerEvent trigger)
     {
@@ -294,7 +294,7 @@ async Task MakeRudeEditChange()
         }
     }
 
-    [PlatformSpecificTheory(TestPlatforms.Windows)] // https://github.com/dotnet/sdk/issues/49307
+    [Theory]
     [CombinatorialData]
     public async Task UpdateAppliedToNewProcesses(bool sharedOutput)
     {
@@ -393,7 +393,7 @@ public enum UpdateLocation
         TopFunction,
     }
 
-    [Theory(Skip = "https://github.com/dotnet/sdk/issues/49307")]
+    [Theory]
     [CombinatorialData]
     public async Task HostRestart(UpdateLocation updateLocation)
     {
@@ -484,7 +484,7 @@ public static void Print()
         await hasUpdate.WaitAsync(w.ShutdownSource.Token);
     }
 
-    [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+    [Fact]
     public async Task RudeEditInProjectWithoutRunningProcess()
     {
         var testAsset = CopyTestAsset("WatchAppMultiProc");
@@ -539,7 +539,7 @@ public enum DirectoryKind
         Obj,
     }
 
-    [PlatformSpecificTheory(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+    [Theory]
     [CombinatorialData]
     public async Task IgnoredChange(bool isExisting, bool isIncluded, DirectoryKind directoryKind)
     {
diff --git a/test/dotnet-watch.Tests/Process/LaunchSettingsProfileTest.cs b/test/dotnet-watch.Tests/Process/LaunchSettingsProfileTest.cs
index b0067ebb8124..44b57449f1c3 100644
--- a/test/dotnet-watch.Tests/Process/LaunchSettingsProfileTest.cs
+++ b/test/dotnet-watch.Tests/Process/LaunchSettingsProfileTest.cs
@@ -16,7 +16,7 @@ public LaunchSettingsProfileTest(ITestOutputHelper output)
         _testAssets = new TestAssetsManager(output);
     }
 
-    [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+    [Fact]
     public void LoadsLaunchProfiles()
     {
         var project = _testAssets.CreateTestProject(new TestProject("Project1")
@@ -60,7 +60,7 @@ public void LoadsLaunchProfiles()
         Assert.NotNull(expected);
     }
 
-    [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+    [Fact]
     public void DefaultLaunchProfileWithoutProjectCommand()
     {
         var project = _testAssets.CreateTestProject(new TestProject("Project1")
diff --git a/test/dotnet-watch.Tests/TestUtilities/TestOptions.cs b/test/dotnet-watch.Tests/TestUtilities/TestOptions.cs
index e18914e56173..020a7597ec2a 100644
--- a/test/dotnet-watch.Tests/TestUtilities/TestOptions.cs
+++ b/test/dotnet-watch.Tests/TestUtilities/TestOptions.cs
@@ -13,8 +13,12 @@ public static int GetTestPort()
     public static readonly ProjectOptions ProjectOptions = GetProjectOptions([]);
 
     public static EnvironmentOptions GetEnvironmentOptions(string workingDirectory = "", string muxerPath = "", TestAsset? asset = null)
-        // 0 timeout for process cleanup in tests. We can't send Ctrl+C, so process termination must be forced.
-        => new(workingDirectory, muxerPath, ProcessCleanupTimeout: TimeSpan.FromSeconds(0), TestFlags: TestFlags.RunningAsTest, TestOutput: asset != null ? GetWatchTestOutputPath(asset) : "");
+    {
+        // 0 timeout for process cleanup in tests. We can't send Ctrl+C on Windows, so process termination must be forced.
+        var processCleanupTimeout = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? TimeSpan.FromSeconds(0) : TimeSpan.FromSeconds(1);
+
+        return new(workingDirectory, muxerPath, processCleanupTimeout, TestFlags: TestFlags.RunningAsTest, TestOutput: asset != null ? GetWatchTestOutputPath(asset) : "");
+    }
 
     public static CommandLineOptions GetCommandLineOptions(string[] args)
         => CommandLineOptions.Parse(args, NullReporter.Singleton, TextWriter.Null, out _) ?? throw new InvalidOperationException();
diff --git a/test/dotnet-watch.Tests/TestUtilities/WatchableApp.cs b/test/dotnet-watch.Tests/TestUtilities/WatchableApp.cs
index a73e765a7923..dcbbc27b6073 100644
--- a/test/dotnet-watch.Tests/TestUtilities/WatchableApp.cs
+++ b/test/dotnet-watch.Tests/TestUtilities/WatchableApp.cs
@@ -124,7 +124,10 @@ public void Start(TestAsset asset, IEnumerable<string> arguments, string relativ
 
             var testOutputPath = asset.GetWatchTestOutputPath();
             Directory.CreateDirectory(testOutputPath);
+
+            // FileSystemWatcher is unreliable. Use polling for testing to avoid flakiness.
             commandSpec.WithEnvironmentVariable("DOTNET_USE_POLLING_FILE_WATCHER", "true");
+
             commandSpec.WithEnvironmentVariable("__DOTNET_WATCH_TEST_FLAGS", testFlags.ToString());
             commandSpec.WithEnvironmentVariable("__DOTNET_WATCH_TEST_OUTPUT_DIR", testOutputPath);
             commandSpec.WithEnvironmentVariable("Microsoft_CodeAnalysis_EditAndContinue_LogDir", testOutputPath);
diff --git a/test/dotnet-watch.Tests/Watch/BuildEvaluatorTests.cs b/test/dotnet-watch.Tests/Watch/BuildEvaluatorTests.cs
index f71b5a161ad1..33db911fe985 100644
--- a/test/dotnet-watch.Tests/Watch/BuildEvaluatorTests.cs
+++ b/test/dotnet-watch.Tests/Watch/BuildEvaluatorTests.cs
@@ -24,7 +24,7 @@ private static DotNetWatchContext CreateContext(bool suppressMSBuildIncrementali
             };
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task ProcessAsync_EvaluatesFileSetIfProjFileChanges()
         {
             var context = CreateContext();
@@ -41,7 +41,7 @@ public async Task ProcessAsync_EvaluatesFileSetIfProjFileChanges()
             Assert.True(evaluator.RequiresRevaluation);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task ProcessAsync_DoesNotEvaluateFileSetIfNonProjFileChanges()
         {
             var context = CreateContext();
@@ -60,7 +60,7 @@ public async Task ProcessAsync_DoesNotEvaluateFileSetIfNonProjFileChanges()
             Assert.Equal(1, counter);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task ProcessAsync_EvaluateFileSetOnEveryChangeIfOptimizationIsSuppressed()
         {
             var context = CreateContext(suppressMSBuildIncrementalism: true);
@@ -80,7 +80,7 @@ public async Task ProcessAsync_EvaluateFileSetOnEveryChangeIfOptimizationIsSuppr
             Assert.Equal(2, counter);
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public async Task ProcessAsync_SetsEvaluationRequired_IfMSBuildFileChanges_ButIsNotChangedFile()
         {
             // There's a chance that the watcher does not correctly report edits to msbuild files on
diff --git a/test/dotnet-watch.Tests/Watch/GlobbingAppTests.cs b/test/dotnet-watch.Tests/Watch/GlobbingAppTests.cs
index 381e1ead7f69..48ff617568cc 100644
--- a/test/dotnet-watch.Tests/Watch/GlobbingAppTests.cs
+++ b/test/dotnet-watch.Tests/Watch/GlobbingAppTests.cs
@@ -103,6 +103,28 @@ public async Task ChangeExcludedFile()
             Assert.NotSame(fileChanged, finished);
         }
 
+        [Fact]
+        public async Task ListsFiles()
+        {
+            var testAsset = TestAssets.CopyTestAsset(AppName)
+               .WithSource();
+
+            App.DotnetWatchArgs.Clear();
+            App.Start(testAsset, ["--list"]);
+            var lines = await App.Process.GetAllOutputLinesAsync(CancellationToken.None);
+            var files = lines.Where(l => !l.StartsWith("dotnet watch ⌚") && l.Trim() != "");
+
+            AssertEx.EqualFileList(
+                testAsset.Path,
+                new[]
+                {
+                    "Program.cs",
+                    "include/Foo.cs",
+                    "WatchGlobbingApp.csproj",
+                },
+                files);
+        }
+
         private async Task AssertCompiledAppDefinedTypes(int expected)
         {
             var prefix = "Defined types = ";
diff --git a/test/dotnet-watch.Tests/Watch/NoRestoreTests.cs b/test/dotnet-watch.Tests/Watch/NoRestoreTests.cs
index a837b2022fec..7d7319f84f44 100644
--- a/test/dotnet-watch.Tests/Watch/NoRestoreTests.cs
+++ b/test/dotnet-watch.Tests/Watch/NoRestoreTests.cs
@@ -23,7 +23,7 @@ private static DotNetWatchContext CreateContext(string[] args = null, Environmen
             };
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void LeavesArgumentsUnchangedOnFirstRun()
         {
             var context = CreateContext();
@@ -32,7 +32,7 @@ public void LeavesArgumentsUnchangedOnFirstRun()
             AssertEx.SequenceEqual(["run", InteractiveFlag], evaluator.GetProcessArguments(iteration: 0));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void LeavesArgumentsUnchangedIfMsBuildRevaluationIsRequired()
         {
             var context = CreateContext();
@@ -45,7 +45,7 @@ public void LeavesArgumentsUnchangedIfMsBuildRevaluationIsRequired()
             AssertEx.SequenceEqual(["run", InteractiveFlag], evaluator.GetProcessArguments(iteration: 1));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void LeavesArgumentsUnchangedIfOptimizationIsSuppressed()
         {
             var context = CreateContext([], TestOptions.GetEnvironmentOptions() with { SuppressMSBuildIncrementalism = true });
@@ -55,7 +55,7 @@ public void LeavesArgumentsUnchangedIfOptimizationIsSuppressed()
             AssertEx.SequenceEqual(["run", InteractiveFlag], evaluator.GetProcessArguments(iteration: 1));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void LeavesArgumentsUnchangedIfNoRestoreAlreadyPresent()
         {
             var context = CreateContext(["--no-restore"], TestOptions.GetEnvironmentOptions() with { SuppressMSBuildIncrementalism = true });
@@ -65,7 +65,7 @@ public void LeavesArgumentsUnchangedIfNoRestoreAlreadyPresent()
             AssertEx.SequenceEqual(["run", "--no-restore", InteractiveFlag], evaluator.GetProcessArguments(iteration: 1));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void LeavesArgumentsUnchangedIfNoRestoreAlreadyPresent_UnlessAfterDashDash1()
         {
             var context = CreateContext(["--", "--no-restore"]);
@@ -75,7 +75,7 @@ public void LeavesArgumentsUnchangedIfNoRestoreAlreadyPresent_UnlessAfterDashDas
             AssertEx.SequenceEqual(["run", "--no-restore", InteractiveFlag, "--", "--no-restore"], evaluator.GetProcessArguments(iteration: 1));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void LeavesArgumentsUnchangedIfNoRestoreAlreadyPresent_UnlessAfterDashDash2()
         {
             var context = CreateContext(["--", "--", "--no-restore"]);
@@ -85,7 +85,7 @@ public void LeavesArgumentsUnchangedIfNoRestoreAlreadyPresent_UnlessAfterDashDas
             AssertEx.SequenceEqual(["run", "--no-restore", InteractiveFlag, "--", "--", "--no-restore"], evaluator.GetProcessArguments(iteration: 1));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void AddsNoRestoreSwitch()
         {
             var context = CreateContext();
@@ -95,7 +95,7 @@ public void AddsNoRestoreSwitch()
             AssertEx.SequenceEqual(["run", "--no-restore", InteractiveFlag], evaluator.GetProcessArguments(iteration: 1));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void AddsNoRestoreSwitch_WithAdditionalArguments()
         {
             var context = CreateContext(["run", "-f", ToolsetInfo.CurrentTargetFramework]);
@@ -105,7 +105,7 @@ public void AddsNoRestoreSwitch_WithAdditionalArguments()
             AssertEx.SequenceEqual(["run", "--no-restore", "-f", ToolsetInfo.CurrentTargetFramework, InteractiveFlag], evaluator.GetProcessArguments(iteration: 1));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void AddsNoRestoreSwitch_ForTestCommand()
         {
             var context = CreateContext(["test", "--filter SomeFilter"]);
@@ -115,7 +115,7 @@ public void AddsNoRestoreSwitch_ForTestCommand()
             AssertEx.SequenceEqual(["test", "--no-restore", InteractiveFlag, "--filter SomeFilter"], evaluator.GetProcessArguments(iteration: 1));
         }
 
-        [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+        [Fact]
         public void DoesNotModifyArgumentsForUnknownCommands()
         {
             var context = CreateContext(["pack"]);