Repository navigation
core: the site comes first — get_site, place_building, one setback envelope - #1044
Conversation
f7b3a45 to
c87f17c
Compare
…d its walls are measured as built From the review of the site placement (#1044): - A house about to rise (no walls yet) is turned square to the street by place_building when no rotationDeg is given; a standing house still keeps its facing. On a turned lot a plan that fits the setbacks only squared now lands in them, and the brief names the rotation as the fix for a standing house only: named for one yet to rise, a Low run read it as the rotation to draw at. - Yards are met together: on a lot askew to the house a front and a side yard move each other, so they are solved as one system, and a pose that misses them is refused. The street's side is read at the rotation the house will have, so yards given with a new rotation land on the right faces and a replay moves nothing. - One front edge for the setbacks, the street and the yards: the recorded front edge, else the first street edge, else the north-facing guess. The 3D site and the site plan read it through siteFrontEdge and siteSetbackEnvelope. - Setbacks of zero exclude nothing: the whole lot is buildable, not "none". - A house bridging a notch reports how far its middle reaches past the lot line, not 0. - create_room and add_wall measure the walls they raise, mitred and curved, against the lot line, and read only the lot to do it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d its walls are measured as built From the review of the site placement (#1044): - A house about to rise (no walls yet) is turned square to the street by place_building when no rotationDeg is given; a standing house still keeps its facing. On a turned lot a plan that fits the setbacks only squared now lands in them, and the brief names the rotation as the fix for a standing house only: named for one yet to rise, a Low run read it as the rotation to draw at. - Yards are met together: on a lot askew to the house a front and a side yard move each other, so they are solved as one system, and a pose that misses them is refused. The street's side is read at the rotation the house will have, so yards given with a new rotation land on the right faces and a replay moves nothing. - One front edge for the setbacks, the street and the yards: the recorded front edge, else the first street edge, else the north-facing guess. The 3D site and the site plan read it through siteFrontEdge and siteSetbackEnvelope. - Setbacks of zero exclude nothing: the whole lot is buildable, not "none". - A house bridging a notch reports how far its middle reaches past the lot line, not 0. - create_room and add_wall measure the walls they raise, mitred and curved, against the lot line, and read only the lot to do it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ast its lot line reported A house is placed on its lot by its building node's pose, never by moving its walls, so the model's coordinates survive and every path (a layout program, a hand trace, a DXF import, the lot drop-in) places it the same way. - Core owns the setback envelope (the edge roles, setbackEnvelope with #1017's straight-front rule, the oriented yards) and the building footprint (levelFootprintLoops, footprintOutline, siteFrame, levelFrame). The editor's site plan, its lot code and the 3D site import them; the 3D copy in nodes/src/site/setbacks.ts is deleted, so the 2D and 3D envelopes no longer diverge. - lib/site-placement.ts: siteBrief reads the site in the building's level frame (the lot, the buildable area and its state: lot / setbacks / none / advisory, its centre, the street side and the yaw that squares the house to it, north, address, parcel, zoning, the new-project placeholder, and the footprint's fit, yards and overhang, judged by area so an edge across a notch counts). placeBuildingPose centres a footprint (an outline about to be drawn, or the walls) in the buildable area, the pole when the centroid will not hold it, or at the yards given; the yaw is kept unless asked, the lot never resized, and a replay changes nothing. - Two shared tools, both surfaces: get_site (read) and place_building (the building node only), with annotations, justifications, the README table and the MCP agent guide. - verify_scene reports footprint_outside_site, footprint_outside_setbacks and buildable_area_none; create_room and add_wall say when what they drew crosses the lot line. - The lot drop-in re-centres a house the new lot leaves outside through placeBuildingPose, keeping its facing (buildingRecentreOffset is gone). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e schema The chat's durable lane serializes the scene snapshot inside a workflow, whose bundle refuses any module that reaches nanoid; the site brief did, through migrateSiteMetadata in the site node's schema. The address, parcel and setbacks schemas and their read-side lift now live in schema/nodes/site-facts.ts (zod only), which site.ts and the brief import, and the brief is a subpath of its own, @pascal-app/core/site-placement. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Low run read "rotationDeg -20 squares the house" as layout()'s rotationDeg, which turns the rooms inside their level, and broke its first plan; the rotation that squares the house is the building's, which place_building sets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d its walls are measured as built From the review of the site placement (#1044): - A house about to rise (no walls yet) is turned square to the street by place_building when no rotationDeg is given; a standing house still keeps its facing. On a turned lot a plan that fits the setbacks only squared now lands in them, and the brief names the rotation as the fix for a standing house only: named for one yet to rise, a Low run read it as the rotation to draw at. - Yards are met together: on a lot askew to the house a front and a side yard move each other, so they are solved as one system, and a pose that misses them is refused. The street's side is read at the rotation the house will have, so yards given with a new rotation land on the right faces and a replay moves nothing. - One front edge for the setbacks, the street and the yards: the recorded front edge, else the first street edge, else the north-facing guess. The 3D site and the site plan read it through siteFrontEdge and siteSetbackEnvelope. - Setbacks of zero exclude nothing: the whole lot is buildable, not "none". - A house bridging a notch reports how far its middle reaches past the lot line, not 0. - create_room and add_wall measure the walls they raise, mitred and curved, against the lot line, and read only the lot to do it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e placeholder's A Pro run read "… on its centre The lot is the new-project placeholder …" as one sentence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
c87f17c to
77931d5
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 77931d5. Configure here.
| const drawing = buildSitePlanDrawing({ | ||
| collections: after.collections, | ||
| installedPlugins: after.installedPlugins, | ||
| materials: after.materials, |
There was a problem hiding this comment.
Lot drop-in skips multi-building recentre
Medium Severity
Lot drop-in recentres an outside house by calling placeBuildingPose without yards. That helper refuses with other_building_on_site whenever another walled building shares the site, so the update is skipped and recentred stays false. A house-plus-garage project whose house falls outside a newly imported lot is left sitting past the property line, with no error.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 77931d5. Configure here.


What does this PR do?
This PR places a house on its lot by moving the building node, never its walls. The model's coordinates stay as written. Every path places the house the same way: a layout program, a hand trace, a DXF import, the lot drop-in.
It is rebased on editor main (
cc978fe8: Bundle 2's #1024, then #1039, #1040, #1041, #1043).setbackEnvelope(with Site: lot as a block in mapped surroundings, straight front setbacks #1017's straight-front rule) and the oriented yards move fromeditor/.../site-plan/geometry.tstocore/src/lib/setback-envelope.ts.levelFootprintLoops,footprintOutline,siteFrame,levelFrame) moves tocore/src/lib/building-footprint.ts.nodes/src/site/setbacks.tsis deleted, so the 2D and 3D envelopes can't drift apart. Its ribbon tests are nowline-ribbon.test.ts.siteFrontEdge(site)andsiteSetbackEnvelope(site)are the one recipe the site plan, the 3D site and the agents use. The front edge is the recorded front edge, else the first street edge, else the north-facing guess.core/src/lib/site-placement.ts(also exported as@pascal-app/core/site-placement):siteBriefreads the site in the building's level frame:lot/setbacks/none/advisory) and centre. Setbacks of zero exclude nothing.squareRotationDegthat squares the house to itplaceholderflagplaceBuildingPosecentres a footprint in the buildable area. The footprint is an outline about to be drawn, or the walls already standing. It tries the centroid first, then the most interior point.describeSiteBriefwrites the same facts as a few lines. The hosted chat prints them at the top of its scene snapshot. The street line names a rotation only for a standing house that is askew.get_site(read).place_building(writes only the building node). Refusals:needs_footprint,other_building_on_site,no_street,yards_conflict,yards_infeasible.verify_scenereportsfootprint_outside_site,footprint_outside_setbacksandbuildable_area_none. Off-centre is never an issue.create_roomandadd_wallreturnsite: { fit: 'outside', overhangM, sides }when the walls they raise, mitred and curved, cross the lot line. Only the lot is read to do it.placeBuildingPose, keeping its facing.buildingRecentreOffsetis gone.SiteAddress,SiteParcel,SiteSetbacksandmigrateSiteMetadatanow live inschema/nodes/site-facts.ts. This lets the site brief run inside a workflow, whose bundle refuses anything that reaches nanoid.Reconciled with Bundle 2
Bundle 2 (#1024) and the editor PRs that landed after it touched the same registration and test files. Nothing in this PR was already there: Bundle 2 has no site placement.
verify_scene: Bundle 2's description, with "a house past its lot line or in its required yards" added to its list. The three site codes sit after Bundle 2's new item and wall codes.ADD_FENCE_CASES.get_siteandplace_buildingbetween Bundle 2'splace_in_roomandsearch_assets, whosecatalog: trueis unchanged.create_room: Bundle 2's room-name import besidelotOverhangandlevelFootprintLoops.add_wall: Editing foundations: roofs on the building's roof level, add_fence shared, corner-window editing and review fixes #1039'sisRoofLevelbeside them.aboveGradeLevels) and its roof-level storey count, with the footprint now read from core.get_siteandplace_buildingare not inrun_batch's list (BATCHABLE_TOOLS). That list belongs to Bundle 2, and both tools work on their own.How to test
bun test packages/core/src/agent-operations. The tables in__fixtures__/site-cases.tsalso run on the MCP (packages/mcp/src/tools/shared-tools.test.ts) and in the hosted chat. They cover get_site, place_building, the lot-line flags and the verify_scene issues:Screenshots / screen recording
Verified end to end through the hosted app in pascalorg/private-editor#910, which has the screenshots. Those runs used this code before the rebase; the hosted PR now pins this branch's head:
get_siteand calledplace_buildingwith the outline before its first wall. The house was centred on the lot when its walls first went up.Review
The pascalorg/private-editor#910 reviews covered this code. On the editor side:
Fixed
siteSetbackEnvelopelike the site plan.create_room/add_wallmeasure their walls' mitred, curved bands, reading only the lot.Rejected
which_buildingfor the tools' ambiguous case. The tools resolve the building throughtargetBuilding, the helper every building-targeted tool shares, which refusesbuilding_required. Renaming it would give one case two names.Checklist
bun dev(through the hosted app's dev server, before the Bundle 2 rebase)bun checkto verify)mainbranchChecks run on this branch (rebased on editor main
cc978fe8), from the hosted worktree:check-typesfor every editor package: 9 of 9.bun testfor core, mcp, the editor's site-plan and lot code, nodes' site code and the tool-annotation policy: 5475 pass, 0 fail. Themetadata references (R3)failure is gone; fix(core): the embed page's robots metadata is Next.js page metadata #1045 fixed it on main.🤖 Generated with Claude Code
Note
Medium Risk
New shared placement/setback geometry affects editor, 3D site, and agent tools; incorrect math would misplace buildings or miss lot violations, though coverage is heavy on fixtures.
Overview
Adds site-first agent workflows: new read tool
get_siteand write toolplace_building(building pose only; walls keep level coordinates), backed by shared core librariessite-placement,building-footprint, and a unified setback envelope insetback-envelope.Consolidates lot geometry that was duplicated across editor site-plan, 3D site renderer, and nodes—editor/nodes now import
siteFrontEdge,siteSetbackEnvelope, footprint loops, etc. from core; deletesnodes/src/site/setbacks.tsandbuildingRecentreOffset. Site address/parcel/setback schemas move tosite-facts.ts.Surfaces lot compliance in
verify_scene(footprint_outside_site,footprint_outside_setbacks,buildable_area_none) and increate_room/add_wallvialotOverhangon mitred wall bands. Lot drop-in re-centres withplaceBuildingPosewhen the footprint is outside the new lot.Large
site-casesfixture suite exercises placement, yards, rotated parcels, and refusals; MCP README and agent guide updated.Reviewed by Cursor Bugbot for commit 77931d5. Bugbot is set up for automated code reviews on this repo. Configure here.