diff --git a/docs/reviews/2026-08-18-r3-test-truth-ledger.md b/docs/reviews/2026-08-18-r3-test-truth-ledger.md index e83bc159..0078e725 100644 --- a/docs/reviews/2026-08-18-r3-test-truth-ledger.md +++ b/docs/reviews/2026-08-18-r3-test-truth-ledger.md @@ -52,7 +52,7 @@ inventory refreshed for Batch L reports: | Mechanical output-only candidates | 2 | | Constant-truth assertion methods / sites | 0 / 0 | | Reviewed syntactic self-comparison methods / sites | 4 / 5 | -| Reviewed diagnostic methods | 57 | +| Reviewed diagnostic methods | 82 | | Methods directly using `Thread.Sleep` / `Task.Delay` | 15 / 14 | | Cancellable-infinite-only / elapsed-time methods | 5 / 24 | | Methods directly reading environment variables | 47 | @@ -68,10 +68,10 @@ the three platform branches; they are not missing-prerequisite passes. The output-only classifier follows same-file helper calls, so an assertion used only while constructing a probe can prevent an otherwise diagnostic method from appearing in its two-candidate result. The reviewed `Purpose=Diagnostic` set is -therefore the authority: Batch C's 51 methods plus six artifact/report writers -reconciled in Batch L, for 57 methods and 76 discovered cases (28 App, 48 Core). -Their investigative output and historical rationale remain executable outside -the release total. +therefore the authority: Batch C's 51 methods, six artifact/report writers from +Batch L, and 25 fixture-sanity-only investigations from Batch R, for 82 methods +and 103 discovered cases (35 App, 68 Core). Their investigative output and +historical rationale remain executable outside the release total. The five syntactic self-comparisons are reviewed, not vacuous: two call a hash twice to assert determinism, one reads the singleton twice, and two read the @@ -139,13 +139,13 @@ identifiable in the lane report. | T-008 incapable-of-failing diagnostics | resolved in batches A, C, and L | The literal wiring smoke test was deleted, the GPU contract tautology was repaired, and the six remaining constant-truth diagnostic assertions were removed after their output was preserved under `Purpose=Diagnostic`. | | T-009 wall-clock double-click tests | resolved in batch E | Four sleeps were replaced by a deterministic test clock behind an internal factory overload. The production factory still reads `Environment.TickCount64` exactly as before. | | T-010 two useless cases | high-confidence cleanup batch A | Delete `SmokeTest.TestProject_IsWired` and `ChaseCameraTests.ImplementsICamera`; compilation already proves both claims. | -| T-011 output-only methods | classified in batches C and L | The reviewed current set is 57 methods / 76 cases. All carry `Purpose=Diagnostic`, preserving the apparatus while removing it from release pass totals. Contract-shaped names remain explicitly flagged until a stable oracle exists. | +| T-011 diagnostic-only methods | resolved in batches C, L, and R | The reviewed current set is 82 methods / 103 cases. All carry `Purpose=Diagnostic`, preserving the apparatus while removing it from release pass totals. Batch R catches investigations whose only assertion validates fixture/DAT availability, which the original mechanical output-only scan could not distinguish from an oracle. | | T-012 source-text freezes | replacement map complete in batch I; 46 implementation-shape facts remain staged | Twelve whole-tree architecture rules and five cross-artifact contracts stay. The other 46 are campaign-era literal implementation freezes; retire each only beside its semantic/behavioral replacement, with user approval for owner/lifecycle assertions that have no equivalent yet. | | T-013 controller self-comparison | high-confidence cleanup batch A | Capture the first controller next to the first body and compare every retry with that reference. | | T-014 seven load-sensitive tests | six mechanisms repaired in batch F; one product defect classified | Virtual/wall-clock mixing, tiered-JIT allocation noise, a live controller clock, and a ThreadPool-start timing oracle were removed without weakening behavioral contracts. `DatSoundCache` #321 is a real in-flight-entry race and now runs as `Status=KnownFailure` pending a product fix. | | T-015 four non-prerequisite skips | resolved in batches A/B | PVS scaffold deleted with rationale preserved; redundant chat/radar generators deleted in favor of the comprehensive Manual lane; tower oracle is `Status=KnownFailure`. | -| T-016 historical test taxonomy | first descriptive-identity batch active in batch J | Remove opaque AP/R/J/K/Slice codes from current test/type names while preserving provenance in comments and ledgers. The 47 `Issue###` files remain an explicit user-decision set because their IDs still connect tests to retail evidence. | -| T-017 Avalonia ownership | reopened and closed in batch D | The full gate exposed the same compositor ownership class between six newer `MainWindowViewTests` facts. Their six named assertion phases now run in one owned Avalonia application session; 25 fresh-process stress iterations and the complete gate pass. | +| T-016 historical test taxonomy | descriptive identities repaired in batches J and R; issue-prefix decision remains | Opaque AP/R/J/K/Slice and `SCRATCH` labels have been removed from maintained regression identities while provenance stays in comments and ledgers. The 47 `Issue###` files remain an explicit user-decision set because their IDs still connect tests to retail evidence. | +| T-017 Avalonia ownership | batch D's window cleanup was necessary but incomplete; runner lifetime fixed in batch S | Batch R's clean gate reproduced a compositor thread-affinity cleanup failure. The sole combined Avalonia fact still used the package's default per-test application isolation. Batch S explicitly owns one application/dispatcher for the assembly's one UI fact; 25 focused processes, 25 complete Launcher assemblies, and the complete gate pass. | | T-018 stderr observer race | resolved in R2 | Live reader shares writes/deletes; 25 focused repetitions plus the complete gate. | | T-019 remaining explicit waits | resolved in batches M–O | All nine fixed-delay negative oracles now use observed monitor/thread/lease/process state. The remaining 24 elapsed-time methods are the reviewed cancellation, real-time protocol, cooperative-yield, bounded-polling, and positive-timeout categories from Batch H. | | T-020 exact duplicate bodies/data | resolved in batch K | Four redundant executions were removed. The remaining 11 body-equivalent groups are intentionally split theories with disjoint, meaningfully named datasets; the inventory enforces zero repeated rows within a theory or across body-equivalent theories. | @@ -793,8 +793,8 @@ cooperative yields, bounded integration polling, or a positive timeout guard. The 47 `Issue###` files contain 155 attributed methods. They are not a hidden duplicate or source-freeze family: none reads production source text and none -contains a direct timing wait. Twenty-nine methods are explicitly classified -`Purpose=Diagnostic`; the other 126 are executable regression contracts. +contains a direct timing wait. Forty-six methods are explicitly classified +`Purpose=Diagnostic`; the other 109 are executable regression contracts. Ninety-four methods belong to the installed-DAT lane, overlapping both groups where retail assets are the evidence prerequisite. @@ -807,8 +807,85 @@ contracts materially easier to understand. R3's recommendation is therefore to retain the current `Issue###` class/file prefixes. Continue correcting individual method names whose oracle overclaims, -and keep diagnostic methods outside the release count, but do not re-home 126 +and keep diagnostic methods outside the release count, but do not re-home 109 useful regressions merely to erase their provenance. If the issue archive is later split into component-owned records, a class-level `Issue` trait can preserve filtering before any component-based file rename. This recommendation remains a user approval item because it closes T-016 without code churn. + +## Batch R fixture-sanity-only diagnostics + +The original output-only scan intentionally treated any assertion as a failure +signal. A final name/comment review found 25 more investigation methods whose +only assertions establish that the DAT, fixture, setup, or synthetic replay +loaded; none compares the report, sweep, or characterization named by the +method. Their own names or comments say `Diagnostic`, `Dump`, `Measure`, +`Characterize`, “always passes,” or “not a regression.” + +The 25 methods (27 expanded cases) divide into: + +- seven visibility/flood measurement reports across corner flood, house exit, + issue #181, and issue #95; +- ten raw building/geometry dumps across issues #113, #119, #147, and #337; + and +- eight physics transition traces across issue #112, cellar ascent, door + collision, issue #108, and issue #265. + +Batch R adds only `Purpose=Diagnostic`. It does not remove a body, fixture, +assertion, output line, retail/ACE citation, or historical comment. The +diagnostic inventory is now 82 methods / 103 cases: 35 App and 68 Core. The +explicit non-Manual diagnostic filters pass 27/27 App and 68/68 Core; the other +eight App cases remain in their separately selected Manual tasks. + +The same pass corrected two inverse naming errors without changing their +oracles: + +- `SCRATCH_BuyShapedFreshGuidCreateObjectThenContainId_FinalOrder` is a durable + vendor-order regression and is now + `BuyShapedFreshGuidCreateThenPlacementZeroEcho_InsertsAtRetailListHead`; and +- `Diagnostic_ExitDoorTopEdge_GateVsAperture` already enforces hard pixel + bounds and is now + `ExitDoorTopEdge_GateCoversLiftedApertureWithinPixelTolerance`. + +Both focused regressions pass. Their detailed queue-order and doorway/scissor +rationale remains beside the tests. The only remaining unclassified method +names beginning `Reflect_` or `Dump` are real asserted collision/reflection or +environment-default contracts, not output apparatus. + +Only three of Batch R's 27 newly classified cases had still been hermetic; +the other 24 already belonged to the installed-DAT lane. After Batch S's +ownership correction, the no-retry complete hermetic Release gate therefore +passes 14,379/14,379 with zero skips or failures, exactly three below Batch O. + +## Batch S Avalonia application-session ownership + +The first clean complete gate after Batch R did not pass: every non-Launcher +assembly passed, but `MainWindowViewTests` failed during Avalonia test cleanup +with `DefaultRenderLoop.Add` calling `Dispatcher.VerifyAccess` from a thread +that did not own the compositor. The gate reported 14,378 passes and one +failure. This was recorded and fixed rather than retried unchanged. + +Batch D had closed and pumped every shown window and consolidated the six UI +assertion phases into one `[AvaloniaFact]`. That removed leaked-window overlap, +but it did not change Avalonia 12.1.1's default +`AvaloniaTestIsolationLevel.PerTest`: the runner still recreated and disposed +the application, dispatcher, and compositor around that one fact. The failure +occurred in that isolated application setup/cleanup path, before any product +assertion could explain it. + +`AcDream.Launcher.Tests` contains exactly one Avalonia fact, and that fact owns +the complete real-window matrix. Batch S declares +`AvaloniaTestIsolationLevel.PerAssembly`, so the runner creates one +application/dispatcher on its dedicated session thread and lets the fresh test +process own its terminal cleanup. All individual windows still close and pump +their dispatcher in `finally`; no suite serialization, retry, product code, or +assertion was changed. + +Verification: + +- the focused Avalonia fact passes in 25/25 fresh processes; +- the complete 56-case Launcher assembly passes in 25/25 fresh processes; +- the complete 44-project Release build reports zero warnings and zero errors; + and +- the no-retry complete hermetic Release gate passes 14,379/14,379 with zero + skips or failures across all 12 test assemblies. diff --git a/tests/AcDream.Launcher.Tests/TestAppBuilder.cs b/tests/AcDream.Launcher.Tests/TestAppBuilder.cs index 7fcfe0f4..4245f0e4 100644 --- a/tests/AcDream.Launcher.Tests/TestAppBuilder.cs +++ b/tests/AcDream.Launcher.Tests/TestAppBuilder.cs @@ -2,6 +2,7 @@ using Avalonia; using Avalonia.Headless; [assembly: AvaloniaTestApplication(typeof(AcDream.Launcher.Tests.TestAppBuilder))] +[assembly: AvaloniaTestIsolation(AvaloniaTestIsolationLevel.PerAssembly)] namespace AcDream.Launcher.Tests; @@ -12,6 +13,11 @@ namespace AcDream.Launcher.Tests; /// live for every test — 's controls (TreeView, /// Button, TextBox, ...) need a real control theme to template and focus /// correctly, not just an unstyled visual tree. +/// The assembly contains one Avalonia test that owns the complete window +/// matrix. Keep one application/dispatcher for that process: Avalonia 12's +/// per-test teardown can recreate the compositor after its render loop has +/// become owned by a different worker, producing a thread-affinity cleanup +/// failure after an otherwise passing test. /// public static class TestAppBuilder {