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 c51113f2..240a090c 100644
--- a/docs/reviews/2026-08-18-r3-test-truth-ledger.md
+++ b/docs/reviews/2026-08-18-r3-test-truth-ledger.md
@@ -133,18 +133,18 @@ identifiable in the lane report.
| T-002 physics tautology | high-confidence cleanup batch A | Compare old-model and new-model reflection decisions, as the surrounding contract states. |
| T-003 duplicate theory row | high-confidence cleanup batch A | Remove literal `0x41000012`, which is identical to `MotionCommand.Crouch`; remove the temporary analyzer suppression. |
| T-004 warning mismatches | resolved in R2 | Clean rebuild is zero-warning with repository-wide warnings-as-errors. |
-| T-005 dormant panel stack | production reachability and architecture intent reconciled in batch P; user taxonomy decision remains | No shipping code implements `IPanelRenderer`/`IPanelHost` or constructs `ChatPanel`, `DebugPanel`, or `VitalsPanel`, but the architecture explicitly preserves this first-party developer-panel contract for issue #258. Do not call its behavioral tests useless or delete the contract as dead code. Decide separately whether 26 fake-renderer self-tests belong in the release count. |
+| T-005 dormant panel stack | resolved in batches P and V | No shipping code implements `IPanelRenderer`/`IPanelHost` or constructs `ChatPanel`, `DebugPanel`, or `VitalsPanel`, but the architecture explicitly preserves this first-party developer-panel contract for issue #258. Batch V removes exactly 26 fake-renderer self-tests and two compiler-generated `PanelContext` record tests while retaining `FakePanelRenderer` and all 23 executable panel behavior contracts. |
| T-006 misleading installed-DAT reason | resolved in batch B | Shared opt-in is now `ACDREAM_RUN_INSTALLED_DAT_TESTS=1` (legacy switch retained), the reason names the lane, and all nine owners carry `Lane=InstalledDat`. |
| T-007 271 silent passing gates | resolved in batch G | All 280 directly found gates plus three later-reconciled sites now fail explicitly when their selected lane lacks its prerequisite. The only three syntax candidates left are reviewed cross-platform branch false positives. |
| 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 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 | direct-read map completed in batch I and helper-mediated gap corrected in batch U; 85 implementation/test-model facts remain staged | Seventeen whole-tree architecture rules and five cross-artifact contracts stay. The other 85 are literal implementation or test-model freezes; retire each only beside its semantic/behavioral replacement, with user approval for owner/lifecycle assertions that have no equivalent yet. |
+| T-012 source-text freezes | direct-read map completed in batch I, helper-mediated gap corrected in batch U, and staged replacement approved | Seventeen whole-tree architecture rules and five cross-artifact contracts stay. The other 85 are literal implementation or test-model freezes; approval authorizes retiring each only beside its semantic/behavioral replacement. |
| 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 | 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-016 historical test taxonomy | resolved in batches J, Q, R, and T | Opaque AP/R/J/K/Slice and `SCRATCH` labels have been removed from maintained regression identities while provenance stays in comments and ledgers. The user approved retaining the 47 descriptive `Issue###` files because their IDs connect executable contracts 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. |
@@ -578,8 +578,8 @@ The dormant presentation-only subset currently contains 51 attributed methods
| Subset | Methods | Disposition |
|---|---:|---|
-| `IPanelRendererWidgetTests` and `IPanelRendererMainMenuBarTests` | 26 | They exercise only `FakePanelRenderer`, not a production backend. Treat as test-harness/contract self-tests; user decision whether to retain outside the release total, consolidate, or remove. |
-| `PanelContextTests` | 2 | Small public-contract value, but only compiler-generated record round-trip/equality. Same taxonomy decision as the renderer self-tests. |
+| `IPanelRendererWidgetTests` and `IPanelRendererMainMenuBarTests` | 26 | They exercise only `FakePanelRenderer`, not a production backend. Approved for removal in Batch V. |
+| `PanelContextTests` | 2 | Only compiler-generated record round-trip/equality. Approved for removal in Batch V. |
| `ChatPanel` layout, input, focus, and colored-render behavior | 23 | Meaningful executable behavior for the explicitly preserved future developer-panel contract. Retain unless that architecture promise is intentionally retired. |
The earlier audit's recommendation to remove the whole stack was therefore too
@@ -849,8 +849,8 @@ prefixes. Continue correcting individual method names whose oracle overclaims,
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.
+preserve filtering before any component-based file rename. The user approved
+this recommendation before Batch V, closing T-016 without code churn.
## Batch R fixture-sanity-only diagnostics
@@ -998,3 +998,32 @@ Verification:
and
- the no-retry complete hermetic Release gate passes 14,379/14,379 with zero
skips or failures across all 12 test assemblies.
+
+## Batch V dormant panel self-test removal
+
+The user approved Batch P's narrow recommendation. Batch V deletes exactly the
+three self-test files in the approved boundary:
+
+- `IPanelRendererWidgetTests.cs`: 17 tests whose oracle is the behavior of the
+ test-only `FakePanelRenderer` recorder;
+- `IPanelRendererMainMenuBarTests.cs`: nine more tests of that recorder; and
+- `PanelContextTests.cs`: two tests of compiler-generated positional-record
+ assignment and equality.
+
+The useful boundary remains intact. `IPanel`, `IPanelRenderer`, `IPanelHost`,
+and `PanelContext` remain public production contracts for issue #258;
+`FakePanelRenderer.cs` remains as the observation seam for 22 `ChatPanel*`
+methods plus the `ChatVMCombatTests` render-path method; and all 23 layout,
+input, focus, color, and command behavior tests remain executable. No panel,
+ViewModel, parser, command contract, product source, or historical rationale is
+removed.
+
+Verification:
+
+- the retained 23 attributed panel behavior methods pass all 29 expanded cases;
+- the complete UI-abstractions assembly passes 898/898, exactly 28 below its
+ previous 926-case total;
+- the complete 44-project Release build reports zero warnings and zero errors;
+ and
+- the no-retry complete hermetic Release gate passes 14,351/14,351 with zero
+ skips or failures across all 12 test assemblies, exactly 28 below Batch U.
diff --git a/tests/AcDream.UI.Abstractions.Tests/IPanelRendererMainMenuBarTests.cs b/tests/AcDream.UI.Abstractions.Tests/IPanelRendererMainMenuBarTests.cs
deleted file mode 100644
index 807bdce1..00000000
--- a/tests/AcDream.UI.Abstractions.Tests/IPanelRendererMainMenuBarTests.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-namespace AcDream.UI.Abstractions.Tests;
-
-///
-/// K.3: gains a top-of-screen menu-bar
-/// surface for global navigation. Same shape as ImGui's main menu bar:
-/// open with BeginMainMenuBar, drop top-level menus with
-/// BeginMenu, drop clickable items with MenuItem (which
-/// returns true on the frame the user clicks).
-///
-public sealed class IPanelRendererMainMenuBarTests
-{
- [Fact]
- public void BeginMainMenuBar_records_call_and_returns_default_true()
- {
- var r = new FakePanelRenderer();
- bool result = r.BeginMainMenuBar();
- Assert.True(result);
- Assert.Contains(r.Calls, c => c.Method == "BeginMainMenuBar");
- }
-
- [Fact]
- public void BeginMainMenuBar_returns_override_when_set()
- {
- var r = new FakePanelRenderer { MainMenuBarReturns = false };
- Assert.False(r.BeginMainMenuBar());
- }
-
- [Fact]
- public void EndMainMenuBar_records_call()
- {
- var r = new FakePanelRenderer();
- r.EndMainMenuBar();
- Assert.Contains(r.Calls, c => c.Method == "EndMainMenuBar");
- }
-
- [Fact]
- public void BeginMenu_records_label_and_returns_default_true()
- {
- var r = new FakePanelRenderer();
- bool result = r.BeginMenu("View");
- Assert.True(result);
- var call = Assert.Single(r.Calls, c => c.Method == "BeginMenu");
- Assert.Equal("View", call.Args[0]);
- }
-
- [Fact]
- public void BeginMenu_returns_override_when_set()
- {
- var r = new FakePanelRenderer { MenuReturns = false };
- Assert.False(r.BeginMenu("View"));
- }
-
- [Fact]
- public void EndMenu_records_call()
- {
- var r = new FakePanelRenderer();
- r.EndMenu();
- Assert.Contains(r.Calls, c => c.Method == "EndMenu");
- }
-
- [Fact]
- public void MenuItem_records_label_and_shortcut()
- {
- var r = new FakePanelRenderer();
- r.MenuItem("Settings", "F11");
- var call = Assert.Single(r.Calls, c => c.Method == "MenuItem");
- Assert.Equal("Settings", call.Args[0]);
- Assert.Equal("F11", call.Args[1]);
- }
-
- [Fact]
- public void MenuItem_returns_override_when_set()
- {
- var r = new FakePanelRenderer { MenuItemReturns = true };
- Assert.True(r.MenuItem("Settings"));
- }
-
- [Fact]
- public void MenuItem_default_returns_false_unsclicked()
- {
- var r = new FakePanelRenderer();
- Assert.False(r.MenuItem("Settings"));
- }
-}
diff --git a/tests/AcDream.UI.Abstractions.Tests/IPanelRendererWidgetTests.cs b/tests/AcDream.UI.Abstractions.Tests/IPanelRendererWidgetTests.cs
deleted file mode 100644
index 78315bdb..00000000
--- a/tests/AcDream.UI.Abstractions.Tests/IPanelRendererWidgetTests.cs
+++ /dev/null
@@ -1,308 +0,0 @@
-using System.Numerics;
-
-namespace AcDream.UI.Abstractions.Tests;
-
-///
-/// Tests the shape of the new Phase I.1 widget extensions on
-/// . Every test dispatches through the
-/// interface (not the concrete fake) so the test only compiles when
-/// the interface actually has the method — that's the RED signal for
-/// TDD when adding new widgets in the future.
-///
-public sealed class IPanelRendererWidgetTests
-{
- private static (IPanelRenderer Iface, FakePanelRenderer Fake) New()
- {
- var f = new FakePanelRenderer();
- return (f, f);
- }
-
- [Fact]
- public void TextColored_RecordsCallWithColorAndText()
- {
- var (r, fake) = New();
- var color = new Vector4(0.9f, 0.4f, 0.2f, 1f);
-
- r.TextColored(color, "ouch");
-
- var call = Assert.Single(fake.Calls);
- Assert.Equal("TextColored", call.Method);
- Assert.Equal(color, call.Args[0]);
- Assert.Equal("ouch", call.Args[1]);
- }
-
- [Fact]
- public void CollapsingHeader_RecordsLabelAndDefaultOpen_ReturnsBackingValue()
- {
- var (r, fake) = New();
- fake.CollapsingHeaderNextReturn = false;
-
- bool open = r.CollapsingHeader("Player Info");
- Assert.False(open);
-
- var call = Assert.Single(fake.Calls);
- Assert.Equal("CollapsingHeader", call.Method);
- Assert.Equal("Player Info", call.Args[0]);
- Assert.Equal(true, call.Args[1]); // default
- }
-
- [Fact]
- public void CollapsingHeader_PassesDefaultOpenFalse()
- {
- var (r, fake) = New();
-
- r.CollapsingHeader("Diagnostics", defaultOpen: false);
-
- var call = Assert.Single(fake.Calls);
- Assert.Equal(false, call.Args[1]);
- }
-
- [Fact]
- public void TreeNode_AndTreePop_FormPair()
- {
- var (r, fake) = New();
- fake.TreeNodeNextReturn = true;
-
- if (r.TreeNode("Inventory"))
- {
- r.Text("sword");
- r.TreePop();
- }
-
- Assert.Equal(3, fake.Calls.Count);
- Assert.Equal("TreeNode", fake.Calls[0].Method);
- Assert.Equal("Inventory", fake.Calls[0].Args[0]);
- Assert.Equal("Text", fake.Calls[1].Method);
- Assert.Equal("TreePop", fake.Calls[2].Method);
- }
-
- [Fact]
- public void Checkbox_AppliesNextValueToRefAndRecordsBefore()
- {
- var (r, fake) = New();
- fake.CheckboxNextReturn = true;
- fake.CheckboxNextValue = true;
-
- bool flag = false;
- bool changed = r.Checkbox("DumpMotion", ref flag);
-
- Assert.True(changed);
- Assert.True(flag); // fake mutated the ref
- var call = Assert.Single(fake.Calls);
- Assert.Equal("Checkbox", call.Method);
- Assert.Equal("DumpMotion", call.Args[0]);
- Assert.Equal(false, call.Args[1]); // value-before-the-call captured in trace
- }
-
- [Fact]
- public void Button_ReturnsBackingValueAndRecordsLabel()
- {
- var (r, fake) = New();
- fake.ButtonNextReturn = true;
-
- bool clicked = r.Button("Cycle Weather");
-
- Assert.True(clicked);
- var call = Assert.Single(fake.Calls);
- Assert.Equal("Button", call.Method);
- Assert.Equal("Cycle Weather", call.Args[0]);
- }
-
- [Fact]
- public void Combo_AppliesNextSelectedIndex_AndReturnsChanged()
- {
- var (r, fake) = New();
- fake.ComboNextReturn = true;
- fake.ComboNextSelectedIndex = 2;
-
- int idx = 0;
- var items = new[] { "Dawn", "Noon", "Dusk", "Night" };
- bool changed = r.Combo("Time", ref idx, items);
-
- Assert.True(changed);
- Assert.Equal(2, idx);
- var call = Assert.Single(fake.Calls);
- Assert.Equal("Combo", call.Method);
- Assert.Equal("Time", call.Args[0]);
- Assert.Equal(0, call.Args[1]);
- Assert.Same(items, call.Args[2]);
- }
-
- [Fact]
- public void SliderFloat_AppliesNextValueAndReturnsChanged()
- {
- var (r, fake) = New();
- fake.SliderFloatNextReturn = true;
- fake.SliderFloatNextValue = 0.7f;
-
- float v = 0.1f;
- bool changed = r.SliderFloat("Sensitivity", ref v, 0f, 1f);
-
- Assert.True(changed);
- Assert.Equal(0.7f, v);
- var call = Assert.Single(fake.Calls);
- Assert.Equal("SliderFloat", call.Method);
- Assert.Equal("Sensitivity", call.Args[0]);
- Assert.Equal(0.1f, call.Args[1]);
- Assert.Equal(0f, call.Args[2]);
- Assert.Equal(1f, call.Args[3]);
- }
-
- [Fact]
- public void PlotLines_RecordsAllOptionalArgs()
- {
- var (r, fake) = New();
- var values = new[] { 16f, 17f, 16.5f, 16.2f };
-
- r.PlotLines(
- "FrameMs",
- values,
- count: values.Length,
- offset: 1,
- overlay: "60 fps",
- min: 0f,
- max: 33f,
- size: new Vector2(120, 40));
-
- var call = Assert.Single(fake.Calls);
- Assert.Equal("PlotLines", call.Method);
- Assert.Equal("FrameMs", call.Args[0]);
- Assert.Same(values, call.Args[1]);
- Assert.Equal(values.Length, call.Args[2]);
- Assert.Equal(1, call.Args[3]);
- Assert.Equal("60 fps", call.Args[4]);
- Assert.Equal(0f, call.Args[5]);
- Assert.Equal(33f, call.Args[6]);
- Assert.Equal(new Vector2(120, 40), call.Args[7]);
- }
-
- [Fact]
- public void PlotLines_DefaultsApplied_WhenOptionalsOmitted()
- {
- var (r, fake) = New();
- var values = new[] { 1f, 2f, 3f };
-
- r.PlotLines("FPS", values, count: values.Length);
-
- var call = Assert.Single(fake.Calls);
- Assert.Equal(0, call.Args[3]); // offset default
- Assert.Null(call.Args[4]); // overlay default
- Assert.Null(call.Args[5]); // min default
- Assert.Null(call.Args[6]); // max default
- Assert.Null(call.Args[7]); // size default
- }
-
- [Fact]
- public void Table_BeginNextColumnEnd_FormSequence()
- {
- var (r, fake) = New();
-
- r.BeginTable("Keybinds", 2);
- r.TableNextColumn();
- r.Text("F1");
- r.TableNextColumn();
- r.Text("Toggle Info");
- r.EndTable();
-
- Assert.Equal(6, fake.Calls.Count);
- Assert.Equal("BeginTable", fake.Calls[0].Method);
- Assert.Equal("Keybinds", fake.Calls[0].Args[0]);
- Assert.Equal(2, fake.Calls[0].Args[1]);
- Assert.Equal("TableNextColumn", fake.Calls[1].Method);
- Assert.Equal("Text", fake.Calls[2].Method);
- Assert.Equal("TableNextColumn", fake.Calls[3].Method);
- Assert.Equal("Text", fake.Calls[4].Method);
- Assert.Equal("EndTable", fake.Calls[5].Method);
- }
-
- [Fact]
- public void InputTextSubmit_NoSubmit_ReturnsFalseAndSubmittedNull()
- {
- var (r, fake) = New();
- // InputTextSubmitNextSubmitted left null → the buffer should be untouched
- // and the out arg should be null.
-
- string buf = "hel";
- bool submitted = r.InputTextSubmit("##chat", ref buf, 256, out var output);
-
- Assert.False(submitted);
- Assert.Null(output);
- Assert.Equal("hel", buf);
-
- var call = Assert.Single(fake.Calls);
- Assert.Equal("InputTextSubmit", call.Method);
- Assert.Equal("##chat", call.Args[0]);
- Assert.Equal("hel", call.Args[1]);
- Assert.Equal(256, call.Args[2]);
- }
-
- [Fact]
- public void InputTextSubmit_OnSubmit_OutputsTextAndClearsBuffer()
- {
- var (r, fake) = New();
- fake.InputTextSubmitNextSubmitted = "hello world";
- fake.InputTextSubmitNextBufferAfter = string.Empty;
-
- string buf = "hello world";
- bool submitted = r.InputTextSubmit("##chat", ref buf, 256, out var output);
-
- Assert.True(submitted);
- Assert.Equal("hello world", output);
- // Contract: on submit the impl clears the buffer for the next frame.
- Assert.Equal(string.Empty, buf);
- }
-
- [Fact]
- public void Spacing_RecordsCall()
- {
- var (r, fake) = New();
- r.Spacing();
- var call = Assert.Single(fake.Calls);
- Assert.Equal("Spacing", call.Method);
- }
-
- [Fact]
- public void Dummy_RecordsSize()
- {
- var (r, fake) = New();
- r.Dummy(new Vector2(8, 12));
- var call = Assert.Single(fake.Calls);
- Assert.Equal("Dummy", call.Method);
- Assert.Equal(new Vector2(8, 12), call.Args[0]);
- }
-
- [Fact]
- public void TextWrapped_RecordsText()
- {
- var (r, fake) = New();
- r.TextWrapped("a long description that wraps onto multiple lines");
-
- var call = Assert.Single(fake.Calls);
- Assert.Equal("TextWrapped", call.Method);
- Assert.Equal("a long description that wraps onto multiple lines", call.Args[0]);
- }
-
- // -- Sanity: existing widgets still recorded correctly via the fake -------
-
- [Fact]
- public void BeginEndTextSeparatorProgressBar_StillRecorded()
- {
- var (r, fake) = New();
- fake.BeginReturns = true;
-
- Assert.True(r.Begin("Window"));
- r.Text("hi");
- r.SameLine();
- r.Separator();
- r.ProgressBar(0.5f, 100f, "50%");
- r.End();
-
- Assert.Equal(6, fake.Calls.Count);
- Assert.Equal("Begin", fake.Calls[0].Method);
- Assert.Equal("Text", fake.Calls[1].Method);
- Assert.Equal("SameLine", fake.Calls[2].Method);
- Assert.Equal("Separator", fake.Calls[3].Method);
- Assert.Equal("ProgressBar", fake.Calls[4].Method);
- Assert.Equal("End", fake.Calls[5].Method);
- }
-}
diff --git a/tests/AcDream.UI.Abstractions.Tests/PanelContextTests.cs b/tests/AcDream.UI.Abstractions.Tests/PanelContextTests.cs
deleted file mode 100644
index 2a665c93..00000000
--- a/tests/AcDream.UI.Abstractions.Tests/PanelContextTests.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace AcDream.UI.Abstractions.Tests;
-
-public sealed class PanelContextTests
-{
- [Fact]
- public void Fields_RoundTripThroughConstructor()
- {
- var ctx = new PanelContext(DeltaSeconds: 0.016f, Commands: NullCommandBus.Instance);
-
- Assert.Equal(0.016f, ctx.DeltaSeconds);
- Assert.Same(NullCommandBus.Instance, ctx.Commands);
- }
-
- [Fact]
- public void RecordEquality_ByValue()
- {
- var a = new PanelContext(1f / 60f, NullCommandBus.Instance);
- var b = new PanelContext(1f / 60f, NullCommandBus.Instance);
-
- // Record-struct equality is value-based on DeltaSeconds + reference-based
- // on Commands (since ICommandBus is a reference type, same instance → equal).
- Assert.Equal(a, b);
- }
-}