The findings doc gets an evening-v2 follow-on documenting: - GfxObj dump infrastructure shipped (cc3afbc) - Harness reproduces cap-event collision normal (97fec19) - Residual +0.0266m X-motion divergence — the new investigation target - Pre-existing test suite flakiness (out of scope, tracked separately) CLAUDE.md's "Current A6 phase" block points at the residual divergence as the next concrete move with the test that gives <1s feedback per fix attempt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
378 lines
19 KiB
Markdown
378 lines
19 KiB
Markdown
# A6.P3 #98 — Comparison harness shipped, root cause identified
|
||
|
||
**Session:** 2026-05-23 evening (continuation of full-day session)
|
||
**Worktree:** `C:\Users\erikn\source\repos\acdream\.claude\worktrees\strange-albattani-3fc83c`
|
||
**Branch:** `claude/strange-albattani-3fc83c`
|
||
|
||
Read this AFTER the morning's handoff doc
|
||
([`2026-05-23-a6-p3-issue98-harness-handoff.md`](2026-05-23-a6-p3-issue98-harness-handoff.md)) —
|
||
this picks up from "Option A: build the side-by-side comparison harness" and
|
||
documents the FIRST evidence-driven step in the saga.
|
||
|
||
---
|
||
|
||
## TL;DR
|
||
|
||
**Updated 2026-05-23 evening v2: apparatus convergence shipped.** The
|
||
harness now reproduces the live cottage-floor cap event bit-perfect on
|
||
the collision normal. The residual divergence is a single +X-motion
|
||
edge-slide gap; everything else round-trips. The session below covers
|
||
both arcs (root-cause identification THEN convergence).
|
||
|
||
- **Evidence-driven apparatus shipped.** `PhysicsResolveCapture` writes one
|
||
JSON Lines record per player ResolveWithTransition call when
|
||
`ACDREAM_CAPTURE_RESOLVE=<path>` is set. 41,228 records from a single
|
||
cellar-walk session.
|
||
- **Comparison test reproduces the cap divergence on the first try.** The
|
||
new `LiveCompare_*` tests in `CellarUpTrajectoryReplayTests.cs` load three
|
||
representative records (spawn, on-ramp, first-cap) and replay them
|
||
through the harness engine. Spawn + on-ramp PASS bit-perfect; first-cap
|
||
FAILED with a clear divergence — the right divergence.
|
||
- **Root cause identified: the cottage GfxObj was missing from the harness.**
|
||
Live cap attributes the blocking entity to `obj=0xA9B47900` — a
|
||
landblock-baked static building. The cottage's floor polygons live in
|
||
this GfxObj's polygon table (registered as a ShadowEntry), NOT in any
|
||
cottage CELL.
|
||
- **Apparatus convergence (v2 update).** With the cottage GfxObj
|
||
`0x01000A2B` extracted via the new `ACDREAM_DUMP_GFXOBJS` infrastructure
|
||
and registered as a ShadowEntry in `BuildEngineWithCellarFixtures`, the
|
||
harness now reproduces the live `cn=(0,0,-1)` cap exactly. The
|
||
full per-field round-trip reveals one residual: live preserves
|
||
+0.0266 m of +X motion through the cap; harness blocks all motion.
|
||
That's the next investigation target — see the "Residual divergence"
|
||
section below.
|
||
- **Not a step-up / AdjustOffset bug.** The head sphere (top at Z=foot+1.2)
|
||
hits the cottage floor at Z=94.0 from BELOW. Math: cap at foot Z=92.74
|
||
matches 94.0 − 1.2 = 92.80. Confirmed by user reporting same cap when
|
||
JUMPING in the cellar (purely vertical motion). The retail comparison
|
||
question is now sharpened to "how does live's post-cap edge-slide
|
||
preserve the +X component that the harness drops?"
|
||
|
||
---
|
||
|
||
## What ran this session (chronological, 3 commits)
|
||
|
||
| Commit | What |
|
||
|---|---|
|
||
| `fb5fba6` | Apparatus: `PhysicsResolveCapture` static class + JSON Lines writer + body snapshot record + capture probe in `ResolveWithTransition` + smoke tests (capture writes when IsPlayer + enabled, skips otherwise) |
|
||
| `44614ab` | Comparison test: 3 fixture records sampled from live capture + 3 `LiveCompare_*` tests + diagnostic dump that prints cell polygons in world frame |
|
||
| `0f2db62` | Converted FirstCap test to documents-the-bug pattern (passes while harness lacks cottage GfxObj; fails when added) |
|
||
|
||
Live capture launches:
|
||
- `launch-a6-issue98-capture.ps1` — first capture run (no probes beyond cell-transit). Produced `a6-issue98-resolve-capture.jsonl` (12 MB, 5789 records when checked mid-session, finished at 91 MB / 41,228 records).
|
||
- `launch-a6-issue98-polydump.ps1` — second capture with `ACDREAM_PROBE_POLY_DUMP`, `ACDREAM_PROBE_PUSH_BACK`, `ACDREAM_PROBE_RESOLVE`, `ACDREAM_PROBE_INDOOR_BSP`, and `ACDREAM_DUMP_CELLS` covering 0xA9B40140-0xA9B4014F. Produced `a6-issue98-resolve-capture-2.jsonl` (135 MB, 70,572 records) plus 16 cell-dump JSON fixtures and a launch log with 214 [poly-dump] entries.
|
||
|
||
---
|
||
|
||
## The apparatus (committed code)
|
||
|
||
### `PhysicsResolveCapture` ([`src/AcDream.Core/Physics/PhysicsResolveCapture.cs`](../../src/AcDream.Core/Physics/PhysicsResolveCapture.cs))
|
||
|
||
Static module. When `ACDREAM_CAPTURE_RESOLVE=<path>` is set, every player-side
|
||
`PhysicsEngine.ResolveWithTransition` call appends one JSON Lines record:
|
||
|
||
```json
|
||
{
|
||
"tick": 0,
|
||
"timestampMs": 40919993,
|
||
"input": { ... full inputs ... },
|
||
"bodyBefore": { ... full PhysicsBody snapshot ... },
|
||
"result": { ... full ResolveResult ... },
|
||
"bodyAfter": { ... full PhysicsBody snapshot ... }
|
||
}
|
||
```
|
||
|
||
Filtered to `IsPlayer` mover flag so NPC / remote DR calls don't pollute.
|
||
Thread-safe writer with per-record flush. Process-exit hook for clean
|
||
shutdown.
|
||
|
||
### Comparison harness ([`tests/AcDream.Core.Tests/Physics/CellarUpTrajectoryReplayTests.cs`](../../tests/AcDream.Core.Tests/Physics/CellarUpTrajectoryReplayTests.cs))
|
||
|
||
Three `LiveCompare_*` tests + one diagnostic dump:
|
||
|
||
| Test | Outcome | Meaning |
|
||
|---|---|---|
|
||
| `LiveCompare_Tick0_Spawn` | PASSES | Spawn at Z=92.5333; engine matches live bit-perfect |
|
||
| `LiveCompare_Tick376_OnRamp` | PASSES | Player on ramp at Z=91.49; ramp walkable polygon hydrates correctly, engine reproduces live |
|
||
| `LiveCompare_FirstCap_HarnessMissesCottageFloorBecauseCottageGfxObjNotRegistered` | PASSES (documents the bug) | Live cap at Z=92.74 with cn=(0,0,-1); harness does NOT reproduce because cottage GfxObj isn't registered |
|
||
| `LiveCompare_FirstCap_DiagnosticDump` | PASSES (probe-only) | Prints cell polygons in world frame + enables every probe — captured stdout shows harness BSP query path |
|
||
|
||
The diagnostic dump test runs the cap replay with `[poly-dump]`, `[push-back]`,
|
||
`[indoor-bsp]`, `[step-walk]` probes ALL enabled. The captured stdout shows:
|
||
|
||
```
|
||
[cell-dump] 0xA9B40147 resolved-poly-count=37
|
||
poly id=0x0018 ... worldVerts=[(140.12,11.50,90.95),...(142.10,11.50,90.95)] ← cellar floor
|
||
poly id=0x0001 ... worldVerts=[(142.10,11.50,93.80),...(140.50,8.70,93.80)] ← cellar ceiling
|
||
|
||
[cell-dump] 0xA9B40143 resolved-poly-count=14
|
||
poly id=0x0004 ... worldVerts=[(136.70,3.90,94.00),(140.50,3.90,94.00),(140.50,8.70,94.00)] ← cottage floor (triangle)
|
||
... more cottage floor triangles, all at world Z=94.00 ...
|
||
|
||
[other-cells] primary=0xA9B40147 iter=0xA9B40143 wpos=(141.605,7.097,93.351) result=OK poly=n/a
|
||
[other-cells] primary=0xA9B40147 iter=0xA9B40146 wpos=(141.605,7.097,93.351) result=OK poly=n/a
|
||
```
|
||
|
||
Both other-cells iterations return OK — the cottage floor polys in
|
||
0xA9B40143 don't extend to the sphere's XY (X=141.39 > rightmost-vertex
|
||
X=140.50). So the harness sees no collision, even though the live engine
|
||
does.
|
||
|
||
---
|
||
|
||
## How we identified the missing object (it's NOT a cell)
|
||
|
||
The second capture pass enabled `ACDREAM_PROBE_RESOLVE=1`, which logs
|
||
each call's hit details including the entity guid of the blocking object.
|
||
The cap event prints:
|
||
|
||
```
|
||
[resolve] ent=0x000F4240 in=(141.605,7.304,92.656) tgt=(141.624,6.875,92.656)
|
||
out=(141.605,7.304,92.656) ok=True groundedIn=True cp=valid
|
||
hit=yes n=(0.00,0.00,-1.00) obj=0xA9B47900 walkable=True
|
||
```
|
||
|
||
**obj=0xA9B47900** is in the landblock-baked static range (0xA9B47XXX
|
||
guids belong to landblock 0xA9B4's static objects). This is the cottage
|
||
BUILDING as a GfxObj registered as a ShadowEntry on the landblock —
|
||
NOT a cottage cell.
|
||
|
||
The harness's `BuildEngineWithCellarFixtures` loads three CELL fixtures
|
||
(0xA9B40143, 0xA9B40146, 0xA9B40147) but **does not register any
|
||
landblock-baked static**. There IS a `RegisterStairRampGfxObj` helper
|
||
that constructs ONE polygon (the ramp), but it's commented out today.
|
||
|
||
So the missing apparatus is: register the cottage GfxObj as a ShadowEntry
|
||
with its FULL polygon table — ramp + walls + floor + ceiling. Once
|
||
registered, the harness's multi-cell BSP iteration's
|
||
`FindObjCollisions` will query the GfxObj's BSP and find the cottage
|
||
floor polygon's downward-facing plane just like live.
|
||
|
||
---
|
||
|
||
## The cap geometry (math)
|
||
|
||
Live capture analysis confirmed the sphere physics:
|
||
|
||
- Foot sphere center at world Z = foot_z, radius 0.48m
|
||
- Head sphere center at world Z = foot_z + sphereHeight = foot_z + 1.2m
|
||
- Head sphere top at Z = foot_z + 1.2 + 0.48 = foot_z + 1.68m
|
||
|
||
Cap point in live capture: foot_z = 92.7390 (from tick 1183).
|
||
Predicted head sphere position: head center = 93.9390, head top = 94.4190.
|
||
|
||
The cottage floor is at world Z = 94.0 (from cell 0xA9B40143's poly 0x04
|
||
worldVerts: `(136.70,3.90,94.00)`, etc.).
|
||
|
||
**Head sphere center at Z=93.94 is BELOW the cottage floor at Z=94.0 by 0.06.**
|
||
**Head sphere top at Z=94.42 is ABOVE the cottage floor by 0.42.**
|
||
|
||
The head sphere PENETRATES the cottage floor. BSP push-back direction
|
||
is the negative of the polygon's outward normal (which is +Z facing UP),
|
||
so push-back direction is −Z (pushes sphere DOWN). That matches the
|
||
live cn=(0,0,-1).
|
||
|
||
The "exact" cap position: foot_z when head center is at Z=94.0 (just
|
||
touching). foot_z = 94.0 − 1.2 = 92.80. The observed cap at foot_z=92.74
|
||
is ~0.06 below the predicted (push-back includes epsilon and walk-interp
|
||
adjustments).
|
||
|
||
---
|
||
|
||
## User's confirming observation
|
||
|
||
> "I noticed a thing. When I jump in the cellar, I'm getting blocked at
|
||
> the same height (I think) as I am when running up the stairs."
|
||
|
||
This is the key observation that nailed the diagnosis. **Jumping is
|
||
pure vertical motion** — no ramp slope, no AdjustOffset projection. If
|
||
the cap fires on a pure jump, the obstruction must be a horizontal
|
||
geometric obstacle at the cap height. That immediately rules out every
|
||
step-up / AdjustOffset hypothesis from the prior 6+6 saga and pinpoints
|
||
the bug as a head-sphere head-on collision with a cottage-floor
|
||
polygon facing DOWN.
|
||
|
||
---
|
||
|
||
## What's NOT yet known
|
||
|
||
1. **Why retail doesn't have this cap.** Either:
|
||
- (a) Retail's cottage GfxObj has a HOLE in the floor above the ramp
|
||
(cottage floor polygons stop at the ramp opening; our dat-read
|
||
produces a contiguous floor)
|
||
- (b) Retail's BSP query treats single-sided polygons correctly
|
||
(cottage floor's SidesType allows collision from +Z side only,
|
||
not from −Z side; we treat it as both-sided)
|
||
- (c) Retail uses portal-aware collision: when the sphere is inside
|
||
the cellar EnvCell, queries skip polygons that belong to the
|
||
cottage portal's "other side"
|
||
|
||
Need a retail cdb trace at the ramp-top to disambiguate.
|
||
|
||
2. **The cottage GfxObj's full polygon list.** We have the ramp polygon
|
||
(poly 0x0008 in the cottage GfxObj, normal (0,-0.719,0.695)) and we
|
||
know the floor polygon is at Z=94.0 with normal (0,0,-1) or (0,0,+1).
|
||
We do NOT have:
|
||
- the full polygon list of GfxObj 0xA9B47900
|
||
- the cottage GfxObj's id, BSP root, or scale/rotation
|
||
|
||
These can all be extracted by enabling `ACDREAM_PROBE_BUILDING=1` for
|
||
a future capture — the `[resolve-bldg]` probe dumps per-poly geometry
|
||
when a building shadow entry is hit.
|
||
|
||
3. **`ACDREAM_PROBE_POLY_DUMP` doesn't fire for the cottage hit.** The
|
||
[poly-dump] probe is wired into `AdjustSphereToPlane`, but the
|
||
cottage-floor collision goes through `FindObjCollisions` →
|
||
`BSPQuery.FindCollisions` on the GfxObj's internal BSP — a different
|
||
code path. Future probing should use `ACDREAM_PROBE_BUILDING` instead
|
||
to capture the per-object collision details.
|
||
|
||
---
|
||
|
||
## Next-session pickup
|
||
|
||
### What shipped 2026-05-23 evening v2 (post-prior-section)
|
||
|
||
Three commits land apparatus convergence on the cap event:
|
||
|
||
| Commit | What |
|
||
|---|---|
|
||
| `cc3afbc` | **GfxObj dump infrastructure.** Mirrors `ACDREAM_DUMP_CELLS`: new env var `ACDREAM_DUMP_GFXOBJS` triggers `PhysicsDataCache.CacheGfxObj` to write the full resolved polygon table as JSON, suffix `.gfxobj.json` so dumps don't collide with cell dumps in the same dir. New `GfxObjDump` DTO + `GfxObjDumpSerializer` parallel to `CellDump`; round-trip tests cover Capture / Write / Read / Hydrate; the Hydrate path constructs a synthetic single-leaf BSP for query coverage. |
|
||
| `97fec19` | **Harness reproduces the cottage-floor cap event.** `BuildEngineWithCellarFixtures` now registers a stub landblock 0xA9B40000 (TerrainSurface at z=-1000) so `TryGetLandblockContext` succeeds at the cellar XY, plus a new `RegisterCottageGfxObj` helper that loads the dumped cottage GfxObj fixture, hydrates it with synthetic BSP, and registers as a ShadowEntry at world (130.5, 11.5, 94.0) with 180° Z rotation — matching production's `GameWindow.cs:5893` registration shape for landblock-baked statics. The cottage fixture (74 polys, 6 downward-facing floor triangles, BSP radius 13.989 m) lives at `tests/.../Fixtures/issue98/0x01000A2B.gfxobj.json`; capture launch script is `launch-a6-issue98-cottage-gfxobj-dump.ps1`. |
|
||
|
||
Test outcome at apparatus convergence:
|
||
|
||
| Test | Outcome | Meaning |
|
||
|---|---|---|
|
||
| `LiveCompare_Tick0_Spawn` | PASS | Spawn round-trip preserved by the new landblock + cottage state |
|
||
| `LiveCompare_Tick376_OnRamp` | PASS | On-ramp round-trip preserved |
|
||
| `LiveCompare_FirstCap_HarnessReproducesCottageFloorCapNormal` | PASS (NEW) | Harness reproduces the live cn=(0,0,-1) cap-event normal exactly |
|
||
| `LiveCompare_FirstCap_ResidualXMotionDivergence_DocumentsNextInvestigation` | PASS (documents-the-bug) | Captures the ONE remaining post-cap divergence: live preserves +0.0266 m of +X motion through the cap (edge-slide along the cottage floor in XY); harness blocks ALL motion. Y and Z agree. |
|
||
|
||
### The residual divergence (next investigation target)
|
||
|
||
After registering the cottage GfxObj:
|
||
|
||
```
|
||
Live: cn=(0,0,-1), position=(141.3865, 7.2243, 92.7390) ← +X motion preserved
|
||
Harness: cn=(0,0,-1), position=(141.3599, 7.2243, 92.7390) ← X stuck at input
|
||
Input: currentPos=(141.3599, 7.2243, 92.7390)
|
||
targetPos =(141.3865, 6.8221, 92.7390)
|
||
requestedDelta=(+0.0266, -0.4022, 0)
|
||
```
|
||
|
||
The cap-event collision normal matches bit-perfect. Position diverges
|
||
in X only. Working hypothesis: live's response to a `cn=(0,0,-1)`
|
||
head-bump treats it as a Z-only constraint and edge-slides the
|
||
remaining XY component along the cottage floor; harness's BSP path is
|
||
rejecting the entire move vector instead of computing a slid offset.
|
||
|
||
That hypothesis is the next-session investigation target — work the
|
||
slide path in `Transition.transitional_insert` / `AdjustOffset` against
|
||
the production cap-event call. The new
|
||
`LiveCompare_FirstCap_ResidualXMotionDivergence_DocumentsNextInvestigation`
|
||
test PASSES today (asserting the current residual) and FAILS when the
|
||
divergence closes — that's the signal to flip it into
|
||
`AssertCallMatchesCapture` form.
|
||
|
||
### Alternative pickup move: retail cdb trace at the cottage ramp-top
|
||
|
||
If apparatus polish is enough and the user wants to widen the question
|
||
to "how does retail differ?", attach cdb to a running retail acclient
|
||
(see CLAUDE.md "Retail debugger toolchain"), set breakpoints on
|
||
`BSPTREE::find_collisions` and `CGfxObj::shadow_find_obj_collisions`,
|
||
walk up the cottage ramp, and log every BSP query against the cottage
|
||
GfxObj. Compare which polygons retail finds vs which polygons our
|
||
acdream engine finds. Retail's trace is the ultimate oracle for the
|
||
"how does retail differ?" question — but the apparatus-side X residual
|
||
investigation is the more focused, faster-feedback next step.
|
||
|
||
### Pre-existing test flakiness (out of scope but documented)
|
||
|
||
While verifying the cottage helper, the full `dotnet test` serial run
|
||
produced 8–19 failures across 1192 tests depending on order — the
|
||
suite has static-state leakage between test classes (likely from
|
||
`PhysicsResolveCapture.CapturePath`, `PhysicsDiagnostics.Probe*Enabled`,
|
||
and similar global mutators). The flakiness is **independent of A6.P3**:
|
||
stashing the cottage helper out and rerunning produces the same flaky
|
||
range. All 21 issue-#98-relevant tests (12 harness + 4
|
||
`GfxObjDumpRoundTripTests` + 1 new `PhysicsDiagnosticsTests` + 4
|
||
`CellDumpRoundTripTests`) pass deterministically in isolation.
|
||
|
||
---
|
||
|
||
## Apparatus that exists to use
|
||
|
||
| Tool | Location | Status |
|
||
|---|---|---|
|
||
| `PhysicsResolveCapture` | `src/AcDream.Core/Physics/` | Production-ready; env-var gated; off by default |
|
||
| `LiveCompare_*` tests | `tests/.../CellarUpTrajectoryReplayTests.cs` | 4 tests; 1 documents the bug, 3 are matches |
|
||
| `live-capture.jsonl` fixture | `tests/.../Fixtures/issue98/` | 3 representative records (spawn, on-ramp, first-cap) |
|
||
| `launch-a6-issue98-capture.ps1` | worktree root | Capture-enabled launch (no diagnostic probes) |
|
||
| `launch-a6-issue98-polydump.ps1` | worktree root | Capture + poly-dump + push-back + dump-cells launch |
|
||
| 16 cell-dump fixtures | `tests/.../Fixtures/issue98/0xA9B4014X.json` | All cells in 0xA9B4014X range from second capture |
|
||
| 41K-record live capture | `a6-issue98-resolve-capture.jsonl` (gitignored size) | First capture — full session of cellar movement |
|
||
| 70K-record live capture w/ probes | `a6-issue98-resolve-capture-2.jsonl` | Second capture — included poly-dump events |
|
||
| `a6-issue98-polydump-launch.log` | worktree root | 56K+ line log with [resolve], [poly-dump], [other-cells], [indoor-bsp] events |
|
||
|
||
---
|
||
|
||
## Pickup prompt for next session
|
||
|
||
```
|
||
A6.P3 #98 — apparatus convergence landed, residual X-motion divergence
|
||
is next.
|
||
|
||
Read FIRST:
|
||
1. docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md
|
||
(this doc — particularly the "What shipped 2026-05-23 evening v2"
|
||
and "The residual divergence" sections)
|
||
2. tests/AcDream.Core.Tests/Physics/CellarUpTrajectoryReplayTests.cs
|
||
(especially the two LiveCompare_FirstCap_* tests and the
|
||
RegisterCottageGfxObj helper)
|
||
3. CLAUDE.md "Current A6 phase" block
|
||
|
||
State both altitudes:
|
||
Currently working toward: M1.5 — Indoor world feels right
|
||
Current phase: A6.P3 — apparatus convergence shipped. Harness now
|
||
reproduces the live cottage-floor cap event (cn=(0,0,-1) round-trips
|
||
bit-perfect). Residual: +0.0266 m of +X motion lost in the harness's
|
||
post-cap slide where live preserves it.
|
||
|
||
Two concrete next moves:
|
||
|
||
(A) Investigate the +X edge-slide divergence in the harness. The
|
||
LiveCompare_FirstCap_ResidualXMotionDivergence_DocumentsNextInvestigation
|
||
test currently passes asserting the divergence; flipping it should
|
||
drive the investigation. Likely target: Transition.transitional_insert
|
||
/ AdjustOffset's handling of a cn=(0,0,-1) head-bump — live treats
|
||
it as Z-only constraint and edge-slides the remaining XY motion;
|
||
harness blocks all motion. Decomp anchor: acclient_2013_pseudo_c.txt
|
||
in the find_obj_collisions → adjust_sphere_to_plane chain. ~2 hours
|
||
estimate.
|
||
|
||
(B) Attach cdb to retail at the cottage ramp-top, trace the BSP queries,
|
||
compare polygon-by-polygon what retail finds vs what acdream finds.
|
||
Authoritative for the "how does retail differ?" question but
|
||
larger scope (~half day setup + capture).
|
||
|
||
(A) is recommended — the harness now isolates this divergence to a
|
||
specific known XY slide path; the test gives <1s feedback per fix
|
||
attempt. (B) becomes valuable if (A) hypothesis chase stalls.
|
||
|
||
CLAUDE.md rules apply throughout. NO speculative fixes — the saga
|
||
already converted from speculation to evidence-driven; keep it that
|
||
way.
|
||
|
||
Out-of-scope but observed: pre-existing test suite has 8–19 failures
|
||
across runs of the same code due to static-state leakage between test
|
||
classes (PhysicsResolveCapture, PhysicsDiagnostics statics). Targeted
|
||
issue-#98 tests pass deterministically in isolation. Don't touch the
|
||
flakiness this session; it's a separate investigation.
|
||
|
||
Test baseline: 1178 + 8 pre-existing failures (serial run).
|
||
Maintain throughout. The previously-failing
|
||
LiveCompare_FirstCap_HarnessMissesCottageFloorBecauseCottageGfxObjNotRegistered
|
||
test is now in documents-the-bug form (PASSES while bug exists; FAILS
|
||
when fix lands) — flip it when the cottage GfxObj is registered.
|
||
```
|