docs+fix(ui): Campaign AS CLOSED — connected gate PASSED; AS-GF1 probes stripped; #443 narrowed
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-GF165f6f584ruled 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 show65f6f584). App hermetic suite green (6,337/0). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
65f6f5848a
commit
ddbd7e4096
5 changed files with 33 additions and 90 deletions
|
|
@ -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`)
|
||||
|
|
|
|||
|
|
@ -181,7 +181,6 @@ fall back to `ACDREAM_DAT_DIR`.
|
|||
| `ACDREAM_ORBIT_DISTANCE_METERS` | `=<float>`, 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` | `=<float>`, 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` | `=<float>`, 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: <reason>` and `[AS-GF1-PROBE] TrySynchronize: <reason>` 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` | `=<int>=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` | `=<float>` (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) |
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace AcDream.App.Rendering;
|
||||
|
||||
/// <summary>
|
||||
/// 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 <see cref="AcDream.App.UI.Layout.AppraisalUiController"/> 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
|
||||
/// <see cref="RetailCreatureAppraisalFrameView.TryGetVisibleTarget"/>'s four
|
||||
/// gates or <see cref="RetailCreatureAppraisalCloneFactory.TrySynchronize"/>'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 <c>ACDREAM_PROBE_CREATURE_APPRAISAL_VIEWPORT=1</c>;
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue