fix(overhaul): integrate reviewed resize capture sequencing

This commit is contained in:
Erik 2026-09-05 13:26:06 +02:00
parent 58185285d3
commit 1e376c244b
5 changed files with 205 additions and 0 deletions

View file

@ -773,6 +773,11 @@ images), narrow scheduling diagnosis active. Settled hands rest/combat/rest
provisional PASS; no owner acceptance. All eight PNG paths/hashes in
476-480-graphical-r1.md. No FPS work, no main merge; G4 remains FAIL.
F2 diagnostic-only resize yield now integrates: old-extent capture was valid,
automation queued it before next-frame recreation. Two files, root mutation
and restored68PASS, narrow reviewPASS; no GPU/retail change or new deviation.
Exact integrated graphical replay still owed; see476-lead-verification.md.
## 14. Risk register (condensed)
- **R1 second scene graph.** Graph records own only topology/membership facts;

View file

@ -133,3 +133,40 @@ alongside preserved AD-118 retirement, counted105total/13retired/92active.
Fresh integrated Release/tests and current-frame graphical PASS are still owed.
Owner12:53 FPS deferral remains binding; no profiling/optimization is part
of this capture repair or its graphical correctness run.
## F2 — synchronous automation resize, not stale GPU bytes
The exact integrated0ca10cf24 graphical run is recorded in
476-480-graphical-r1.md: startup and settled capture coherent, but both
first-resize requests captured the old extent. Lead and James independently
traced synchronous window resize inside the already-open render; the test
driver queued its screenshot before next-boundary swapchain recreation.
476-fix-round2-resize-driver.md at58185285d bounds the diagnostic-only fix.
Frozen new scratch s5-476-resize-driver: two files only, runner+11 and
probe tests+143. Successful resize advances once then yields its command
loop; explicit following framebuffer wait remains. No GPU, capture policy,
route, retail algorithm, frame timing policy or FPS changes. No new retail
deviation/register row; AD-121 unchanged. Existing async wait still passes.
Implementer authoritative focused-r5 TRX33PASS; earlier attempts produced
no test evidence or failed before testing, not counted. Root removed the
actual production yield and the synchronous witness failed Assert.Empty:
`first-resized-frame` already requested in the resize Tick. Root's first
sandboxed invocation failed at NuGet.Config access before compiling/testing;
the permitted --no-restore invocation produced the intended mutation failure.
Exact source restoration, then Release build and probe/capture/orchestrator
68PASS/0FAIL/0SKIP. Wegener narrow F2 review PASS, no findings; no unchanged
GPU or retail lens was reopened. No command/client remains active.
| F2 seal (scratch-relative artifacts) | SHA256 |
| --- | --- |
| RetailUiAutomationScriptRunner.cs | 8890A79B19A4EC6138F4E4EFE4AD0611448B4F3B3E67E892F8F5FD07A035650D |
| RetailUiAutomationProbeTests.cs | F3E8D7B10B7D93F76B0272A9A1893FCC260376EC17445C7799E665EA313A373F |
| artifacts/476-fix-round2/focused-r5/476-fix-round2-focused.trx | 51EB0646891A5E1EA0B9ED9488721549968602CCD6EAD6D743EFBD89F5E57A78 |
| artifacts/476-fix-round2/root-mutation/root-476-f2-mutation.trx | 7EA5EC8B71F718859677C5F12EE05891715D4EDF4B7926E9D0044CF77CFCD902 |
| artifacts/476-fix-round2/root-restored/root-476-f2-restored.trx | FBE295ED40D025B3E983F0DF337EE59079B4BE131DCBE90769BCE3CA0284292A |
Lead integrates the exact two blobs with this ledger; fresh integrated
Release/68 tests and unchanged first-request/two-resize graphical route next.
R1 FAIL remains evidence until an actual passing replay, no waived image.

View file

@ -4873,3 +4873,12 @@ item and NPC weapons through rest/combat/rest: provisional narrow PASS.
476-480-graphical-r1.md records exact paths, hashes, scope and lifecycle facts.
No new production change, no register deviation. #478/#479 follow capture
resolution. #477 owner-crash soak still owed; FPS/C2 remain deferred, G4 FAIL.
F2 diagnosis independently confirmed: automation resized the window and
queued capture within the same old-extent render; GPU bytes were current.
Bounded contract58185285d, two-file OpenAI return, root actual-yield-removal
mutation fails and exact restoration/Release68PASS. Wegener narrow F2 PASS,
no findings; no GPU/capture/retail/route change or new deviation. Integrating
successful-resize command-loop yield, then fresh exact-binary graphical
replay. 476-lead-verification.md has seals. No third fix round authorized;
unchanged FPS deferral and G4 FAIL remain.

View file

@ -246,8 +246,19 @@ public sealed class RetailUiAutomationScriptRunner : IDisposable
var command = _commands[_index];
bool finished = Execute(command);
if (!finished) break;
bool yieldAfterResize = command.Parts.Length > 0
&& string.Equals(
command.Parts[0],
"resize",
StringComparison.OrdinalIgnoreCase);
_index++;
_activeIndex = -1;
// Resize is applied while automation is inside private presentation,
// after Vulkan prepared the current frame. Yield so the following
// framebuffer wait cannot observe the new window size until the
// next render has crossed its frame-boundary swapchain recreation.
if (yieldAfterResize)
break;
}
if (_index >= _commands.Count && !_completed)

