Dual review pass (Claude multi-angle + Codex release-quality). One
correctness fix and the agreed do-now cleanups:
- fix: clone-scene-graph remaps roofSegmentId like wallId in both
clone paths — duplicated scenes/levels kept pointing roof-hosted
children at the original segments.
- extract the settled, stateless roof target/cursor math shared by the
four door/window tools into shared/roof-wall-opening-placement.ts
(resolveRoofWallOpeningTarget + getRoofWallOpeningCursorPose +
worldToSelectedBuildingLocal); tools keep the stateful lifecycle
(drafts, undo/temporal, commit field lists). −199 net lines.
- rename host-generic state: currentWallId→currentHostId,
markWallDirty→markHostDirty (they hold segment ids too); capability
cascadesViaHostSegment→dirtyHandledByOwnSystem (behavior-facing,
before the public API hardens).
- drop getRoofAccessoryKinds from core's public API — its only caller
was the standalone Build tab, which now enumerates the registry
inline with its app-specific filter.
- window move-tool uses the shared stripPlacementMetadataFlags; stale
"segment-local" comment fixed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Allow template forks to preserve scans
* Add room preset save trigger
* Show room preset action for selected zones
* Use standard inspector for selected zones
* Support room preset editor workflows
* Preview room clear-underneath items
Stair system (full stack):
- New StairNode + StairSegmentNode schemas with flights, landings, L/U-shapes
- StairSystem: geometry generation with throttled per-frame updates
- Stair tool, edit system, panels, tree node, and renderers
- Event bus types, scene registry, and command palette entries
Scene graph utilities:
- cloneLevelSubtree: deep-clone a level with remapped IDs
- forkSceneGraph: clone + strip scan/guide nodes for project forking
Core improvements:
- Read-only mode on scene store (blocks create/update/delete when locked)
- readOnly guards on node-actions and collection actions
- Upload store for scan/guide file upload handling
Viewer state:
- previewSelectedIds for box-select live preview
- hoverHighlightMode (default/delete) for delete-mode hover outline
The original implementation used spread ({ ...node }) which only shallow-copies.
Nested objects (asset, polygon, position tuples, metadata) would share references
between the original and cloned scene, meaning mutations to the clone could
corrupt the original.
Also removed resolveChildRefId helper — children arrays in the schema are always
string IDs, so the object-with-id handling was dead code.