Skip to content

fix(editor): scope slab room paint to the clicked slab's level - #592

Merged
Aymericr merged 1 commit into
mainfrom
fix/slab-room-paint-level-scope
Aug 4, 2026
Merged

fix(editor): scope slab room paint to the clicked slab's level#592
Aymericr merged 1 commit into
mainfrom
fix/slab-room-paint-level-scope

Conversation

@Aymericr

@Aymericr Aymericr commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Room-scope paint on a slab spreads to slabs on other levels. In a two-storey building — where stacked floors share a footprint by definition — painting a ground-floor slab also repaints the slab directly above it.

Found while reviewing #554; this one is on main today and unrelated to that PR.

Cause

resolvePaintScopeTargets gates on space.levelId in every wall path — resolveWallPaintSpace (paint-scope.ts:169), connectedExteriorBoundaries (:205), wallTargetsForBoundaries (:276). The slab path gated on nothing: it picked the first space whose polygon contains the slab's centroid, then fanned out to every slab whose centroid falls in that polygon, regardless of level.

Since space polygons are per-level footprints and storeys stack, both lookups match the wrong level as readily as the right one.

Fix

Gate both the space lookup and the fan-out on the clicked slab's level, using node.parentId ?? resolveLevelId(...) — the same definition of level membership space-detection uses (space-detection.ts:1532 filters a level's slabs by node.parentId === levelId).

Why no test caught it

The existing slab() fixture built nodes without parentId:

function slab(id: string, polygon: Array<[number, number]>): SlabNode {
  return { id, type: 'slab', polygon } as unknown as SlabNode
}

So the level dimension was invisible to the whole slab suite. Real slabs always carry a parent — packages/nodes/src/slab/tool.tsx:72 calls createNode(slab, levelId), and reconciliation creates them with { node, parentId: levelId }. The fixture now takes a level (defaulting to 'l1', so existing cases are unaffected).

Added slab room stops at the level boundary: two slabs with identical polygons on l1 and l2, one space per level. Verified it fails against unpatched paint-scope.ts (["ground:surface", "upstairs:surface"]) and passes with it.

Only wall and slab declare capabilities.paint.roomScope, and walls were already correct — so this was the only affected kind.

Gates

check 1600 files clean · check-types 9/9 · test 12/12, paint-scope 27 pass

🤖 Generated with Claude Code


Note

Low Risk
Localized fix to paint target resolution with a regression test; no auth, data, or API surface changes.

Overview
Room-scope slab painting no longer bleeds onto slabs on other storeys when floors share the same footprint.

resolvePaintScopeTargets now resolves the clicked slab’s level (parentId ?? resolveLevelId) and uses it for both the space polygon lookup and the fan-out to other slabs—matching how wall room paint already gates on levelId.

Tests: the slab() helper sets parentId (default l1), and slab room stops at the level boundary asserts stacked l1/l2 slabs with identical polygons only paint the ground slab.

Reviewed by Cursor Bugbot for commit cd7f703. Bugbot is set up for automated code reviews on this repo. Configure here.

Space polygons are per-level footprints and stacked storeys share a
footprint, so the upper slab's centroid sits inside the ground floor's
space polygon. Room-scope paint on a ground-floor slab therefore also
repainted the slab directly above it.

Every other branch in resolvePaintScopeTargets already gates on
space.levelId (resolveWallPaintSpace, connectedExteriorBoundaries,
wallTargetsForBoundaries); the slab branch was the one that did not.
Gate both the space lookup and the fan-out, matching how space-detection
itself defines level membership (node.parentId === levelId).

The existing slab fixture omitted parentId, which is why no test caught
this — real slabs always have one, from both the slab tool and
reconciliation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Aymericr
Aymericr merged commit 3e46604 into main Aug 4, 2026
2 checks passed
@Aymericr
Aymericr deleted the fix/slab-room-paint-level-scope branch August 4, 2026 23:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant