From 02c6d23185373102a4f9f31da6369c63d544e217 Mon Sep 17 00:00:00 2001 From: indaco Date: Thu, 7 May 2026 17:38:47 +0200 Subject: [PATCH] fix(dsl/fallback_log): warn when an OOM drops a fallback entry Surface a one-line stderr warning when the diagnostic log fails to grow so the user keeps the signal that a post_install was partially skipped under memory pressure. --- src/core/dsl/fallback_log.zig | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/core/dsl/fallback_log.zig b/src/core/dsl/fallback_log.zig index 7c486178..6d0777e0 100644 --- a/src/core/dsl/fallback_log.zig +++ b/src/core/dsl/fallback_log.zig @@ -48,10 +48,12 @@ pub const FallbackLog = struct { } pub fn log(self: *FallbackLog, entry: FallbackEntry) void { - // Silent drop on OOM: parent allocator failures surface elsewhere - // and threading ctx through every call site just for a warning - // would be more noise than signal. - self.entries.append(self.allocator, entry) catch {}; + self.entries.append(self.allocator, entry) catch { + // OOM still drops the entry, but stay loud about it: this log + // is the only signal the user has for "post_install was + // partially skipped" during a large `mt migrate`. + output.writeStderrAll("malt: fallback log dropped an entry due to OOM\n"); + }; } pub fn hasErrors(self: *const FallbackLog) bool { @@ -179,3 +181,23 @@ test "dslDidWork: at least one handled statement flips the signal" { try std.testing.expect(flog.hasErrors()); try std.testing.expect(flog.dslDidWork()); } + +// Under memory pressure the diagnostic log itself can fail to grow. +// A silent drop hides the only signal the user has for "post_install +// was partially skipped"; surface a one-line warning instead. +test "log surfaces a warning when the entry append OOMs" { + var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{ .fail_index = 0 }); + var flog = FallbackLog.init(failing.allocator()); + defer flog.deinit(); + + var buf: std.ArrayList(u8) = .empty; + defer buf.deinit(std.testing.allocator); + output.beginStderrCapture(std.testing.allocator, &buf); + defer output.endStderrCapture(); + + flog.log(.{ .formula = "demo", .reason = .unknown_method, .detail = "boom", .loc = null }); + + try std.testing.expectEqual(@as(usize, 0), flog.entries.items.len); + try std.testing.expect(std.mem.indexOf(u8, buf.items, "fallback log dropped") != null); + try std.testing.expect(std.mem.endsWith(u8, buf.items, "\n")); +}