From ddbd7e409680220651c241a678b9a848888e0f71 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 25 Aug 2026 14:15:26 +0200 Subject: [PATCH] =?UTF-8?q?docs+fix(ui):=20Campaign=20AS=20CLOSED=20?= =?UTF-8?q?=E2=80=94=20connected=20gate=20PASSED;=20AS-GF1=20probes=20stri?= =?UTF-8?q?pped;=20#443=20narrowed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The owner ran the Campaign AS connected gate live and passed it. The two gate findings resolved in-round: the extras-list "black rectangle" is retail's own authored scroll-less clipped listbox (no scrollbar authored on 0x10000335, verified against the live DAT; wheel-scroll/resize reveal rows — AS-GF1 65f6f584 ruled it not a code defect), and the paperdoll symptom narrowed from "renders nothing" to an intermittent FIRST-OPEN DELAY: the probe round proved the private render layer healthy from the first frames (nonzero handle, 34 MeshRefs, sane bounds/camera) for both the examination clone and the inventory doll, with mesh residency/upload latency the leading suspect. #443 stays open with that narrowed shape. Per the probe-dies-with-its-investigation rule this strips CreatureAppraisalViewportDiagnostics, its call sites, and the launch-options row in one commit (recoverable via git show 65f6f584). App hermetic suite green (6,337/0). Co-Authored-By: Claude Fable 5 --- docs/ISSUES.md | 24 +++++++-- docs/launch-options.md | 1 - ...026-08-25-assess-window-parity-campaign.md | 17 ++++-- .../CreatureAppraisalPresentation.cs | 29 +---------- .../CreatureAppraisalViewportDiagnostics.cs | 52 ------------------- 5 files changed, 33 insertions(+), 90 deletions(-) delete mode 100644 src/AcDream.App/Rendering/CreatureAppraisalViewportDiagnostics.cs diff --git a/docs/ISSUES.md b/docs/ISSUES.md index 78208f5f..fb3beb76 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -24,11 +24,27 @@ What does NOT go here: - Every session: scan OPEN issues at start; promote/close anything we touched during the session before ending. - Promoting to a Phase: mark as `DONE (promoted to Phase X)` + commit SHA where the Phase entry landed. -## #443 — Examination window: player-target paperdoll viewport renders nothing (AS-GF1) +## #443 — Examination/paperdoll private viewport: doll appears only after a delay on first open (was: "renders nothing") -**Status:** OPEN — probe added, root cause NOT isolated. -**Component:** examination window / creature-appraisal private viewport. -**Filed:** 2026-08-25, AS-GF1 gate-fix session. +**Status:** OPEN (narrowed at the gate — intermittent first-open DELAY, not +a hard break; NOT gate-blocking, owner passed the Campaign AS gate with it). +**Component:** private entity viewports (examination clone, inventory +paperdoll — shared `PrivateEntityViewportRenderer`). +**Filed:** 2026-08-25, AS-GF1 gate-fix session. **Narrowed same day at the +gate's probe round:** with `ACDREAM_PROBE_CREATURE_APPRAISAL_VIEWPORT=1` +(probe since DELETED per the probe-dies rule — recover it with +`git show 65f6f584` if this recurs) the live session showed the render +layer HEALTHY from the first probed frames — nonzero texture-table handle, +34 MeshRefs, sane bounds and camera eye, for both the examination clone +and the inventory paperdoll — while the owner initially saw an empty +(black) pane that later popped in ("I can see the paper doll now, after a +while"), and a subsequent fresh session showed it promptly. Leading +suspect: private-clone MESH residency/upload latency in the shared arena +(the pass records draws only for resident meshes; the probe cannot see +residency). The gate's OTHER symptom (the "black rectangle" band over the +extras rows at small window heights) is the authored scroll-less +clipped-list behavior AS-GF1 ruled retail-correct below, plus the clip +line moving with resize — owner-accepted at the gate. Owner report at the Campaign AS connected gate: the animated 3-D paperdoll in the examination window (LayoutDesc `0x2100006B` element `0x10000148`) diff --git a/docs/launch-options.md b/docs/launch-options.md index 02f97a89..5c1d0e3c 100644 --- a/docs/launch-options.md +++ b/docs/launch-options.md @@ -181,7 +181,6 @@ fall back to `ACDREAM_DAT_DIR`. | `ACDREAM_ORBIT_DISTANCE_METERS` | `=`, must be finite and `>0` | Diagnostic-only initial distance for the offline orbit camera, so deterministic renderer acceptance captures land inside a finite shadow reach. | Own doc comment: "used by deterministic renderer acceptance captures." Rejects non-finite/non-positive values silently (parses to `null`, camera default used). | `null` (unset) → normal camera default | `RuntimeOptions.InitialOrbitDistanceMeters` → `GameWindow.cs:1412` → offline orbit-camera composition | | `ACDREAM_ORBIT_PITCH_DEGREES` | `=`, clamped `[-89, 89]` | Diagnostic-only initial orbit camera elevation. | Values outside `[-89,89]` or non-finite are silently rejected (→ `null`, default kept) rather than clamped. | `null` | `RuntimeOptions.InitialOrbitPitchDegrees` → `GameWindow.cs:1414` | | `ACDREAM_ORBIT_YAW_DEGREES` | `=`, must be finite | Diagnostic-only initial orbit camera heading. | Non-finite values silently rejected (→ `null`). | `null` | `RuntimeOptions.InitialOrbitYawDegrees` → `GameWindow.cs:1413` | -| `ACDREAM_PROBE_CREATURE_APPRAISAL_VIEWPORT` | `=1` | #443 temporary probe for the examination-window player-paperdoll regression: logs `[AS-GF1-PROBE] TryGetVisibleTarget: ` and `[AS-GF1-PROBE] TrySynchronize: ` on every REASON TRANSITION (not every frame), pinpointing which of the two private-viewport gates rejects the target | print-only, state-change-gated so cost stays near zero even left on for a whole session | off | `CreatureAppraisalViewportDiagnostics.Enabled` (`CreatureAppraisalViewportDiagnostics.cs`), consumed by `RetailCreatureAppraisalFrameView.TryGetVisibleTarget` and `RetailCreatureAppraisalCloneFactory.TrySynchronize` in `CreatureAppraisalPresentation.cs` | | `ACDREAM_PROBE_REVEAL_RADIUS` | `==1` (unparsable or `<1` → override absent; floor is 1, not 0) | #280 A/B measurement probe: forces the OUTDOOR reveal gate to use this landblock radius instead of the derived streaming window (near radius clamped to it), so a route can be measured with the pre-#280 behavior (`=1`, old `OutdoorNeighborhoodRadius`) vs. current | **Changes what gets revealed, not just measured** — genuinely resizes the reveal/visible window used by the live reveal gate. CLAUDE.md: "Leave it unset for any measurement or gate run — with it set you are measuring a different window than production." `=0` is rejected by the parser specifically because it would hang the very A/B route it exists to measure (`RequiredRenderRadius==0` fails `invalid-readiness-shape`). Not a user setting, not in Settings/RuntimeOptions, not persisted. | unset (derivation in charge, no override) | `StreamingDiagnostics.RevealRadiusOverride` (`StreamingDiagnostics.cs:25-27,76-80`), applied by `StreamingDiagnostics.ApplyRevealRadiusOverride` | | `ACDREAM_PROBE_WORLD_FRAME` | `=1` | gates one `[world-frame] agree` line per projected conversion in `DatLiveEntityProjectionMaterializer`, recording the world-frame center both `LiveWorldOriginState` (App) and Runtime's physics-state owner used (issue #283, "measurement only; it never gates placement") | print-only | off | `PhysicsDiagnostics.ProbeWorldFrameEnabled` | | `ACDREAM_SKY_PHASE_SECONDS` | `=` (any finite value; negative accepted, taken mod 1 per axis) | Campaign V slice V7 instrument-determinism pin: freezes the sky's cloud-sheet UV scroll to a fixed elapsed-seconds value instead of wall-clock time, so two launches of a differential/offline gate agree about cloud position. | **Non-obvious dual effect**: this ONE var pins TWO independently-designed clocks that happen to share a name-adjacent purpose — the sky renderer's cloud scroll (`SkyRenderer.AnimationPhaseSecondsOverride`) AND, since Campaign VM slice VM6, the atmospheric post-process graph's foliage-wind clock (`_windClockSecondsOverride`). A gate that only knows about "sky clouds" and sets this to freeze them will *also* freeze foliage-wind evolution — deliberately snapped-to-target on the first advance per an A6 review fix, but still a second surface a naive reader wouldn't expect this var to touch. Distinct from `ACDREAM_DAY_GROUP`/`ACDREAM_WORLD_TIME`, which pin the OTHER sky clock (day group/sun angle) — retail's clouds drift independently of the calendar date by design. | `null` → wall-clock driven (every ordinary run) | `RuntimeOptions.SkyAnimationPhaseSeconds` → `SkyRenderer.cs:79,85` (cloud UV scroll) **and** `AtmosphericPostProcessGraph.cs:560,586,671` (foliage-wind clock) | diff --git a/docs/plans/2026-08-25-assess-window-parity-campaign.md b/docs/plans/2026-08-25-assess-window-parity-campaign.md index 8818d348..9ee79da7 100644 --- a/docs/plans/2026-08-25-assess-window-parity-campaign.md +++ b/docs/plans/2026-08-25-assess-window-parity-campaign.md @@ -1,9 +1,16 @@ # Campaign AS — assess/examination window retail parity (player targets) -**Status: IMPLEMENTATION COMPLETE 2026-08-25 — AS1–AS5 REVIEW-CLOSED, AS6 -gate script written (`docs/research/2026-08-25-campaign-as-test-script.md`). -ONLY the owner-driven connected gate remains. Branch NOT pushed — the owner -pushes on their word.** +**Status: CLOSED — CONNECTED GATE PASSED 2026-08-25 ("fixed! gate pass!").** +AS1–AS5 review-closed; the gate round harvested two findings, both resolved +in-round: the extras-list "black rectangle" is retail's own authored +scroll-less clipped listbox (no scrollbar authored on 0x10000335 — verified +against the live DAT; wheel-scroll and resize reveal rows; AS-GF1 +`65f6f584` ruled it not-a-code-defect), and the paperdoll's absence +narrowed to an intermittent FIRST-OPEN DELAY (#443, kept open) after the +probe round proved the render layer healthy — the render pipeline was +never broken by this campaign. Gate probes deleted at close per the +probe-dies rule (recoverable via `git show 65f6f584`). Branch NOT pushed — +the owner pushes on their word. Owner report (2026-08-25, side-by-side screenshots, acdream vs retail, both assessing the player "Dww"): acdream's examination window on a PLAYER target @@ -147,4 +154,4 @@ round → narrow re-review → REVIEW-CLOSED. | AS3 | **REVIEW-CLOSED 2026-08-25** | `1616cd3d` (no fix round) | APPROVE first pass — trio/legend/monster-path exact by offset-level decomp verification; R3 flattening theory disproven at source; ratings adjudication: pre-AS3 code already retail-exact. 5 NITs: 12 (legend-order comment), 11 (stronger refresh test) fold into AS4; 14/15 done in the close commit; 13 (geometry-keyed test helper) noted | | AS4 | **REVIEW-CLOSED 2026-08-25** | `4ade9b04` / `bf8f5b70` (docs-only fix) | port exact per dual-lens review (presence-gate adjudicated FOR the implementer at `InqInt @0x005B3830`; Time-in-Dereth = pre-existing `RetailDurationText @0x00565E10` port, correct reuse); fix round was oracle-doc corrections + records only, NO code change. True full-solution hermetic count 15,528 (the commit's 15,410 was a mis-report). **AS6 carry-note: the Society green/red colorIdx is MODEL-ONLY (ResolveColor no-op pending AP-110 FontInfo residual) — the gate script must NOT gate on row colors.** Pre-existing parallel-load flake surfaced (shadow-caster zero-alloc pin) — #442, unrelated to AS4 | | AS5 | **REVIEW-CLOSED 2026-08-25** | `8f8c0c3a` / `9f3e3263` | the campaign's most rigorously verified slice: 170/170 title strings confirmed (164 mechanical diff, 6 PE byte-decoded from the PDB-paired binary), all 20 dispatch arms, all 17 bounds tests, both call sites re-derived; zero behavioral findings. Fix round = "retires AP-109" → "narrows" at 5 comment sites + the plan (the FormatXp sliver keeps the row active); re-review also flagged + this close fixed the last "retires" phrasing (`AppraisalUiController.cs`) and the CT plan's stale retirement intent | -| AS6 | **DONE 2026-08-25 — script committed, WAITING ON THE OWNER'S DRIVE** | (this commit) | `docs/research/2026-08-25-campaign-as-test-script.md`; carries the two standing rulings (row colors model-only — do not gate; AD-114 paperdoll deviation expected) and the R3 legend retail side-by-side check | +| AS6 | **GATE PASSED 2026-08-25** | script `87e98395`; gate round `65f6f584` (AS-GF1) + probe-removal close commit | owner ran the gate live; two findings harvested and resolved in-round (extras clip = retail's authored scroll-less listbox, not a defect; paperdoll = #443 first-open delay, render layer proven healthy by probe); identity block, AL rows, allegiance/extras, and the rank-title title bar all owner-verified; probes stripped at close | diff --git a/src/AcDream.App/Rendering/CreatureAppraisalPresentation.cs b/src/AcDream.App/Rendering/CreatureAppraisalPresentation.cs index 96f351c7..9b34a6ed 100644 --- a/src/AcDream.App/Rendering/CreatureAppraisalPresentation.cs +++ b/src/AcDream.App/Rendering/CreatureAppraisalPresentation.cs @@ -136,37 +136,19 @@ internal sealed class RetailCreatureAppraisalFrameView : if (_controller.ActiveView is not ( AppraisalView.Creature or AppraisalView.Character)) { - CreatureAppraisalViewportDiagnostics.ReportGate( - $"no ActiveView (was {_controller.ActiveView})"); return false; } if (!IsEffectivelyVisible(_windowFrame)) - { - CreatureAppraisalViewportDiagnostics.ReportGate("windowFrame hidden"); return false; - } if (!IsEffectivelyVisible(_viewport)) - { - CreatureAppraisalViewportDiagnostics.ReportGate("viewport hidden"); return false; - } if (_controller.CurrentObjectId == 0u) - { - CreatureAppraisalViewportDiagnostics.ReportGate("no CurrentObjectId"); return false; - } serverGuid = _controller.CurrentObjectId; width = (int)_viewport.Width; height = (int)_viewport.Height; - if (width <= 0 || height <= 0) - { - CreatureAppraisalViewportDiagnostics.ReportGate( - $"zero viewport extent ({width}x{height})"); - return false; - } - CreatureAppraisalViewportDiagnostics.ReportGate("open"); - return true; + return width > 0 && height > 0; } public void SetTextureHandle(uint textureHandle) => @@ -225,18 +207,9 @@ internal sealed class RetailCreatureAppraisalCloneFactory : boundsMin = Vector3.Zero; boundsMax = Vector3.Zero; if (!_entities.TryGet(serverGuid, out WorldEntity source)) - { - CreatureAppraisalViewportDiagnostics.ReportSync( - $"entity not found (guid 0x{serverGuid:X8})"); return false; - } if (source.MeshRefs.Count == 0) - { - CreatureAppraisalViewportDiagnostics.ReportSync( - $"no MeshRefs (guid 0x{serverGuid:X8})"); return false; - } - CreatureAppraisalViewportDiagnostics.ReportSync("synchronized"); WorldEntity clone = currentClone is not null && currentClone.SourceGfxObjOrSetupId == source.SourceGfxObjOrSetupId diff --git a/src/AcDream.App/Rendering/CreatureAppraisalViewportDiagnostics.cs b/src/AcDream.App/Rendering/CreatureAppraisalViewportDiagnostics.cs deleted file mode 100644 index 31ff553e..00000000 --- a/src/AcDream.App/Rendering/CreatureAppraisalViewportDiagnostics.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; - -namespace AcDream.App.Rendering; - -/// -/// AS-GF1 (2026-08-25): a temporary, state-change-gated probe for the -/// creature-examination paperdoll regression (#443 — the animated paperdoll -/// stopped rendering for player targets sometime in Campaign AS, but -/// exhaustive review of the AS2-AS5 diff plus a worst-case hermetic -/// regression test (AppraisalUiControllerTests. -/// CharacterResponse_WorstCaseExtrasCombination_DoesNotThrowAndViewportGateStaysOpen) -/// proved never -/// blocks ActiveView/CurrentObjectId/the viewport's ancestor-visibility -/// chain, even under the combined AS3+AS4 worst case). This means the actual -/// failing condition is one of -/// 's four -/// gates or 's -/// two — none of which the Campaign AS diff touches — and could not be -/// reproduced hermetically (it needs a live entity + a live examine -/// exchange). Enable with ACDREAM_PROBE_CREATURE_APPRAISAL_VIEWPORT=1; -/// logs only on a REASON transition (not every frame) to stay cheap enough -/// to leave on for a whole session. Delete this class and its call sites in -/// the commit that lands the real fix. -/// -internal static class CreatureAppraisalViewportDiagnostics -{ - public static bool Enabled { get; } = - Environment.GetEnvironmentVariable("ACDREAM_PROBE_CREATURE_APPRAISAL_VIEWPORT") == "1"; - - // Two independent last-reason latches — TryGetVisibleTarget and - // TrySynchronize each run every frame and would otherwise "transition" - // against EACH OTHER's most recent line on every healthy frame, - // defeating the point of state-change-only logging. - private static string? _lastGateReason; - private static string? _lastSyncReason; - - public static void ReportGate(string reason) - { - if (!Enabled || reason == _lastGateReason) - return; - _lastGateReason = reason; - Console.WriteLine($"[AS-GF1-PROBE] TryGetVisibleTarget: {reason}"); - } - - public static void ReportSync(string reason) - { - if (!Enabled || reason == _lastSyncReason) - return; - _lastSyncReason = reason; - Console.WriteLine($"[AS-GF1-PROBE] TrySynchronize: {reason}"); - } -}