From 5bd39d56c428f2b7f1969011c1ea20691a6280b0 Mon Sep 17 00:00:00 2001 From: codex Date: Mon, 10 Aug 2026 13:26:19 +0300 Subject: [PATCH] fix: guard p-rec-annot line refresh apply --- .../LineNumberRefreshCommand.cs | 15 +++++++++++++++ .../LineNumberEditCommandRegistrationTests.cs | 3 +++ 2 files changed, 18 insertions(+) diff --git a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/LineNumberRefreshCommand.cs b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/LineNumberRefreshCommand.cs index ad0f280..efa4def 100644 --- a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/LineNumberRefreshCommand.cs +++ b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/LineNumberRefreshCommand.cs @@ -35,6 +35,19 @@ internal static class LineNumberRefreshCommand var apply = preview.Plan!.Operations.Where(operation => operation.Kind is AnnotationOperationKind.Create or AnnotationOperationKind.Update) .Select(operation => new LineNumberApplyPreflight(operation.StableId, rows[operation.StableId].MarkerNumber, operation.Text!, operation.Position, operation.Rotation, operation.Layer, operation.TextStyle)) .OrderBy(operation => operation.MarkerNumber).ToArray(); + if (!IsApply()) + { + document.Editor.WriteMessage($"\nPXP-ULN preview: changes={apply.Length}; DWG не изменён."); + RuntimeDiagnostics.Write("LineNumberRefreshPreviewCompleted", commandName: "PXP-ULN", resultStatus: "PreviewCompleted", document: document, + details: new Dictionary { ["OperationCount"] = apply.Length, ["Scope"] = "ActiveDrawing" }); + return; + } + + // The controlled Apply contract is deliberately narrow: a permit + // binds one resolved source/text pair, preventing a refresh from + // widening into an unreviewed drawing-wide write. + if (apply.Length != 1) throw new InvalidOperationException("LINE_NUMBER_REFRESH_APPLY_REQUIRES_EXACTLY_ONE_OPERATION"); + LineNumberApplyPermitReader.Validate(document, LineNumberApplyPermitReader.Read(), apply[0]); var writer = new LineNumberAnnotationWriter(); var markerResult = writer.Apply(document, apply, configuration.Assets); var mutations = apply.Select(item => new LineNumberRowMutation(item.StableId, rows[item.StableId].MarkerNumber, item.MarkerNumber, item.Text, null)).ToArray(); @@ -50,6 +63,8 @@ internal static class LineNumberRefreshCommand } } + private static bool IsApply() => string.Equals(Environment.GetEnvironmentVariable("P_REC_ANNOT_RUNTIME_MODE"), "Apply", StringComparison.OrdinalIgnoreCase); + private static LineNumberCandidate ReadCandidate(Database database, LineNumberOwnedRow row, Autodesk.ProcessPower.DataLinks.DataLinksManager manager, AnnotateLineNumbersAssets assets) { const string prefix = "acad:"; diff --git a/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/LineNumberEditCommandRegistrationTests.cs b/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/LineNumberEditCommandRegistrationTests.cs index 6afce6b..3ec83f2 100644 --- a/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/LineNumberEditCommandRegistrationTests.cs +++ b/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/LineNumberEditCommandRegistrationTests.cs @@ -50,6 +50,9 @@ public sealed class LineNumberEditCommandRegistrationTests Assert.Contains("LineNumberRefreshCommand.Run", declaration); Assert.DoesNotContain("RunDiagnostic", declaration); var refresh = File.ReadAllText(Path.Combine(ProjectRoot, "LineNumberRefreshCommand.cs")); + Assert.Contains("if (!IsApply())", refresh); + Assert.Contains("LINE_NUMBER_REFRESH_APPLY_REQUIRES_EXACTLY_ONE_OPERATION", refresh); + Assert.Contains("LineNumberApplyPermitReader.Validate", refresh); Assert.Contains("PlantLineNumberCandidateReader", refresh); Assert.Contains("LineNumberCommandPreviewService", refresh); Assert.Contains("ApplyEdits", refresh);