Skip to content

fix(nodes,viewer): let empty and collapsed slabs build without throwing - #920

Open
Aymericr wants to merge 4 commits into
mainfrom
fidelity/a-04-collapsed-slab
Open

Aymericr wants to merge 4 commits into
mainfrom
fidelity/a-04-collapsed-slab

Conversation

@Aymericr

@Aymericr Aymericr commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes B1 from the building-fidelity benchmark (plan item A-04): a slab whose renderable polygon collapses throws in buildSlabGeometry on every frame. The editor and /bake then never settle.

getRenderableSlabPolygon snaps slab edges to wall faces and sibling seams. A door-threshold floor strip about 0.11 m wide that lies inside a wall band seams onto the room floors on both sides, so both long edges project onto the wall centerline and the ring collapses to two points. generateSlabGeometry returns a geometry with no position attribute for that ring. splitSlabFacesByFacing then read position.count (nodes/src/slab/geometry.ts:121) and threw. The geometry system never reached clearDirty, so it rebuilt and threw again every frame, and it also skipped every dirty node after the slab in that frame. On the /next house, 14 slabs hit this.

  • viewer (systems/slab/slab-system.tsx): the existing polygon.length < 3 guard becomes isCollapsedPolygon, which also catches rings with (near) zero area. It applies to the whole polygon and to each region left after hole cuts, for solid and recessed slabs, and to every hole ring. Before this, a zero-area ring with three or more points, or a sliver hole, still grew full-height wall fins.
  • nodes (slab/geometry.ts): following the viewer guard, the builder counts triangles safely on an empty geometry, emits no mesh for a slot that has no triangles, and skips the terrain fill when the slab body is empty. A collapsed slab builds an empty group, the geometry system clears its dirty flag, and the scene settles. The slab node stays in the graph unchanged, and nothing about valid slabs changes.

House effect: native recall does not change, because the benchmark converter still filters these slabs. A-05 will remove that exclusion and measure the recovery.

The failing test was committed first (e422020): 6 of the 11 slab builder tests failed with TypeError: undefined is not an object (evaluating 'position.count') at geometry.ts:121. That includes the raw /next house strip: slab_floor-utility-2, wall_utility-east, and the two sibling floors.

How to test

  1. bun test src/slab in packages/nodes: 44 pass. New cases cover empty, fewer than three points, zero area, the raw collapsed house strip, a hole covering the whole slab, an interior hole and valid slabs, plus repeated rebuild and disposal across collapse and recovery.
  2. bun run test in packages/viewer: 368 pass. New viewer cases: a zero-area ring builds no side-wall fins (failed before the fix), a zero-area interior hole grows no hole-wall fins (failed before the fix: 40 triangles instead of 12, committed first in e50a0ff), and a hole covering the whole slab leaves no triangles.
  3. bun run check: clean.

Proof

Check Result
nodes src/slab 44 pass, 0 fail (before the fix: 5 pass, 6 fail)
viewer src/systems/slab 8 pass, 0 fail (before the fix: both zero-area cases fail); full suite 368 pass, 0 fail
nodes full suite 3490 pass. The failures in that run were timeouts under machine load, in lean-to roof, gutter and hosting-audit files. Each passes alone: canopy-rendering-regression 250/0, roof-corner 24/0, gutter-corner-integration 59/0, unrenderable-host-audit 58/0 + 1/0 over 3 reruns
bun run check Checked 2477 files, no fixes
bun run check-types 9/10 tasks pass. editor (apps/editor) fails only on the uninstalled git dependency @webxr/plugin in the private-editor worktree. That is environmental, and this PR does not touch the app

Browser proof: pending the M5 lock (critical-path reservation). I will add the editor settle and /bake receipts for the raw probe scene here.

Rules R1–R9

Rule Check
R1 No API or schema change. def.geometry stays opaque. The slab builder returns an empty Group for a collapsed polygon, which the generic geometry system already handles
R2 Removes a per-frame throw and a permanent dirty loop. The added work is one O(n) area sum per ring. No new commits, caches or jobs
R3 Unaffected: no references or writers change. Slab node data is untouched
R4 Unaffected: zones and room detection read node polygons, not built meshes
R5 Unaffected: editing tools are unchanged, and a collapsed slab keeps its node, tree entry and 2D polygon
R6 Bake: the empty slab contributes no mesh, and the rest of the scene exports (see browser proof)
R7 Unaffected
R8 Unaffected: no agent tool or check surface changes
R9 Unaffected: capture/import is untouched

Screenshots / screen recording

N/A: this is a geometry robustness fix. The browser receipts for the raw probe are above.

Checklist

  • I've tested this locally with bun dev (probe scene in the hosted editor and /bake, see proof)
  • My code follows the existing code style (run bun check to verify)
  • I've updated relevant documentation (if applicable). N/A here; the private wiki gotcha is updated with the private companion
  • This PR targets the main branch

🤖 Generated with Claude Code

Aymericr and others added 2 commits September 24, 2026 17:17
The /next house door-threshold strip collapses to a two-point line once
its edges seam onto the wall centerline, and buildSlabGeometry throws
reading the missing position attribute (geometry.ts:121). Covers empty,
short, zero-area, raw collapsed, fully holed, interior-holed and valid
polygons, plus repeated rebuild and disposal across collapse.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A slab whose renderable polygon collapses once its edges snap to wall
faces or sibling seams (a door-threshold strip inside a wall band) left
generateSlabGeometry returning a geometry with no position attribute.
buildSlabGeometry then threw on every frame, the geometry system never
cleared the node's dirty flag, and neither the editor nor /bake settled.

- viewer: treat rings with fewer than three points or no area as
  collapsed, for the whole polygon and for each region after hole cuts,
  so a zero-area ring no longer grows full-height side-wall fins.
- nodes: count triangles safely on an empty geometry, emit no mesh for a
  slot without triangles, and skip the terrain fill of an empty slab.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@pascal

pascal Bot commented Sep 24, 2026

Copy link
Copy Markdown

I hit an error while handling your request (Model unavailable on AI Gateway free tier: Free tier users do not have access to this model. Upgrade to paid credits at https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dtop-up for unrestricted…).

Please try again, rephrase, or reach out if it keeps failing.

Error id: 37486602-700a-4643-881c-b20d9f4ab99e

Aymericr and others added 2 commits September 24, 2026 18:18
A 1 m x 0.5 um interior hole passes the point-count filter and emits
double-sided hole-wall quads: 40 triangles where the plain slab has 12.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Holes were filtered by point count only, so a sliver hole below the
collapse area still grew double-sided hole-wall fins. Filter them with the
same collapsed-polygon test as the outer contour.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
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