refactor(iso): drop the 2.5D overlay data nothing reads (#724)

After #714 the 2.5D overlay scene still carried what decides nothing:

- src/iso-overlays.ts: IsoOverlayPlacement loses tether and grounding (always
  invisible) and raisedScene (always equal to visualScene); IsoOverlayOwner
  loses area; IsoOverlayPlacementInput loses hovered, focused, selected and
  filtersSupported, which the resolver ignored. IsoWallSilhouette and
  tetherGeometry go with them.
- src/iso-scene-render.ts: the structural scene no longer projects wall
  silhouettes (isoWallSilhouettesOf and IsoSceneCacheEntry.wallSilhouettes)
  that served only as a cache key. The placement and render-scene caches are
  keyed by the wall geometry the scene is drawn with (IsoOverlaySceneInput.
  structure = scene.geometry): the structural LRU hands out the same object
  across zoom, stage resize and HA state, and a new one after any wall, room
  or opening edit. The resolveCollisions flag and its fit/live cache slots
  are gone: since #713 both held equal placements, and 2.5D renders only in
  View, where the fit probe and the live frame ask with the same devices, so
  they now read one snapshot.
- src/houseplan-card.ts: the fit call passes no flag; the overlay scene gets
  structural.geometry. data-hp-iso-nudged stays the constant "false" read by
  the golden requireOneRise preflight, the live-touch smoke and the benchmark.

Tests: iso-overlays pins the placement fields; iso-scene-render builds the
structure with buildIsoWallGeometry, the #714 zoom/resize and #711 state tests
stay, fit and live are asserted to share one snapshot, and two #724 AC2 tests
run the production path (createIsoStructuralSource -> resolveIsoScene ->
buildIsoOverlayRenderScene): a thicker wall with the same room rebuilds the
scene (red with a key without walls, e.g. keyed by the room rows), and a room
edit that moves the owner gives the new owner (red with a constant key). The
silhouette-construction test goes with the construction.

Mutants: #473 W2 (iso-placement-cache-survives-silhouette-change, id kept for
history) now keys the placement cache by a constant instead of input.structure
and its guard also runs the #724 AC2 tests; W6 patches the new structure line;
the W5 description no longer speaks of a nudge. The isometric-contract regex
checks the new key instead of the silhouette construction. docs/ISOMETRIC.md
names the key.

Live 2.5D output is unchanged: the 21 isometric golden scenes pass on the
accepted baselines.

Issue: #724
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
This commit is contained in:
Claude
2026-09-30 22:39:24 +00:00
committed by claude[bot]
parent 108427dc77
commit 5f8e8ca7e8
9 changed files with 193 additions and 182 deletions
+12 -10
View File
@@ -11085,21 +11085,22 @@ const MUTANT_DEFINITIONS = [
{
id: 'iso-placement-cache-survives-silhouette-change',
guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs '
+ '&& node --test --test-name-pattern="#473 W2" test/iso-scene-render.test.mjs',
because: 'placements are cached per wall-silhouette array identity; keying the cache by a '
+ 'constant serves «near the wall» for a plan whose wall is gone (#473)',
+ '&& node --test --test-name-pattern="#473 W2|#724 AC2" test/iso-scene-render.test.mjs',
because: 'placements are cached per structural wall geometry (#724; the wall-silhouette '
+ 'array the id names was built only to be this key); keying the cache by a constant '
+ 'serves a placement, and with it the owner room, of a plan whose walls are gone (#473)',
patches: [{
file: 'src/iso-scene-render.ts',
find: ' let placements = isoOverlayPlacementCache.get(input.wallSilhouettes);',
find: ' let placements = isoOverlayPlacementCache.get(input.structure);',
replace: ' let placements = isoOverlayPlacementCache.get(ISO_PLACEMENT_CACHE_ANY);',
}, {
file: 'src/iso-scene-render.ts',
find: ' isoOverlayPlacementCache.set(input.wallSilhouettes, placements);',
find: ' isoOverlayPlacementCache.set(input.structure, placements);',
replace: ' isoOverlayPlacementCache.set(ISO_PLACEMENT_CACHE_ANY, placements);',
}, {
file: 'src/iso-scene-render.ts',
find: 'export const ISO_OVERLAY_PLACEMENT_CACHE_LIMIT = 2048;',
replace: 'export const ISO_OVERLAY_PLACEMENT_CACHE_LIMIT = 2048;\nconst ISO_PLACEMENT_CACHE_ANY: readonly IsoWallSilhouette[] = [];',
replace: 'export const ISO_OVERLAY_PLACEMENT_CACHE_LIMIT = 2048;\nconst ISO_PLACEMENT_CACHE_ANY = {} as IsoWallGeometry;',
}],
},
// #474: designer furniture artwork is a lazy chunk. Each protective contract
@@ -11395,12 +11396,13 @@ const MUTANT_DEFINITIONS = [
{
id: 'stage3-w5-runtime-nudge-writes-storage',
guard: 'node demo/smoke_isometric_live_touch.mjs',
because: 'W5: runtime nudge is presentation-only and must never write config, layout or '
because: 'W5: the 2.5D overlay scene (the wall-top rise of tiles and lock badges; nothing '
+ 'is nudged since #713) is presentation-only and must never write config, layout or '
+ 'browser storage (#160)',
patches: [{
file: 'src/houseplan-card.ts',
find: ' return runtime.buildIsoOverlayRenderScene({',
replace: " localStorage.setItem('houseplan_stage3_nudge_mutant', space.id);\n"
replace: " localStorage.setItem('houseplan_stage3_overlay_mutant', space.id);\n"
+ ' return runtime.buildIsoOverlayRenderScene({',
}],
},
@@ -11415,8 +11417,8 @@ const MUTANT_DEFINITIONS = [
replace: ' if (!runtime || !layers || (!structural && disp.showBorders)) return null;',
}, {
file: 'src/houseplan-card.ts',
find: ' wallSilhouettes: structural.wallSilhouettes,',
replace: ' wallSilhouettes: structural?.wallSilhouettes ?? [],',
find: ' structure: structural.geometry,',
replace: " structure: structural?.geometry ?? { topPath: '', topFaces: [], sides: [], contactPath: '', edgeCount: 0 },",
}],
},
{