feat: add guarded controlled entity selection transport
This commit is contained in:
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user