fix(ui): morning gate — House tab renders retail's TWO houseless lines, not one

User finding 2 (retail screenshot, houseless character): the House tab
shows "You do not currently own a house." ABOVE "You may buy another
house immediately." — ours showed only the second line, and the prior
session had REFUTED the first line outright ("no such string exists
anywhere in the 2013 dump").

Re-derivation: the string exists in the binary at data_7ab688 — it is
gmHouseUI::DisplayBuyPayment @0x004a2b30's HOUSELESS branch. Two
compounding misreads hid it: (a) DisplayBuyPayment was mislabeled
houseless-silent, but its m_pHouseData gate only selects WHICH text
(jne 0x4a2b63) — the ListBox emit (@0x004a2b80 onward,
AddItemFromTemplateList + SetTextWithFont) runs in BOTH branches; and
(b) BN's pseudo-C renders both push-literal operands as spurious
&vftable.RecvNotice_* symbol matches (the TS-85/F3 artifact class), so
text sweeps of the dump find nothing — capstone byte-decode of the
PDB-paired binary resolves houseless @0x004a2b57 push 0x7ab688 =
"You do not currently own a house." and owned @0x004a2b63 push
0x7ab65c = "The purchase price for this dwelling is:\n" (+
HousePaymentList::ComposeText, still #413 item-3 scope). The morning
brief's alternate DAT-string-table hypothesis was checked and is NOT
the mechanism — plain exe string-pool literal.

RuntimeHouseState.Recompute now renders the houseless case as retail's
exact two lines in gmHouseUI::Update's fixed builder order
(DisplayBuyPayment first, DisplayPurchaseTimeText last); the owned case
is unchanged (its DisplayBuyPayment content needs ComposeText, #413
item 3). Class doc + ISSUES #413 corrected honestly — the user's retail
evidence supersedes the earlier refutation. Runtime house tests updated
to pin both lines; 9/9 pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-17 08:54:55 +02:00
parent 9d9280a069
commit ef567bfa20
3 changed files with 139 additions and 76 deletions

View file

@ -24,12 +24,14 @@ 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.
## #413 — House tab shows no content (owned-house display, six Display* line builders unported)
## #413 — House tab shows no content (owned-house display, Display* line builders unported)
**Status:** NARROWED 2026-08-17 (House-tab ownership-text closer session);
item 2's not-expired branch closed same-day at the night-round review fix
round (F8). Items 1 and 2 below are DONE; item 3 (six owned-house-only
builders) remains OPEN and is the entire remaining scope.
round (F8); **the houseless case CORRECTED same-day at the morning gate
round (user finding 2 — see the correction note inside item 2)**. Items 1
and 2 below are DONE; item 3 (owned-house-only builder content) remains
OPEN and is the entire remaining scope.
**What's shipped (this session, on top of Batch C's mount + parser
groundwork).**
@ -71,23 +73,34 @@ groundwork).**
locale, full date+time" intent — filed as register row IA-23, an
approximation, not a gap).
**Corrects a framing this session's task brief carried in from outside
this doc**: the brief described retail as ALSO showing a preceding line
"You do not currently own a house." No such string, in that or any close
wording, exists anywhere in the 2013 EoR `acclient_2013_pseudo_c.txt`
dump, in any `gmHouseUI`/`gmMapUI` method, in ACE's `GameEventHouseStatus`
writer, or in the live-DAT House ListBox/page (re-confirmed empty this
session — `MapHousePanelSlotProbeTests`, zero rows, zero sibling
content). The closest strings found are UNRELATED generic command-error
chat text ("You do not own a house!", WeenieError `0x45E`/`0x45F`; "You
must own a house to use this command.", WeenieError `0x47F`), routed
through the GENERIC WeenieError-to-chat dispatcher, never through
`gmHouseUI`. This ISSUES entry's OWN pre-existing "Acceptance test once
closed" line below (written before this session, by the same research
pass that produced the recon doc) already named the single-line
"You may buy another house immediately." text as the target — this
session's mechanism derivation independently reached the same
conclusion and is now the shipped, tested behavior.
**CORRECTION (2026-08-17 morning gate round, user finding 2 — the
paragraph this replaces was WRONG):** the earlier session had refuted
the preceding line "You do not currently own a house." with the claim
that "no such string exists anywhere in the 2013 EoR
`acclient_2013_pseudo_c.txt` dump" — but the user's retail screenshot
(their live client, houseless character) shows exactly that line ABOVE
"You may buy another house immediately.", and the user's side-by-side
retail reports are axioms. The re-derivation found the string DOES
exist in the 2013 binary, at `data_7ab688`: it is
`gmHouseUI::DisplayBuyPayment @0x004a2b30`'s HOUSELESS branch. Two
compounding misreads hid it: (a) `DisplayBuyPayment` was mislabeled
houseless-silent — its `m_pHouseData` gate only selects WHICH text
(`jne 0x4a2b63`); the ListBox emit (`AddItemFromTemplateList` +
`SetTextWithFont`, `@0x004a2b80` onward) runs in BOTH branches; and
(b) the pseudo-C dump renders both `push <literal>` operands as
spurious `&gmHouseUI::vftable'.RecvNotice_*` symbol matches (the same
BN artifact class TS-85/F3 documented), so a TEXT sweep of the dump
finds no house strings there — the raw string pool has them
(byte-decoded via capstone this round: houseless `@0x004a2b57` `push
0x7ab688` = "You do not currently own a house."; owned `@0x004a2b63`
`push 0x7ab65c` = "The purchase price for this dwelling is:\n" +
`HousePaymentList::ComposeText`, still item-3 scope). The morning-gate
task brief's alternate hypothesis (a DAT string-table id) was also
checked and is NOT the mechanism — it is a plain exe string-pool
literal, same as every other gmHouseUI line. `RuntimeHouseState.
Recompute` now renders the houseless case as retail's exact TWO lines
in builder order: "You do not currently own a house." then the
purchase-time line; `RuntimeHouseStateTests` updated to pin both.
**Also fixed in the same pass: `HousePageController.Bind` never wired
`UiTemplateListBox.TemplateResolver`.** Without it,
@ -101,10 +114,11 @@ groundwork).**
**What remains open — item 3, the entire surviving scope:**
3. **The six owned-house-only `Display*` line builders** (`DisplayBuyPayment`,
`DisplayRentPayment`, `DisplayBuyTime`, `DisplayRentTimes`,
`DisplayLocation`, `DisplayWarningText` — all called from
`gmHouseUI::DisplayHouseData @0x004a3380`). Each is dozens-to-a-few-hundred
3. **The owned-house-only `Display*` line content** (`DisplayBuyPayment`'s
OWNED branch — its houseless branch shipped at the 2026-08-17 morning
gate correction above — plus `DisplayRentPayment`, `DisplayBuyTime`,
`DisplayRentTimes`, `DisplayLocation`, `DisplayWarningText`, all called
from `gmHouseUI::DisplayHouseData @0x004a3380`). Each is dozens-to-a-few-hundred
lines of heavily FPU/string-mangled BN pseudo-C (PStringBase sprintf
chains, `HousePaymentList` iteration, `IsPaidInFull`/
`ConstructRentWarningMessage`-style formatting) — genuinely sized as its