fix: make p-rec-update initialization load-safe

This commit is contained in:
codex
2026-08-07 07:34:54 +03:00
parent 20960ae91f
commit 70ae18981a
4 changed files with 63 additions and 6 deletions
@@ -9,8 +9,18 @@ public sealed class EntryPoint : IExtensionApplication
{
public void Initialize()
{
RuntimeMarker.Write("AssemblyInitializeStarted");
RuntimeMarker.Write("AssemblyInitializeCompleted");
// Keep the load boundary free of Plant/DataLinks access. Any exception
// here must remain observable independently of a project fixture.
RuntimeMarker.Write("InitializeEntered");
try
{
RuntimeMarker.Write("AssemblyInitializeCompleted");
}
catch (System.Exception exception)
{
RuntimeMarker.Write("AssemblyInitializeFailed", exception.GetType().Name + ":" + exception.Message);
throw;
}
}
public void Terminate() => RuntimeMarker.Write("AssemblyTerminateCompleted");
@@ -37,8 +47,27 @@ public sealed class EntryPoint : IExtensionApplication
},
UpdateExecutionMode.DryRun,
ControlledFixturePermit: false));
var status = new
{
schemaVersion = 1,
operationId = Environment.GetEnvironmentVariable("P_REC_UPDATE_OPERATION_ID") ?? "unknown",
processId = Environment.ProcessId,
command,
assemblyVersion = typeof(EntryPoint).Assembly.GetName().Version?.ToString(),
assemblySha256 = RuntimeMarker.AssemblySha256(),
product = "Plant3D",
productYear = 2026,
activeDocumentPresent = document is not null,
activePlantProjectPresent = HasPlantProject(),
dryRunInfrastructure = "Available",
domainApply = "BlockedByExactFixture",
diagnostics = plan.Diagnostics
};
document?.Editor.WriteMessage($"\nP_Rec_Update {command}: {(plan.Accepted ? "DryRun planned; no project data changed." : string.Join(", ", plan.Diagnostics))}");
RuntimeMarker.Write(plan.Accepted ? "CommandRegistrationVerified" : "CommandPreflightRejected");
// STATUS proves registration and reports preflight state; it must not
// be classified as a failed command merely because no controlled Plant
// project fixture is open.
RuntimeMarker.Write(command == "P-R-UPD-STATUS" || plan.Accepted ? "CommandRegistrationVerified" : "CommandPreflightRejected", System.Text.Json.JsonSerializer.Serialize(status));
}
private static bool HasPlantProject()
@@ -1,19 +1,30 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace PRecUpdate.Plant3D2026;
internal static class RuntimeMarker
{
internal static void Write(string state)
internal static void Write(string state, string? detail = null)
{
var root = Environment.GetEnvironmentVariable("P_REC_UPDATE_MARKER_ROOT");
if (string.IsNullOrWhiteSpace(root)) return;
try
{
Directory.CreateDirectory(root);
var payload = $"{DateTimeOffset.UtcNow:O}|{state}|{typeof(EntryPoint).Assembly.GetName().Name}";
File.WriteAllText(Path.Combine(root, state + ".marker"), payload, new UTF8Encoding(false));
var payload = JsonSerializer.Serialize(new
{
schemaVersion = 1,
operationId = Environment.GetEnvironmentVariable("P_REC_UPDATE_OPERATION_ID") ?? "unknown",
processId = Environment.ProcessId,
timestampUtc = DateTimeOffset.UtcNow,
state,
detail,
assembly = typeof(EntryPoint).Assembly.GetName().Name,
assemblySha256 = AssemblySha256()
});
File.WriteAllText(Path.Combine(root, state + ".marker.json"), payload, new UTF8Encoding(false));
}
catch { /* Evidence cannot make Plant initialization fail. */ }
}
@@ -46,4 +46,17 @@ public sealed class UpdatePlanBuilderTests
Assert.True(plan.MayWriteDrawing);
Assert.True(plan.MayWriteProjectData);
}
[Fact]
public void Status_probe_preflight_is_deterministic_and_never_write_capable()
{
var request = new UpdateRequest(true, false, false, new HashSet<UpdateOperation>(), UpdateExecutionMode.DryRun, false);
var first = builder.Build(request);
var second = builder.Build(request);
Assert.False(first.Accepted);
Assert.Equal(first.Diagnostics, second.Diagnostics);
Assert.False(first.MayWriteDrawing);
Assert.False(first.MayWriteProjectData);
Assert.Contains("PLANT_PROJECT_REQUIRED", first.Diagnostics);
}
}