From 87e98395612897e94683d01a0a8f8a722b5f43ea Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 25 Aug 2026 10:33:49 +0200 Subject: [PATCH] =?UTF-8?q?docs(AS):=20AS5=20REVIEW-CLOSED;=20AS6=20gate?= =?UTF-8?q?=20script=20committed=20=E2=80=94=20Campaign=20AS=20implementat?= =?UTF-8?q?ion=20COMPLETE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AS1-AS5 are all review-closed (AS5: 170/170 title strings verified, 6 of them PE byte-decoded; zero behavioral findings across the campaign after AS2). The AS6 connected-gate script is written and waits on the owner: docs/research/2026-08-25-campaign-as-test-script.md, carrying the two standing rulings (Society row colors are model-only pending AP-110 FontInfo — do not gate on them; the AD-114 animated paperdoll deviation is expected) and the R3 unconditional-legend retail side-by-side check. Also: the re-review's grep-hygiene tail — the last "retires AP-109" phrasing in AppraisalUiController.cs now reads "closes AP-109's title-bar residual", and the CT plan's stale "this campaign retires AP-109" intent line (never executed — CT4 narrowed) carries a dated correction. Branch remains unpushed per the owner's standing instruction. Co-Authored-By: Claude Fable 5 --- ...6-08-24-character-panel-parity-campaign.md | 8 +- ...026-08-25-assess-window-parity-campaign.md | 9 +- .../2026-08-25-campaign-as-test-script.md | 147 ++++++++++++++++++ .../UI/Layout/AppraisalUiController.cs | 2 +- 4 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 docs/research/2026-08-25-campaign-as-test-script.md diff --git a/docs/plans/2026-08-24-character-panel-parity-campaign.md b/docs/plans/2026-08-24-character-panel-parity-campaign.md index 76281ede..18204b83 100644 --- a/docs/plans/2026-08-24-character-panel-parity-campaign.md +++ b/docs/plans/2026-08-24-character-panel-parity-campaign.md @@ -4,8 +4,12 @@ **Execution model:** Fable plans and coordinates; Sonnet implements each slice; Opus runs the dual-lens review (retail-faithful + architectural) per slice, then a fix round. No pushes to gitea until the owner says so. -**Register:** this campaign retires AP-109 (inert Titles page) when CT3+CT4 -land; every deviation a slice introduces adds its row in the same commit. +**Register:** [STALE-INTENT CORRECTION 2026-08-25, at Campaign AS's AS5 +re-review: this retirement never happened — CT3/CT4 NARROWED AP-109 and +Campaign AS AS5 later closed its rank-prefix residual; the row remains +ACTIVE-narrowed to CT4's FormatXp sliver.] Original intent: retire AP-109 +(inert Titles page) when CT3+CT4 land; every deviation a slice introduces +adds its row in the same commit. ## Owner report (2026-08-24, screenshots on file) 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 ad1f0874..8818d348 100644 --- a/docs/plans/2026-08-25-assess-window-parity-campaign.md +++ b/docs/plans/2026-08-25-assess-window-parity-campaign.md @@ -1,6 +1,9 @@ # Campaign AS — assess/examination window retail parity (player targets) -**Status: EXECUTING — AS1 research synthesis complete 2026-08-25; slices final.** +**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.** 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 @@ -143,5 +146,5 @@ round → narrow re-review → REVIEW-CLOSED. | AS2 | **REVIEW-CLOSED 2026-08-25** | `f8a22589` / `cc5290af` | port exact per dual-lens review; 6 findings (docs/test/refactor) fixed; +AD-114 (animated paperdoll), +AD-115 (title clear-vs-stale), PK bits promoted to `PublicWeenieFlags`; carried follow-up chip: 3 more Core PK-bit copies | | 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 | fix round applied, re-review pending | `8f8c0c3a` + comment-fix commit | 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) | -| AS6 | pending AS5 review-close | — | | +| 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 | diff --git a/docs/research/2026-08-25-campaign-as-test-script.md b/docs/research/2026-08-25-campaign-as-test-script.md new file mode 100644 index 00000000..38e75ece --- /dev/null +++ b/docs/research/2026-08-25-campaign-as-test-script.md @@ -0,0 +1,147 @@ +# Campaign AS — connected gate script (AS6, user-driven) + +**Purpose:** live verification of the assess/examination-window parity +campaign (AS2–AS5) against ACE, retail side-by-side as the oracle. +Launch: the normal connected launch (`ACDREAM_RETAIL_UI=1`, live ACE at +`127.0.0.1:9000`, `ACDREAM_PAK_PATH=\artifacts\owner-gate\acdream-v5.pak`). +Sections marked **[TWO-CLIENT]** use the FA-campaign account pair +(`testaccount`/`+Acdream` + `testaccount2`/`+Horan`) — assess the OTHER +player. Where a second retail client is available, compare the same assess +performed from retail. + +**Two standing rulings, read before judging:** +- **Row COLORS are out of scope this gate.** The Society green/red (and any + buffed/debuffed row tint) is MODEL-ONLY today — `ResolveColor` renders + the authored default until AP-110's FontInfo-list residual lands. Judge + TEXT content, ordering, and presence only. +- **The paperdoll is our registered intentional deviation (AD-114):** ours + mirrors the target's live motion; retail's clone plays its own decoupled + idle cycle. A mismatch there is EXPECTED and correct. + +--- + +## 1. Header identity block (AS2) [TWO-CLIENT] + +Assess the other player. Under the title bar, four lines: + +1. **Gender+heritage**: " " composed (e.g. "Female + Aluvian") — not a raw property string. PASS: matches retail's line for + the same target. +2. **Title**: the target's CURRENT display title (e.g. "War Mage"). Have + the target change their display title (Character panel → Titles → Set + as Display Title), re-assess: the new title shows. No title set → + the line is empty (ours deliberately clears where retail can show a + stale previous target's title — register AD-115; do not fail us for + being cleaner than retail here). +3. **PK status**: "Non-Player Killer" for a normal character. (If a PK or + PKLite character is available, verify its variant.) +4. **Allegiance name**: shown only when the target is in a named + allegiance (sworn, rank ≥ 1); otherwise empty. +5. **Failed assess**: on the TARGET client enable Deception's + "Attempt to Deceive" option (retail Options → Character) if available + with a high-Deception target; a failed assess shows "???" attributes + BUT the four header lines above still render (identity rides even on + failure). Best-effort — needs a target whose Deception beats the + examiner's Assess Person. +6. **The invented literal is gone**: assess a MONSTER and fail (or any + monster assess) — the line that used to read "Assessment incomplete" + shows nothing, matching retail. + +## 2. Per-bodypart armor levels (AS3) [TWO-CLIENT] + +1. Target wearing armor: three rows appear — + "Head/Chest/Groin AL: a/b/c", "Bicep/Wrist/Hand AL: a/b/c", + "Thigh/Shin/Foot AL: a/b/c" — values matching retail's assess of the + same target (buffed values: cast an armor buff on the target and + re-assess; numbers rise). +2. Target naked (bank the armor): the three rows AND their leading blank + line disappear entirely. +3. Unenchantable coverage (if an unenchantable piece is available): the + affected group shows "*N" (N = the level without the sentinel). +4. **The R3 legend check (explicit retail side-by-side):** "* = + Unenchantable" renders as the LAST line of a player assess. Our port + shows it UNCONDITIONALLY (decomp-proven structure). Confirm retail + does the same on a player with NO starred values and again with NO + armor at all. If retail hides it in either case, report it — that + flips ruling R3 and we change ours. + +## 3. Ratings block (regression) + +On a target with ratings (augmented/geared): "Dmg/CritDmg Rating: x/y", +"Dmg/CritDmg Resist: x/y", "DoT/Life: Resist: x/y" rows as before, +each appearing only when its family is nonzero. Unchanged behavior — +spot-check only. + +## 4. Society / allegiance / fellowship rows (AS4) [TWO-CLIENT] + +1. **Fellowship**: form a fellowship (FA campaign flow), assess the other + member: "Fellowship: " row appears; disband → row gone on + re-assess. +2. **Allegiance cascade** (best-effort — ACE's swear-at-close-range quirk + #384 may block creating fresh vassals): assess a sworn character — + "Monarch:" (and "Patron:" when different, "Monarch/Patron:" when the + same person). Assess a MONARCH: "Alleg. Monarch:" + "N Follower(s)". +3. **Society** (only if a faction-joined character exists on this ACE): + "Society: " with the rank suffix (" ~ Initiate" … " ~ Master") + per the target's standing. TEXT only — ignore colors (standing ruling). + +## 5. Target-configurable extras (AS4) [TWO-CLIENT] + +On the TARGET client open Options → the retail Character tab and toggle +each "Allow others to see..." option, re-assessing from the other client +after each change (each row appears iff the target allows it — the server +gates; we render what arrives): + +| Toggle on target | Row | Expected value shape | +|---|---|---| +| Date of Birth | `Arrived in Dereth:` | server-formatted date, verbatim | +| Age | `Time in Dereth:` | retail duration format — bare units, e.g. "3mo 2d 5h 12m 40s"; zero components omitted except seconds | +| Chess Rank | `Chess Rank:` | number | +| Fishing Skill | `Fishing Skill:` | number | +| Number of Deaths | `Deaths:` | number; a deathless character shows "Has never died" as the value (label stays "Deaths:") | +| Number of Titles | `Titles Earned:` | number | + +PASS: each row toggles with its option, the labels match retail exactly, +and the full extras ORDER matches retail: Society → allegiance rows → +[blank] → armor trio → [blank] → ratings → [blank] → Fellowship → Arrived +→ Time → Chess → Fishing → Deaths → Titles → "* = Unenchantable". + +## 6. Title-bar allegiance rank prefix (AS5) [TWO-CLIENT, best-effort] + +Assess a sworn character with an allegiance rank: the WINDOW TITLE reads +" " (e.g. "Yeoman Horan") — heritage- and +gender-specific title from retail's table. The character panel (F9) name +line of YOUR OWN sworn character shows the same prefix. Unsworn/rank 0 → +plain name in both places. (Creating a fresh sworn pair may be blocked by +#384 — use any already-sworn character; otherwise mark SKIPPED.) + +## 7. Combat auto-refresh + +Enter combat mode with the exam window open on a player: the window +refreshes ~every 0.75 s; the AL trio, extras, and header lines persist +and update (buff the target's armor mid-watch: values change without +re-assessing manually). + +## 8. Regression sweep (5 minutes) + +- Monster assess: species line + stat rows + ratings as before; NO armor + trio, NO legend, nothing where the old invented literal was. +- Item assess: the full item report unchanged; inscription flow intact. +- Spell assess unchanged. +- The animated paperdoll mirrors the target's motion (our AD-114 + deviation — expected). +- Character panel (F9): CT-campaign behaviors intact (titles page, header + identity block, resize clamps 372–1000, scrollbar hand-off). + +--- + +## Report back + +Per section PASS/FAIL plus anything odd. The three answers that matter +most: +1. §5 — does every option-gated row toggle correctly with the target's + own options, in retail's exact order? +2. §2.4 — does retail show the "* = Unenchantable" legend unconditionally + (our R3 reading), or does it hide it in some case? +3. §1 — is the identity block exact against retail for the same target + (composition, title live-update, PK text)? diff --git a/src/AcDream.App/UI/Layout/AppraisalUiController.cs b/src/AcDream.App/UI/Layout/AppraisalUiController.cs index 05d6f9e2..c3da98be 100644 --- a/src/AcDream.App/UI/Layout/AppraisalUiController.cs +++ b/src/AcDream.App/UI/Layout/AppraisalUiController.cs @@ -827,7 +827,7 @@ public sealed class AppraisalUiController : IRetainedPanelController => GetInt(p, 30u) >= 1 ? GetString(p, 47u) : string.Empty; /// - /// Campaign AS slice AS5 (retires AP-109's title-bar residual): the char + /// Campaign AS slice AS5 (closes AP-109's title-bar residual): the char /// path's title-bar OVERWRITE, AllegianceData::GetFullName /// @0x005B6950 called from CharExamineUI::SetAppraiseInfo /// (`@0x004b4c8c`-`@0x004b4cec`, AFTER 's String