✦ SPIELWELTENMoto Racer - Rampage ↗

Concurrent QA report publication

Evidence cutoff: 15 September 2026, 15:41 Vienna. The real rendered memory run 20260915-152717-820-memory stopped after nine loads with an IOException in CampaignMemoryQA.Save / File.Replace. Its final report and invocation remain unchanged. It exited 1, had no reported game errors or display-driver events, and did not complete the required 21 loads. It establishes neither a new memory leak nor a memory plateau pass.

Cause and ownership

The exception says that the file being replaced cannot be removed. Windows reports that wording for native error 1175; the complete originating HResult was not serialized in the failed report. A real temporary-file regression reproduced replacement failure with native error 32 by keeping the destination open without FileShare.Delete. This demonstrates the conflicting sharing contract, but does not identify the external reader in the game run or establish an antivirus cause.

The actual Tools/memory-test.ps1 does not poll partial JSON. During play it polls the player log using the existing delete-sharing log reader, then reads the final report after process exit. An external status observer can nevertheless conflict with the player's partial-report replacement if it uses Get-Content or File.ReadAllText.

QAReportFile.cs is now shared directly by the Unity QA writer and the PowerShell reader/runner. It has no Unity dependency. Readers open with FileShare.ReadWrite | FileShare.Delete, consume one complete file generation, then dispose their handles. The writer serializes to a temporary sibling and replaces the destination; it never truncates or deletes the published report as a fallback.

The native threaded stress test additionally observed very short file-not-found/sharing windows during replacement. Readers therefore retry only native codes 2/32/33, with five waits totalling 62ms before propagating the exception. Writers retry only sharing/lock/replace-conflict codes 32/33/1175, with four waits totalling 150ms, and only while both complete destination and temporary source still exist. Scheduling can add wall-clock overhead to those waits. Runner callbacks and player warning logs expose retries. Malformed JSON, permanent missing reports and persistent publication failures remain errors; no partial report can pass the existing runner assertions. If final player report publication fails, the player logs the full failure and exits 1 rather than remaining open without a report.

Use the shared reader for live observation:

. ./Tools/qa-report-io.ps1
Read-QaReport -Path 'Docs/QA/<active-run>/campaign-memory-partial.json'

Actual regression

Tools/memory-runner-test.ps1 passed 97/97 at 13:41:45 UTC: report. It keeps all previous pass/fail, profile, native-log and ownership assertions. Additional tests compile and execute the actual QAReportFile.cs with QAReportFileVerification.cs, using real Windows handles and threads:

The first expanded regression lacked the reader transition retry and failed 95/96 assertions. Its report is preserved as initial I/O regression; it records the actual concurrent not-found/sharing exceptions. Correcting that newly observed reader race did not relax memory criteria.

The initial full source compile passed 89 Runtime + 19 Editor files, zero warnings/errors. No Unity or player process was started by these tests. A new built-player 21-load memory run is still required; previous content/build memory passes remain historical evidence.

Campaign reader/writer follow-up

The same publication mechanism now covers CampaignQA.Write and WriteTrace, plus both campaign-runner invocation writes. All live campaign-report polling and final report reads in Tools/campaign-test.ps1 use Read-QaReport. Flow mode does not poll its once-published final report while the player is running; it is read only after exit, so no unrelated Flow writer refactor was required. A final trace publication failure is recorded before profile restoration; a final summary publication failure is logged and produces a nonzero player exit. The existing failed 15:39 campaign series is unchanged.

The campaign starter now validates arguments before build/process/output-directory setup. Albedo diagnosis requires one stage, one pass, rendered captures and no Flow mode. Any series containing campaign-race passes requires consecutive stage IDs in actual catalog order, including chapter boundaries. The order is read from the source catalog with shape/duplicate guards; unknown or wrongly cased IDs are rejected. Separate single-stage fixtures remain valid, including Canyon02. A single solo-only pass can still sample disconnected stages, but multiple passes alternate solo/campaign even with -Solo, so that switch cannot bypass the campaign ordering rule.

The existing runner guard suite now passes 114/114: latest combined report. It contains the previous Windows concurrent-I/O regression and 17 actual campaign catalog/argument checks, including all three invalid Albedo combinations, the original disconnected Jungle/Canyon request, valid single Canyon, valid cross-chapter succession, duplicates/reversal/unknown IDs, and the alternate-pass Solo case. Functions are extracted from the real campaign script without executing its launch body. Latest managed compilation passed 90 Runtime + 20 Editor files, zero warnings/errors. These are source/OS-I/O checks; the ongoing older built player's diagnostic outcome is not altered or claimed by this change.