From 2a44ea3c2ce486010ce051684481bc5fd9b7cae9 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Tue, 7 Jul 2026 00:28:36 -0400 Subject: [PATCH] =?UTF-8?q?feat(phase-b):=20sweep=20[--boot]=20=E2=80=94?= =?UTF-8?q?=20corpus-scale=20VM+state=20validation=20harness?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Runs every SC/SP scene through GameSession (fresh or booted-from-snapshot) and reports halt distribution + line counts. Unbooted matches the vm0.py A0 baseline (294 exit + the same 3 LOOP scenes) — cross-validates the C# VM at scale. FINDING: booting the *INIT data tables gives byte-identical results (regression- free) but does NOT change ADV dialogue flow — *INIT feeds gameplay; ADV branches key on story-state flags (chapter/form/choices) from the progression layer, not *INIT. So story-state seeding (e.g. the Lily form flag) is the ADV-unlock lever. Co-Authored-By: Claude Opus 4.8 (1M context) --- engine/Age.Cli/Program.cs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/engine/Age.Cli/Program.cs b/engine/Age.Cli/Program.cs index 9680be1..3bbfbae 100644 --- a/engine/Age.Cli/Program.cs +++ b/engine/Age.Cli/Program.cs @@ -103,6 +103,42 @@ if (args[0] == "play") return 0; } +if (args[0] == "sweep") +{ + // sweep [--boot] — run every SC/SP scene through GameSession (each from a fresh or booted-from-snapshot + // baseline) and report halt distribution + line counts. Validates the VM + state substrate at scale and + // surfaces how booted real data affects the corpus. Headless. + var sceneRe = new Regex(@"^S[CP]\d{4}\.BIN$"); + var scripts = Paths.Scripts(); + var names = scripts.Keys.Where(n => sceneRe.IsMatch(n)).OrderBy(n => n, StringComparer.Ordinal).ToList(); + bool boot = args.Contains("--boot"); + string? baseline = null; + if (boot) + { + var bootSession = new GameSession(); + foreach (var s in new[] { "SKINIT.BIN", "ITINIT.BIN", "EBINIT.BIN", "CGINIT.BIN", "MPINIT.BIN", + "AFINIT.BIN", "CCINIT.BIN", "STINIT.BIN", "STINIT2.BIN" }) + bootSession.RunScene(Sys4Loader.Load(scripts[s], table), table, new CaptureHost()); + baseline = bootSession.ToJson(); + Console.WriteLine($"[boot] baseline = {bootSession.Globals.Count} globals; running {names.Count} scenes from it."); + } + var haltDist = new SortedDictionary(StringComparer.Ordinal); + long totalLines = 0; var anomalies = new List(); + foreach (var name in names) + { + var session = baseline != null ? GameSession.FromJson(baseline) : new GameSession(); + var r = session.RunScene(Sys4Loader.Load(scripts[name], table), table, new CaptureHost()); + var halt = r.Halt ?? "null"; + haltDist[halt] = haltDist.GetValueOrDefault(halt) + 1; + totalLines += r.Emitted.Count; + if (halt != "exit") anomalies.Add($"{name}: {r.Emitted.Count} lines, halt={halt}"); + } + Console.WriteLine($"swept {names.Count} scenes{(boot ? " (booted)" : "")}: {totalLines} total lines"); + Console.WriteLine("halt distribution: " + string.Join(", ", haltDist.Select(kv => $"{kv.Key}={kv.Value}"))); + if (anomalies.Count > 0) { Console.WriteLine($"non-exit halts ({anomalies.Count}):"); foreach (var a in anomalies) Console.WriteLine(" " + a); } + return 0; +} + if (args[0] == "trace") { var scene = new Regex(@"^S[CP]\d{4}\.BIN$");