feat: add guarded controlled entity selection transport

This commit is contained in:
codex
2026-08-10 15:03:38 +03:00
parent e12b8c549c
commit 5a14658994
5 changed files with 53 additions and 7 deletions
+15
View File
@@ -21,6 +21,10 @@ public sealed record PilotLocalConfiguration
public bool AutomationDiagnostic { get; init; }
public string? AutomationConfigurationPath { get; init; }
public string? FixtureMarkerPath { get; init; }
// Controlled automation may reproduce an actual Editor.GetEntity boundary
// only with this immutable, explicitly typed fixture selection. It is never
// populated for normal production launches.
public ControlledEntitySelection? AutomationSelection { get; init; }
// Test-only: permits a pilot process to be terminated after its plugin has
// persisted completed automation evidence. It never represents graceful exit.
public bool ForceAfterEvidence { get; init; }
@@ -34,6 +38,16 @@ public sealed record PilotLocalConfiguration
public IReadOnlyDictionary<string, string?> PathVariables { get; init; } = new Dictionary<string, string?>();
public static PilotLocalConfiguration SafeDefault { get; } = new();
}
public sealed record ControlledEntitySelection(string CommandName, string ObjectHandle, string ExpectedDxfName)
{
public void Validate()
{
if (string.IsNullOrWhiteSpace(CommandName) || !CommandName.All(value => char.IsAsciiLetterOrDigit(value) || value is '-' or '_') ||
string.IsNullOrWhiteSpace(ObjectHandle) || ObjectHandle.Length > 32 || !ObjectHandle.All(Uri.IsHexDigit) ||
string.IsNullOrWhiteSpace(ExpectedDxfName) || !ExpectedDxfName.All(value => char.IsAsciiLetterOrDigit(value) || value == '_'))
throw new InvalidOperationException("AUTOMATION_ENTITY_SELECTION_INVALID");
}
}
public enum AcadLaunchMode { Production, NextPilotPreview, NextPilotApply, LifecycleProbe }
public sealed record PilotConfigurationLoadResult(PilotLocalConfiguration Configuration, IReadOnlyList<string> Errors)
{ public bool IsValid => Errors.Count == 0; }
@@ -83,6 +97,7 @@ public sealed class NextPilotSafetyGate : INextPilotSafetyGate
if (c.Configuration.PluginExecutionMode == PluginExecutionMode.Apply && (!Path.IsPathFullyQualified(c.Configuration.ApplyPermitPath ?? string.Empty) || (c.Configuration.ApplyPermitPath ?? string.Empty).StartsWith("\\\\", StringComparison.Ordinal))) v.Add("APPLY_PERMIT_PATH_REQUIRED");
var automation = c.Configuration.LaunchMode is AcadLaunchMode.NextPilotPreview or AcadLaunchMode.NextPilotApply or AcadLaunchMode.LifecycleProbe;
if (automation && (!string.Equals(c.Configuration.AutomationMode, "FirstLimitedApplyRuntimeTest", StringComparison.Ordinal) || !Path.IsPathFullyQualified(c.Configuration.AutomationConfigurationPath ?? string.Empty) || (c.Configuration.AutomationConfigurationPath ?? string.Empty).StartsWith("\\\\", StringComparison.Ordinal) || !Path.IsPathFullyQualified(c.Configuration.FixtureMarkerPath ?? string.Empty) || (c.Configuration.FixtureMarkerPath ?? string.Empty).StartsWith("\\\\", StringComparison.Ordinal) || c.Configuration.ProductionDeploy)) v.Add("AUTOMATION_CONFIGURATION_INVALID");
try { c.Configuration.AutomationSelection?.Validate(); } catch (InvalidOperationException) { v.Add("AUTOMATION_ENTITY_SELECTION_INVALID"); }
if (!automation && c.Configuration.AutomationMode is not null) v.Add("AUTOMATION_MODE_MISMATCH");
if (c.Configuration.ForceAfterEvidence && !automation) v.Add("FORCED_CLEANUP_TEST_AUTOMATION_REQUIRED");
if (c.Product is null) v.Add("PRODUCT_NOT_FOUND"); else if (c.Product.ProductVersion != "2026" || c.Product.Architecture != "x64") v.Add("PRODUCT_NOT_PLANT3D_2026");