aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/src/Godosa.Core/Runs/Scripts/RunReport.cs
diff options
context:
space:
mode:
authorgodosa <godosa@godosa.eu>2026-10-06 23:39:36 +0200
committergodosa <godosa@godosa.eu>2026-10-06 23:39:36 +0200
commit39066900773e7857faf2d02e7ed51b71d219d97d (patch)
treeffab8e4ddd971626776c3b1f4ce1da2c186c2187 /src/Godosa.Core/Runs/Scripts/RunReport.cs
downloadgodosa-engine-39066900773e7857faf2d02e7ed51b71d219d97d.tar.gz
godosa-engine-39066900773e7857faf2d02e7ed51b71d219d97d.zip
godosa-engine: initial public history
Diffstat (limited to 'src/Godosa.Core/Runs/Scripts/RunReport.cs')
-rw-r--r--src/Godosa.Core/Runs/Scripts/RunReport.cs58
1 files changed, 58 insertions, 0 deletions
diff --git a/src/Godosa.Core/Runs/Scripts/RunReport.cs b/src/Godosa.Core/Runs/Scripts/RunReport.cs
new file mode 100644
index 0000000..f0ecf0d
--- /dev/null
+++ b/src/Godosa.Core/Runs/Scripts/RunReport.cs
@@ -0,0 +1,58 @@
+// Provenance: own design — shared run scripts (spec §7.2, §7.3)
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Godosa.Core.Runs.Scripts;
+
+public sealed record CheckpointRecord(string Name, long Frame, string Digest, string Source);
+
+public sealed record FailureInfo(string At, IReadOnlyList<string> Chain, string Step, string? Label, string Reason,
+ IReadOnlyList<string> Recent, string Dump, IReadOnlyDictionary<string, string> Vars, string? Snapshot);
+
+public sealed record RunReport(string Script, RunMode Mode, ulong Seed, bool Deterministic, RunOutcome Outcome, long Frames,
+ int Steps, double WallSeconds, int CacheHits, int CacheMisses, IReadOnlyList<CheckpointRecord> Checkpoints,
+ IReadOnlyList<string> Warnings, FailureInfo? Failure);
+
+public static class ReportWriter
+{
+ private static readonly JsonSerializerOptions Json = new() { WriteIndented = true, Converters = { new JsonStringEnumConverter() } };
+
+ public static string Text(RunReport r)
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine($"script: {r.Script} mode: {r.Mode.ToString().ToLowerInvariant()} seed: {r.Seed} deterministic: {(r.Deterministic ? "yes" : "no")}");
+ sb.AppendLine($"outcome: {r.Outcome} frames: {r.Frames} steps: {r.Steps} wall: {r.WallSeconds:0.0} s cache: {r.CacheHits} hit / {r.CacheMisses} miss");
+ foreach (var c in r.Checkpoints)
+ sb.AppendLine($"checkpoint {c.Name} f={c.Frame} {c.Digest} ({c.Source})");
+ foreach (var w in r.Warnings)
+ sb.AppendLine($"warning: {w}");
+ if (r.Failure is { } f)
+ {
+ sb.AppendLine();
+ sb.AppendLine($"FAILED at {string.Join(" → ", f.Chain.Append(f.At))}{(f.Label == null ? "" : $" [{f.Label}]")}");
+ sb.AppendLine($" step: {f.Step}");
+ sb.AppendLine($" reason: {f.Reason}");
+ sb.AppendLine("recent steps:");
+ foreach (var s in f.Recent)
+ sb.AppendLine($" {s}");
+ sb.AppendLine("variables:");
+ foreach (var (k, v) in f.Vars.OrderBy(kv => kv.Key, StringComparer.Ordinal))
+ sb.AppendLine($" {k} = {v}");
+ sb.AppendLine("game state:");
+ sb.AppendLine(f.Dump);
+ if (f.Snapshot != null)
+ sb.AppendLine($"failure save: {f.Snapshot}");
+ }
+ return sb.ToString();
+ }
+
+ public static void Write(string dir, RunReport report, byte[]? failSnapshot)
+ {
+ Directory.CreateDirectory(dir);
+ File.WriteAllText(Path.Combine(dir, "report.txt"), Text(report));
+ File.WriteAllText(Path.Combine(dir, "report.json"), JsonSerializer.Serialize(report, Json));
+ if (failSnapshot != null)
+ File.WriteAllBytes(Path.Combine(dir, "fail.snap"), failSnapshot);
+ }
+}