diff --git a/Launcher.Next.App/PilotCli.cs b/Launcher.Next.App/PilotCli.cs index 1186fac..eead713 100644 --- a/Launcher.Next.App/PilotCli.cs +++ b/Launcher.Next.App/PilotCli.cs @@ -127,7 +127,12 @@ internal static class PilotCli if (!TryResolvePaths(config, out var paths, out _)) return new(false, false, false, false, false); var service = CreateService(paths); var product = new ProductInstallation(paths.AcadExecutable, "2026", "x64", []); var request = new PilotPreparationRequest(config, product, manifest, manifestHash, options.PackageSource, options.PilotRoot, Path.Combine(options.PilotRoot, "startup", phase + ".scr")); - var prepared = await service.PrepareAsync(request, CancellationToken.None); if (prepared.Plan is null) return new(false, false, false, false, false); + var prepared = await service.PrepareAsync(request, CancellationToken.None); + if (prepared.Plan is null) + { + Console.Error.WriteLine("AUTOMATION_PREPARE_BLOCKED:" + string.Join(',', prepared.SafetyDecision.Violations)); + return new(false, false, false, false, false); + } var execution = await service.ExecuteAsync(new(request, prepared.Plan, PermitService.Issue(prepared.Plan, config.ExecutionPermitLifetimeSeconds)), CancellationToken.None); if (!execution.Started || execution.ProcessId is null) return new(false, false, false, false, false); var forcedCleanup = false; var gracefulExit = false; diff --git a/tests/Launcher.Next.Tests/PathAndLaunchTests.cs b/tests/Launcher.Next.Tests/PathAndLaunchTests.cs index 28a3221..821a8f6 100644 --- a/tests/Launcher.Next.Tests/PathAndLaunchTests.cs +++ b/tests/Launcher.Next.Tests/PathAndLaunchTests.cs @@ -90,6 +90,11 @@ public sealed class PathAndLaunchTests [Fact] public void ProductionLaunch_DoesNotExposeSentinels() { var source = File.ReadAllText(Path.Combine(FindRoot(), "Launcher.Next.Autodesk", "AutodeskAdapters.cs")); Assert.DoesNotContain("PHP_REC_AUTOMATION_BEFORE_PXP_1", source[..source.IndexOf("BuildAutomationScript", StringComparison.Ordinal)]); } [Fact] public void CorporateDfsRoot_RemainsConfigurationOnly() => Assert.DoesNotContain("phimproject.com", File.ReadAllText(Path.Combine(FindRoot(), "Launcher.Next.Core", "LauncherPaths.cs")), StringComparison.OrdinalIgnoreCase); [Fact] public void PilotRootPolicy_AcceptsItsTrustedRoot() => Assert.True(new PilotRootPolicy("C:\\Pilot").Validate("C:\\Pilot").IsValid); + [Fact] public void AutomationPrepare_ReportsSafetyViolations() + { + var source = File.ReadAllText(Path.Combine(FindRoot(), "Launcher.Next.App", "PilotCli.cs")); + Assert.Contains("AUTOMATION_PREPARE_BLOCKED", source); + } [Fact] public void ProductionSource_HasNoForbiddenPathLiterals() {