View file

@ -52,6 +52,8 @@ public sealed class RetailUiAutomationProbeTests
public int RenderPackDisableCount { get; private set; }
public int RenderPackReenableCount { get; private set; }
public List<(int Width, int Height)> FramebufferResizes { get; } = [];
public bool PublishFramebufferSizeOnResize { get; set; }
public bool ResizeSucceeds { get; set; } = true;
public int ClientCloseRequestCount { get; private set; }
public List<string> Checkpoints { get; } = new();
public HashSet<string> ScreenshotRequests { get; } = new();
@ -90,6 +92,16 @@ public sealed class RetailUiAutomationProbeTests
public bool TryResizeFramebuffer(int width, int height, out string error)
{
FramebufferResizes.Add((width, height));
if (!ResizeSucceeds)
{
error = "resize rejected";
return false;
}
if (PublishFramebufferSizeOnResize)
{
FramebufferWidth = width;
FramebufferHeight = height;
}
error = string.Empty;
return true;
}
@ -1023,6 +1035,137 @@ public sealed class RetailUiAutomationProbeTests
}
}
[Fact]
public void ScriptRunner_synchronousResizeYieldsBeforeWaitAndFirstScreenshot()
{
var (root, _, _, _, objects) = RootWithTwoItemLists();
var runtime = new FakeRuntime { PublishFramebufferSizeOnResize = true };
var probe = new RetailUiAutomationProbe(root, objects);
string path = Path.Combine(
Path.GetTempPath(),
Path.GetRandomFileName() + ".ui-probe.txt");
File.WriteAllLines(path,
[
"resize 1024 768",
"wait framebuffer 1024 768 1000",
"screenshot first-resized-frame 1000",
]);
try
{
using var runner = new RetailUiAutomationScriptRunner(
probe,
path,
dumpOnStart: false,
runtime: runtime);
runner.Tick(0d);
Assert.Equal([(1024, 768)], runtime.FramebufferResizes);
Assert.Empty(runtime.ScreenshotRequests);
Assert.False(runner.Completed);
runner.Tick(0.001d);
Assert.Equal([(1024, 768)], runtime.FramebufferResizes);
Assert.Equal(["first-resized-frame"], runtime.ScreenshotRequests);
Assert.False(runner.Completed);
runtime.CompletedScreenshots.Add("first-resized-frame");
runner.Tick(0.001d);
Assert.True(runner.Completed);
}
finally
{
File.Delete(path);
}
}
[Fact]
public void ScriptRunner_sequentialSynchronousResizesYieldOnceEachWithoutRepeating()
{
var (root, _, _, _, objects) = RootWithTwoItemLists();
var runtime = new FakeRuntime { PublishFramebufferSizeOnResize = true };
var probe = new RetailUiAutomationProbe(root, objects);
string path = Path.Combine(
Path.GetTempPath(),
Path.GetRandomFileName() + ".ui-probe.txt");
File.WriteAllLines(path,
[
"resize 1024 768",
"wait framebuffer 1024 768 1000",
"resize 1600 900",
"wait framebuffer 1600 900 1000",
"screenshot second-resized-frame 1000",
]);
try
{
using var runner = new RetailUiAutomationScriptRunner(
probe,
path,
dumpOnStart: false,
runtime: runtime);
runner.Tick(0d);
Assert.Equal([(1024, 768)], runtime.FramebufferResizes);
Assert.Empty(runtime.ScreenshotRequests);
runner.Tick(0.001d);
Assert.Equal([(1024, 768), (1600, 900)], runtime.FramebufferResizes);
Assert.Empty(runtime.ScreenshotRequests);
runner.Tick(0.001d);
runner.Tick(0.001d);
Assert.Equal([(1024, 768), (1600, 900)], runtime.FramebufferResizes);
Assert.Equal(["second-resized-frame"], runtime.ScreenshotRequests);
Assert.False(runner.Completed);
}
finally
{
File.Delete(path);
}
}
[Fact]
public void ScriptRunner_failedResizeStopsWithoutYieldingIntoFollowingCommands()
{
var (root, _, _, _, objects) = RootWithTwoItemLists();
var runtime = new FakeRuntime { ResizeSucceeds = false };
var probe = new RetailUiAutomationProbe(root, objects);
var logs = new List<string>();
string path = Path.Combine(
Path.GetTempPath(),
Path.GetRandomFileName() + ".ui-probe.txt");
File.WriteAllLines(path,
[
"resize 1024 768",
"screenshot must-not-run 1000",
]);
try
{
using var runner = new RetailUiAutomationScriptRunner(
probe,
path,
dumpOnStart: false,
log: logs.Add,
runtime: runtime);
runner.Tick(0d);
runner.Tick(0.001d);
Assert.True(runner.Completed);
Assert.Equal([(1024, 768)], runtime.FramebufferResizes);
Assert.Empty(runtime.ScreenshotRequests);
Assert.Contains(logs, line => line.Contains("resize rejected", StringComparison.Ordinal));
}
finally
{
File.Delete(path);
}
}
[Fact]
public void ScriptRunner_closeClientRequestsNormalRuntimeShutdownExactlyOnce()
{