From ad07f8b43a1ee330f9642e55b4d3f78df3de1297 Mon Sep 17 00:00:00 2001 From: Yahoo <119646314+y4ho0@users.noreply.github.com> Date: Wed, 2 Sep 2026 00:03:53 +0800 Subject: [PATCH] fix(batch): preserve per-item warnings --- src/officecli/BatchTypes.cs | 9 +++++++ src/officecli/CommandBuilder.Batch.cs | 37 +++++++++++++++++++++++++-- src/officecli/CommandBuilder.cs | 8 ++++++ src/officecli/Core/OutputFormatter.cs | 2 +- 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/officecli/BatchTypes.cs b/src/officecli/BatchTypes.cs index 3ef487c29..7d916f953 100644 --- a/src/officecli/BatchTypes.cs +++ b/src/officecli/BatchTypes.cs @@ -225,6 +225,8 @@ public class BatchResult public string? Code { get; set; } /// The original batch item, included when the command fails so the agent can inspect/retry. public BatchItem? Item { get; set; } + /// Advisory diagnostics produced while executing this item. + internal List? Warnings { get; set; } } /// @@ -244,6 +246,8 @@ internal class BatchResultConverter : JsonConverter if (root.TryGetProperty("error", out var err)) result.Error = err.GetString(); if (root.TryGetProperty("code", out var cod)) result.Code = cod.GetString(); if (root.TryGetProperty("item", out var itm)) result.Item = JsonSerializer.Deserialize(itm.GetRawText(), BatchJsonContext.Default.BatchItem); + if (root.TryGetProperty("warnings", out var wrn)) + result.Warnings = JsonSerializer.Deserialize(wrn.GetRawText(), OfficeCli.Core.AppJsonContext.Default.ListCliWarning); return result; } @@ -277,6 +281,11 @@ public override void Write(Utf8JsonWriter writer, BatchResult value, JsonSeriali JsonSerializer.Serialize(writer, value.Item, BatchJsonContext.Default.BatchItem); } } + if (value.Warnings is { Count: > 0 }) + { + writer.WritePropertyName("warnings"); + JsonSerializer.Serialize(writer, value.Warnings, OfficeCli.Core.AppJsonContext.Default.ListCliWarning); + } writer.WriteEndObject(); } diff --git a/src/officecli/CommandBuilder.Batch.cs b/src/officecli/CommandBuilder.Batch.cs index cf7e2de42..06fb35bfa 100644 --- a/src/officecli/CommandBuilder.Batch.cs +++ b/src/officecli/CommandBuilder.Batch.cs @@ -77,14 +77,47 @@ internal static List ApplyBatchItems( continue; } } + // Batch dispatches straight to the shared handler methods, bypassing + // the standalone add/set wrappers that normally drain warnings into + // their output envelope. Capture a fresh warning scope per item so + // diagnostics retain the step that produced them. + OfficeCli.Core.WarningContext.Begin(); try { var output = ExecuteBatchItem(handler, item, json); - results.Add(new BatchResult { Index = bi, Success = true, Output = output }); + var warnings = OfficeCli.Core.WarningContext.End(); + if (handler is OfficeCli.Handlers.WordHandler word) + { + // Word advisories live on the handler rather than in + // WarningContext; standalone add/set merge these explicitly. + var advisory = string.Equals(item.Command, "add", StringComparison.OrdinalIgnoreCase) + ? word.LastAddWarnings + : string.Equals(item.Command, "set", StringComparison.OrdinalIgnoreCase) + ? word.LastSetWarnings + : null; + if (advisory is { Count: > 0 }) + { + warnings ??= new List(); + warnings.AddRange(advisory.Select(message => new OfficeCli.Core.CliWarning + { + Message = message, + Code = "advisory", + })); + } + } + results.Add(new BatchResult { Index = bi, Success = true, Output = output, Warnings = warnings }); } catch (Exception ex) { - results.Add(new BatchResult { Index = bi, Success = false, Item = item, Error = ex.Message, Code = OfficeCli.Core.OutputFormatter.InferErrorCode(ex) }); + results.Add(new BatchResult + { + Index = bi, + Success = false, + Item = item, + Error = ex.Message, + Code = OfficeCli.Core.OutputFormatter.InferErrorCode(ex), + Warnings = OfficeCli.Core.WarningContext.End(), + }); if (stopOnError) break; } // BUG-BT2: per-item unrecognized-LaTeX diagnostics. The handler diff --git a/src/officecli/CommandBuilder.cs b/src/officecli/CommandBuilder.cs index a88260224..6b5dc3628 100644 --- a/src/officecli/CommandBuilder.cs +++ b/src/officecli/CommandBuilder.cs @@ -1419,6 +1419,11 @@ internal static void PrintBatchResults(List results, bool json, int System.Text.Json.JsonSerializer.Serialize(slimWriter, r.Item, BatchJsonContext.Default.BatchItem); } } + if (r.Warnings is { Count: > 0 }) + { + slimWriter.WritePropertyName("warnings"); + System.Text.Json.JsonSerializer.Serialize(slimWriter, r.Warnings, OfficeCli.Core.AppJsonContext.Default.ListCliWarning); + } slimWriter.WriteEndObject(); } slimWriter.WriteEndArray(); @@ -1452,6 +1457,9 @@ internal static void PrintBatchResults(List results, bool json, int { @out.WriteLine($"{prefix}ERROR: {r.Error}"); } + if (r.Warnings is { Count: > 0 }) + foreach (var warning in r.Warnings) + @out.WriteLine($" WARNING: {warning.Message}"); } var succeeded = results.Count(r => r.Success); diff --git a/src/officecli/Core/OutputFormatter.cs b/src/officecli/Core/OutputFormatter.cs index cdfa57590..f352e2f08 100644 --- a/src/officecli/Core/OutputFormatter.cs +++ b/src/officecli/Core/OutputFormatter.cs @@ -74,7 +74,7 @@ internal class CliWarning } /// -/// Thread-static context for capturing warnings during command execution in JSON mode. +/// Thread-static context for capturing warnings during command execution. /// internal static class WarningContext {