Skip to content

feat: Bundle 2 editing, interior captures and native materials - #1024

Open
AxiomeCG wants to merge 77 commits into
pascalorg:mainfrom
AxiomeCG:feat/bundle-2-delivery-20261010
Open

AxiomeCG wants to merge 77 commits into
pascalorg:mainfrom
AxiomeCG:feat/bundle-2-delivery-20261010

Conversation

@AxiomeCG

@AxiomeCG AxiomeCG commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Bundle 2, editor half. Point and ask, building feedback that follows the work, steadier keyboard and audio, sturdier walls, slabs, ceilings and roofs, interior captures, persistent native materials, and the shared tool contracts that the hosted chat and the MCP server both use.

Ready for review. The owner marked it ready after testing the bundle on a server built from the PR heads, and is testing these exact heads on that server now; Wassim merges. The hosted half is pascalorg/private-editor#877, whose editor gitlink pins this PR's head (cad1839f).

Only bundle 2 is in this PR. A bundle PR contains only the features selected for that bundle (the 29 on the delivery board). Everything else stays in the lab (R&D) branch for a later bundle. The lab families that had been carried here by default were taken out on 13 October; they are listed below under "Not part of bundle 2".

Size: 66 commits plus 6 merge commits, on top of upstream main (80d7a2f, #1033, as last fetched; re-check before merging): 400 files, +40,358 / −920 lines. About 18,000 of the added lines are tests and fixtures. (Figures as of cad1839f; before the scope trim the PR stood at 489 files, +59,777.) Upstream main is merged in twice, never rebased: 86f8e83 (#1026) earlier on 12 October and 80d7a2f (#1033, Blender bpy modules) that night, so this PR merges cleanly with main as of then.

What a person gets

  • Point and ask (the editor's half). Point at an element or region, ask for a change, compare before and after (hold to compare, and a before view in 3D), ask for more, Undo/Redo. The card closes when the pointer is on neither it nor the pin. Room suggestions come from what the room contains. A host can say "Pascal is working", and an ask sent meanwhile is handed to the chat, which queues it. A pin that never heard its turn end is settled when the chat goes idle, and a turn's changes go only to the pin of the ask that is running. A scene preview shows another state of the document without writing it, and autosave stands still while one is held. The standalone editor gains no hosted chat; the host provides the handler.
  • Follow Pascal and construction feedback. The camera follows the build onto the target floor and keeps the target in sight; a manual camera move pauses it. When Follow ends it puts back the cutaway, the floor and the hidden floors it had changed. Construction reveal, reverse playback on Undo and animated updates to existing pieces, including changes from a hosted MCP agent.
  • Keyboard and audio. Group, editor, move and draft shortcuts stay out of text fields; editable keys survive group rotation. Construction sounds respect the calm and playful controls.
  • Walls, slabs and ceilings. A wall authored at storey height binds to the covering slab's datum, and scene verification reports wall contacts with covering slabs. Ceiling-mounted scripted objects are anchored at the top; their bounds are checked above attachments and inside slabs. Floor underlays and authored footprint bounds are honoured. verify_scene also reports an object that is wholly or partly inside its wall (item_wall_body_overlap), one that goes through the wall (item_through_wall) and one that faces the wall (item_faces_wall); a new back-side wall object with no rotation now defaults to a half-turn yaw (the old zero default turned it to face the wall, which z-fought the cinema screen).
  • Furniture. Wall-hosted furniture stays planar to its wall face. A new advisory lists wall-bound pieces standing away from every wall. Finishes of a room and its walls can be read back.
  • Windows and rooms. Windows are centred when no sill is given; corner windows place, snap and merge; storey heights are coherent; named rooms get no duplicate zones and are not silently renamed.
  • Interior captures. view_scene can show the inside of a chosen storey. It waits for the scene to finish building and sets aside nodes no system will settle, then restores the normal view.
  • Persistent native materials. Create a named material, apply it to supported native surfaces, reuse and read it back, save/reload, Undo/Redo. Ceiling paint keeps existing properties; identical paint reports zero changes; an invalid target fails with no partial paint and no orphan material. No generated textures.
  • Doors and windows. A door or window that carries clips draws its own leaves instead of a static batch instance, the walkthrough operates a door or window built from a script through its mechanism, and an open scripted door can be walked through (upstream main causes, fixed here).
  • Roofs. A roof segment's fascia takes a height, a depth and its own finish. A roof outline's sharp corner is bevelled past a mitre limit instead of reaching out without bound (857016ea, from Bugbot).
  • Blender (bpy) modules, from main (Blender (bpy) modules as a second geometry-script language #1033). Merged in. The compile refusals of this PR sit in the shared finishBuild, so a bpy build gets the same non-finite-transform refusal and "no visible meshes". add_object's unmeasurable_object, scripted-door clips and walk-through, and the wall-object observations read the manifest and the loaded model, not the language. A test commit (6be2b6d9) pins this for bpy builds. Where main's tool text gave a method order ("read it first with get_source", "fields first; code only beyond them"), this PR keeps its factual wording and adds main's facts about bpy.
  • Smaller repairs. add_object refuses an object it cannot measure (unmeasurable_object) before building its asset; the geometry script's compile fails on a mesh at a non-finite position and names the mesh; the room labels' timers are called as functions, not as methods of the placer (an upstream main bug, kept as its own commit 9a1375c5 because it was asked for in review). Plugin nodes are validated and respect vertical collision bounds; the geometry script compiles consecutive imports on one line; scene-tool evidence and batching are optional in core.

Not part of bundle 2 (kept in R&D)

Removed from this PR on 13 October, one commit per family, because they are not among bundle 2's selected features. They stay in the lab branch for a later bundle. Anything already on upstream main stays.

  • Photo location (locate_photo), 4cc6ee74. The photo tool straighten_facade_photo stays.
  • The scene coherence checker (dispute_coherence_item and the checker behind it), 60b78690. verify_scene, validate_scene and validate_design are main's and stay; new checkpoints record no checker issues.
  • Site works (add_site_surface, add_steps, this PR's add_fence), dc015331. add_fence is main's again.
  • Apartments, units and circulation (propose_unit_layouts, apply_unit_layout, name_units, create_stairs_and_lifts, circulation and reachability), 56b7b1b9. main's unit tools stay.
  • Plan reading and reference construction (plan reference import, calibration, survey, matching, correction, reference elements, vectorising, furnish-from-plan, and the plans guide), bbb4e1b6.
  • request_upload: its hints and the resolver type, 95813f7a.
  • Visual reference search contracts, 00c76f05.
  • Modules nothing imports after the above, cad1839f.
  • The facade family, removed earlier (e9a36f6a): its nodes, schemas, tools and prompts. straighten_facade_photo stays.

Tests trimmed (da081b46). Two independent audits read every test this PR adds. Where both named the same blocks as pinning implementation details rather than behaviour (animation feel, copied constants, pixel placement, copy text), those blocks went: 11 files, −666 lines. No test that guards a fix made on 12 October was dropped.

Bugbot, round 1

  • Roof outline mitre can explode: real, fixed in 857016ea (see Roofs), with tests in create-roof.test.ts.
  • Short landing still builds the stair: real, but the tool has left this PR with the apartments and circulation family; the fix is written and tested separately for when that family ships.

Tests and checks on this head (cad1839f)

Run on 13 October on cad1839f, from the hosted PR worktree, with the editor built.

Check Result
root bun run check-types (turbo) 13 of 13 tasks green
core tests 4268 pass, 0 fail
geometry-script tests 49 pass, 0 fail (includes main's Blender tests)
nodes tests 4791 pass, 1 skip, 0 fail
viewer tests 665 pass, 0 fail
mcp tests 777 pass, 0 fail
editor package tests 2419 pass on the tracked test files (the one failure in the local run is an untracked file that is not part of this PR)
bun run skills:validate 69 pass (run on 00c76f05; cad1839f only removes modules nothing imports)

Carried from c9f54660, not re-run on this head: cli 107, ifc-converter 92 and editor app 51 pass; bun run check (biome) exit 0 with warnings only. Not run for this description: turbo run build, browser/e2e tests, GPU capture checks.

The viewer test "pins the vendored closure to the installed R3F version" (packages/viewer/src/lib/pointer-events.test.ts) passes here, where @react-three/fiber resolves to 9.8.1. It used to be red in the hosted repo (and on hosted main), whose lock resolved 9.6.1; #877 now pins 9.8.1 with a root override, and it passes there too.

Seen, not in this PR

Upstream main issues found while testing this branch. They are not fixed here; they are listed so they are not lost.

  • view_scene timeout contract. The hosted tool call gives up after 60 s without cancelling the capture, the view tool waits 75 s for its snapshot, and the capture's own budgets allow 120 s, so late capture work outlives the error. One deadline and real cancellation would fix it.
  • Capture pipeline lifetime. In thumbnail-generator.tsx, a capture can run on a pipeline that was cleaned up while it awaited; a version check right before each capture closes that gap. Possibly linked to an "uncaptured WebGPU error: destroyed texture used in a submit" seen in the browser; not proven.
  • Shared studio HDR. The hosted item preview, thumbnail modal and auto-thumbnail share one HDR, and drei's Environment disposes it on unmount, which can leave the live scene with a destroyed PMREM.cubeUv texture. The hosted procedural-items/preview.tsx already clones and disposes its own copy; the others could do the same.
  • Pool plugin dirty mark. The pool plugin never clears its dirty mark, so a capture waited for the scene to finish building for ever. This PR's capture no longer waits on nodes no system will settle; the plugin-side patch is with Sudhir.

Not in yet

  • The lab families above (kept in R&D) and facades.
  • A wall readback that gives the finish actually visible on each face, including a room's override of that face, is written and tested but held for the next bundle.
  • Nothing else is known to be pending on the editor side. Nothing in this PR needs a migration, a credential or a paid service.

Before merging

  • Bugbot's threads resolved on the exact PR head, and CI green on it.
  • Re-check that this PR still merges cleanly with main.
  • Carried over from the earlier description and not re-checked at this head: transient corner-placement HUD state should not live in Core.
  • If this PR is squashed, update the hosted PR's gitlink to the resulting public commit before that PR merges.

How to test

Use a disposable scene. Chat scenarios run in a compatible host and may use normal model credits.

Scenario What should happen
Edit a window or room Place or merge a corner window, then Undo. An explicit sill wins over centering. Creating a room over existing walls adds no duplicate zone and does not rename a person's room.
Ask for a local change Point at an object or region, ask for a small change, compare, Undo/Redo, Change more. The request keeps its intended scope.
Watch a build, then reverse it The camera follows the build; Undo plays it back. Typing in a field is not taken by shortcuts.
Walls against slabs Author a storey-height wall on a slab; it binds to the slab datum and the scene check reports the contact.
Open a scripted door A door built by the agent with an open clip slides open from the action menu or E, and in the walkthrough; an open one can be walked through, a closed one blocks.
Follow Pascal ends After a build Follow had shown in cutaway, on another floor, the view, the floor and the hidden floors come back as the person had them.
A screen on the back of a wall Add a wall-side object on the back of a wall with no rotation: it faces into the room, and verify_scene stays quiet. Push an object into its wall: the check names it.
A roof on a sharp corner Roof a footprint with a very sharp point: the roof stays within about a wall's width of the corner.
Look inside a storey Capture the interior of a two-storey scene with a roof, then an ordinary view; normal presentation returns.
Create and reuse a finish Apply one named native material to two surfaces, save/reload, Undo/Redo. Repainting identically adds no change; an invalid target leaves nothing behind.
Only bundle 2's tools The agent's tool list has no plan-reading, unit-layout, coherence-dispute, site-works, upload or visual-reference tool, and a full build completes without asking for one.

Screenshots / screen recording

A recording of the final pair is still needed.

Checklist

  • I've tested this locally with bun dev (the owner is testing these exact heads on a local dev server)
  • My code follows the existing code style (bun check exited 0 on c9f54660; tick once it passes on the final head)
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

No merge, deployment, migration, credential change or paid model run is part of this PR.


Note

Medium Risk
Touches core agent operations, scene apply/merge semantics, and layout verification used by MCP and chat; behaviour changes (refusals, room zones, window placement, rotations) are broad but heavily fixture-tested.

Overview
Agent tools and scene writes gain add_corner_window, merge_windows, create_roof, run_batch (ordered calls merged into one undo step, including scene materials), plus stricter material ref checks and session checkpoint/keep wiring in apply-outcome.

Scripted geometry is rejected up front via requireMeasured (unmeasurable_object) when compile bounds are non-finite (NaN or null after JSON), for add_object, columns, and door/window paths—instead of building assets or surfacing raw schema errors.

Rooms and openings: create_room reuses zones from existing walls, refuses silently renaming person-named rooms unless rename: true, and windows from room creation are centred on storey height rather than a fixed sill. Wall-side items default back-side mounts to π yaw so they face into the room.

Layout and verification expand: plan footprints use script manifest bounds (offsets/scales/rotation), floor rugs/underlays stop blocking placement, vertical separation between ceiling and floor items, item_mid_room for wall-bound furniture away from walls, ceiling–slab overlap observations, and wall-hosted overlap/facing checks (cinema-screen fixture). Read APIs expose wallFaceChains and zone wall intent; patches that set fields to undefined remove optional keys like the store.

Tests/fixtures add large declarative tables for corner/merge windows, walled create_room, finish readback, native paint contract helpers, and many layout-clearance regressions.

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

AxiomeCG and others added 30 commits October 10, 2026 04:40
…61010

Brings in pascalorg#1021 (dependency upgrades) and pascalorg#1026 (plugin agent tools:
AgentToolContract, AgentOperations, PluginChatExtension, registerOperationTool).

Conflicts, all in the agent tool registries pascalorg#1026 reshaped:
- core/agent-operations/index.ts: keep the bundle's operation list and imports;
  AGENT_OPERATIONS (the export that carries run_batch) is `satisfies AgentOperations`.
- core/agent-tools/index.ts: keep the bundle's contract list and exports;
  the list is `satisfies readonly AgentToolContract[]`; export ./contract too.
- mcp/tools/shared-tools.ts: main's exported registerOperationTool and
  OperationTool are the shape; the bundle's per-call logic (plan judge and
  vectoriser hosts, agent session, native materials, run_batch place_in_room
  catalog, marked-plan image) folds into it, behind a trailing `hosts`
  parameter. agentRuntime also writes collection changes, and a call persists
  on main's `writesScene(changes) || afterReconcile` (collection-only outcomes
  now publish a snapshot); the bundle's checkpoint branch stays after it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… already calls

reference-construction.ts imports planPreviewSvg from @pascal-app/core/building
and draws an imported SVG plan with it (the picture the chat's import also
returns), but the helper and its export never came across, so the MCP build
failed. The helper redraws the plan from the shapes and texts the import read,
never from the agent's SVG, so an agent's markup is not handed to the host's
renderer. Its two dependencies (markedPlanSvg, referenceContours) were already
in the branch.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Main removed the 'zone' action-menu control and narrowed Phase to
'site' | 'building' (pascalorg#1019). The Point button was anchored on the zone
control, so it would never have rendered; it now sits after Select, before
the measurement control, where it stood. The point pick's phase type is the
same two values; a building or site is still pickable only in the site phase.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The shared tools already register create_roof from the one core contract and
operation, and construction-tools.ts still registered main's own copy, so every
MCP server threw "Tool create_roof is already registered" at start-up. Remove
the second registration and its schemas, and move its tests to the shared
contract (the answer lists segmentIds; with no arguments the roof covers the
rooms a story shell derived).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…own finish

create_roof already advertised fasciaHeight and fasciaMaterialPreset (the shared
contract came across with the operation), but the roof-segment schema had no
such fields and parsing dropped them: a flat roof asked for a 0.3 m black band
got the plain 1x8. Port the schema half of the lab's change so the contract
means what it says.

- RoofSegmentNode: fasciaHeight, fasciaDepth, fasciaMaterial, fasciaMaterialPreset;
  getSegmentFasciaMaterial reads the finish (the trim slot, else the wall role's).
- The viewer sizes the boards from them; with a height set, a flat roof's boards
  rise to its top, a coping band over the deck and membrane edges.
- The segment panel gets a Fascia toggle and a height.

Not included: the lab's second half of this change (place_design by preset name).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…arries

The branch brings the facade and panel node schemas and the core facade
operations that write them, but not their node definitions, so builtinPlugin
registered 2 fewer kinds than AnyNode held (main's registry invariant) and the
facade/panel fixtures and frozen corpus did not cover them. This is the data
model only, not Facade Studio:

- nodes/src/facade: a data-only, hidden, non-duplicable definition.
- nodes/src/panel: the flat cladding node (geometry, floorplan, paint, slots).
  Its box comes from viewer's buildPanelGeometry and core's panelDepthOffset.
- fixtures: facade's required `set`, and the frozen pre-host-children corpus
  gains a facade and a panel row.
- reference inventory: the facade/panel reference fields and the metadata keys
  the branch's sources read (facadeOwner, facadeReleasedFrom, balcony*, plan
  reference keys, guessed.unitId...). Rows for floor-plan-import metadata the
  branch does not ship are left out, so no row is stale.

If facade tools do not ship in bundle 2, this commit and the facade/panel
schema go together.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… to date

- mcp read-tool-annotations + plugin-evals/tool-annotation-justifications.json:
  the 32 tools the shared tools now serve are classified (the packet was main's).
- mcp server.test: instructions are the host's line at the end of the built-in
  guidance, not the whole text; the plan/photo and upload wording is checked.
- mcp measure-world: the node union has a facade.
- core operations.test: the circulation check names a stair with no landing in
  the bare scene; the design targets must stay informational, which is what the
  test is for.
- editor history probe: stub useThree, which Door/WindowSystem now call to
  register their materials for scene capture (this probe fails the same way in
  the lab tree).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…alFromPresetRef

The panel host system subscribes to wall rebuilds, which the viewer notifies
but did not export; the segment renderer's fascia finish needs the preset
material helper it was not importing.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Owner decision: facade sets, their tools and nodes stay in R&D. Remove what the
branch carried of them, so nothing references code that is not shipped.

- core: agent-tools/facade.ts (preview_facade_unit, apply_facade, apply_facade_set,
  describe_facade), the facade operations (result, set-operation, targets) and
  describe_facade's operation and batch entry; building/facade*.ts and
  systems/facade/*; the Facade and Panel node schemas and their places in
  AnyNode, building.children and wall.children; the facade exports of the
  schema, building and core indexes.
- mcp: tools/facade.ts (the editor-tab facade tools) and describe_facade's
  shared-tool registration; the four tools leave the annotation test and
  plugin-evals/tool-annotation-justifications.json.
- nodes, viewer: the facade/panel node definitions registered earlier, the
  panel geometry and the wall-rebuild subscription they used.
- prompts: the plans guide no longer names a facade step (5) or apply_facade;
  create_reference_elements, correct_plan_reading, circulation and site-works
  text stop naming facade tools and pieces.
- verify_scene's `photo` input is dropped: its check (photo_facade_mismatch) is
  the facade checker and was never in the branch, so the input did nothing.
- reference inventory: facade/panel rows and the metadata keys only they read
  leave; rows for the plan-reference, balcony, coherence and fascia keys the
  branch's sources do read are classified.

Kept on purpose, and named in the PR description: straighten_facade_photo (a
photo-measuring tool paired with locate_photo, not a facade-set tool) and
create_reference_elements' outlineSteps value 'facade', the default, which leaves
a balcony step unbuilt and names the wall behind it (balconyWalls).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…erve

Brings scripts/openai-tool-annotation-policy.ts in step with
plugin-evals/tool-annotation-justifications.json (28 tools the branch registers
that main's list did not know), without the facade tools.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The hosted chat's unit-layout judge reads it from @pascal-app/core/agent-operations
(the lab exports everything from unit-layouts); the branch exported only
proposeUnitLayoutsInScene.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ut writing it

beginScenePreview(snapshot) puts a past state on screen (the scene as it was before an agent's
turn, from the history Undo would restore) until the handle ends. History is held and the scene
is read only for its whole life, the swap is a remote change that announces no commit, and only
the nodes that differ are marked for a rebuild. Anything about to write the scene (an agent's
write, a collaborator's patch, a snapshot, a load, an undo or redo) ends it first through a
write guard, because a write onto a preview would be lost or land on the wrong state.
isScenePreviewing and subscribeScenePreview tell whoever persists the scene to stand still.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 1533e9a73adf7c9b4fdf66abde7f8bebb30f470d)
(cherry picked from commit b11f136ac346be38e21f850404d44c53c5241f0b)
A preview is on screen, not in the document: the subscription ignores it, a save that comes due
under it waits and is written from the live scene when it ends, and the exit flush and the save
shortcut end it first so what they write is the live scene.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 8ccb1d665897b780be5e86a0eafb638d1f1d6e94)
(cherry picked from commit 75255635fbbdc3bea96db799a2b11fa622d4b2d3)
…efore in 3D

"Change more" replaces "Hold to compare": it reopens the bubble on the same elements (a fresh
view and picture, the field focused, chips that move on from the words just sent) and sends as
its own ask; the earlier pin gives way and goes when the new ask is taken. The card's pictures
shrink to one small after thumbnail, and holding "Hold to see before" on the card, or the pin,
puts the scene as it was before that turn on screen in 3D (the history Undo would restore) with
a small "Before" tag, and lets go on release. It ends on the pointer leaving, a key, the window
losing focus, the page going away or another turn starting, and writes nothing.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 007dbc2950743d838c604f3d4357ae3b9ec214ae)
(cherry picked from commit 3b054bc1da59fc6f3e2b4fea528fedf453ee0304)
…ther it nor the pin

A card that closed under the pointer (a click on Redo or Keep) never heard its leave, so its
"pointer is on me" flag stayed up and the next card never closed on its timer; the flag is
reset when the card changes and the browser's :hover is asked as well. A card's close timer also
shut the card a pin hover had just opened when the pointer went from the card to the pin, and a
card carried from under a still pointer stayed for good: it now waits 6 s for the pointer. The
leave of a pointer that walked out arrives before its move, so a carried card is told by
position (the leave reports where the pointer is, against where it was last seen), not by time.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 211b55e06ede6d30a248eda0f0206dce8b541642)
(cherry picked from commit a943bfb0b812b3260624b66de43b2968f13b56f5)
…ge more starts empty

The tag sat under the level selector; it now sits below it. "Change more" starts from an empty
field, and the words of an ask that was sent are no longer kept as a draft for the next time the
same element is pointed at (only words that were dismissed unsent are).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit f4b64a95fb9002d14cbeae49c781a0b7dc4c6323)
(cherry picked from commit c260874be36f76bb6bc42f514edb5387589d27d2)
(cherry picked from commit 6f9424f7d5f82a31b8934e7992acf0249eaed21c)
(cherry picked from commit 0a8c657dfe3828aa42410dbca3637a3aba8d872f)
(cherry picked from commit e4b220feb128dc5824b094241fc20e66f58d4eb0)
(cherry picked from commit cd91545d5a362e7d59164a5824f50e96168eb1d1)
(cherry picked from commit c1e2dc5f64ca7bbc4efee4488910ad0b501e8cec)
(cherry picked from commit 3a73b74cf0051f1f127a3931045e4bfd9945c537)
(cherry picked from commit e11ee4094bc3b0d4df16bfa90bc0c84ade59875d)
(cherry picked from commit 4120cf640eb93ec2b9fd5952252ca94b6ab2e4d0)
…s its before at the ask

The hold did nothing, silently, after an agent turn. Three causes, each with a test:

- A refused hold said nothing. `compare.start` now records why (a turn is writing, history cannot
  show the before, another change holds the scene) and the card says it for a few seconds.
- A pin that said "working" while the chat was idle, or one still "sent", shut every hold for good.
  Only a pin that is working while the chat is busy counts as a turn writing.
- The before was the scene at the chat's first "working", which comes from the stream and can
  follow the turn's first write. It is taken when the chat accepts the ask (an ask the chat queues
  is marked when its own turn starts).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…th its own pin

- `PointAskSubmitResult` gets `code: 'busy'`. While the host says Pascal is working, the bubble's
  button says so and does not send, and `sendBubble` keeps the words with the reason.
- `retryPointAsk(askId)`, next to `setPointAskBusy`: the chat's Retry on an ask sends the same
  words, targets, camera and crop as a new ask with its own pin. A finished ask keeps its pin (its
  compare and Undo stay); an errored one is replaced, as the pin's "Try again" does. It answers
  false when the pin is gone, and the host sends the words as a plain message.
- The pure part of the retry is `planRetry`, tested without the viewer.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
AxiomeCG and others added 7 commits October 12, 2026 20:45
…tor branch

A door or window with clips stays out of the static batch, and the walkthrough runs a scripted
door or window through its mechanism (b6be116, f9ca67e, 6dbfbad).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
verify_scene reports item_wall_body_overlap, item_through_wall and item_faces_wall, and a new
back-side wall mount with no rotation defaults to a half-turn yaw (5417caf). Merged with the
add_object refusal of an unmeasurable object (8e76db8): both are kept.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e walkthrough

The walkthrough stops the player at a door with a stand-in leaf box at the door's frame, built
from the node's own fields, and drops it when the door is open: `operationState` for a sliding
door, `swingAngle` for a hinged one, set by the native tween, whose end rebuilds the collider
world. A door built from a script is opened by its model's `open` clip through its mechanism,
which moves neither field and ends in no event. So its leaf box stayed in the doorway whatever
the leaves did, and nothing rebuilt the world when it opened.

This became visible with f9ca67e and is a regression of it in effect: before it, E on a
scripted door ran the native swing, which drew nothing but did move `operationState`, so the
collider dropped and the doorway was passable while the leaves stayed shut. Since it, E runs the
clip: the leaves open and the doorway stays blocked.

Two changes:
- the leaf box of a scripted door is not built while its mechanism is on (open). Closed, it is
  the same box a closed native door gets. The model's own meshes never collide (the door's
  subtree is not swept), the `cutout` box included.
- the walkthrough rebuilds the collider world when a mounted scripted door opens or closes
  (`watchScriptedDoors`, on the interactive store), as it does at the end of a native tween.

Timing differs from a native door's: the rebuild is at the toggle, not at the end of the clip,
so the doorway is passable as the leaves start to move (about 1.3 s early) and solid as they
start to close.

Tests: the collider world with a scripted door, closed (the player is stopped by the 0.06 m leaf,
not by the 1 m cutout box) and open (nothing is hit), red before; the watcher asks for a rebuild
on open and close and for nothing else, red with the subscription off.
Scripted windows are not covered: their model meshes go through the generic sweep.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…tor branch

An open scripted door can be walked through in the walkthrough (f9333f8); it also fixes a
regression the previous door commit f9ca67e introduced.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…scalorg#1033) into the bundle 2 editor branch

Conflicts and what was kept:

- packages/core/src/agent-tools/add-object.ts: main's `bpy` paragraph in the description is
  taken whole; the `Edit:` line stays ours (states that get_source provides the current code and
  params, where main says to read it first). get_source's description keeps ours (optional
  evidence, an edited module can be passed back) and takes main's "three.js or Blender bpy,
  named by language".
- packages/core/src/agent-tools/wall-openings.ts: ours (a module is available, native fields and
  code express the requested shape), with main's mention that the module may be bpy by language.
  The two sentences "Fields first; code only beyond them" and "the fields cannot express" stay out.
- skills/pascal-3d/SKILL.md: ours (the editor-tab settings and refusals, `editor_tab_busy`), with
  main's bpy sentence and "Blender modules run in its Blender service and need no tab".
- skills/pascal-3d/references/tool-workflows.md: ours (the sections after the scripted-object
  paragraph that main does not have), with main's parenthesis that a bpy module builds without a tab.
- wiki/architecture/authored-objects.md: both: our normalized-origin table, then main's
  "Blender modules" section.

Both languages end in `finishBuild`, where the compile refuses a non-finite size naming the
mesh and its component; add_object's `unmeasurable_object` reads the manifest, whichever
language built it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… Blender build too

Both languages end in `finishBuild`, so the compile refuses a mesh at a position that is not a
number, and an empty object, the same way for a bpy build (checked through finishBuild with
language 'bpy', since a NaN transform cannot be made in a Blender export). The artifact carries
its language. A door whose source is bpy follows its mechanism in the walkthrough's collider:
solid closed, passable open.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

detail: `${rect.long.toFixed(2)} × ${rect.short.toFixed(2)} m holds neither a ${MIN_FLIGHT} m switchback nor a straight flight of ${Math.round(getStoredLevelHeight(lower) / RISER)} steps.`,
})
break
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Short landing still builds stair

Medium Severity

A switchback whose leftover landing is shorter than MIN_FLIGHT is pushed into skipped as short_landing, but the stair is still created. core_too_small and already_built actually skip; this path only warns while still writing the stair.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6be2b6d. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed: the stair is created and the short landing is only warned about under skipped. This tool has left this PR with the rest of its family (apartments and circulation are not part of bundle 2, removed in 56b7b1b), so it is not changed here; the fix (the warning moves to its own shortLandings list, and skipped keeps only cores that got nothing) is written and tested separately for when the family ships.

// Along the bisector, as far as both edges move by `distance`.
const scale = distance / (1 + dot)
return [point[0] + sum[0] * scale, point[1] + sum[1] * scale]
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Roof outline miter can explode

Medium Severity

grown offsets each corner with distance / (1 + dot) of adjacent unit normals. Near-reversing edges make 1 + dot ~ 0, so the roof outline becomes huge or non-finite before segments are built.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6be2b6d. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Real: the mitre distance / (1 + dot) is unbounded as two edges fold back (a 30 m sliver with a 1° point gave a 41.6 m roof segment, 0.2° gave 87 m; an exactly folded edge divides by zero). Fixed in 857016e: a corner whose mitre would pass 4× the distance (under about 29°, the limit an SVG stroke uses) gets the two edge-offset points instead, so the outline is always finite; right angles still mitre exactly. Tests in create-roof.test.ts: 'a sharp corner does not stretch the roof past the building', 'keeps a point of 0.2° within a wall's width of its corner', 'gives finite points where an edge folds back exactly on itself'.

AxiomeCG and others added 10 commits October 12, 2026 23:42
…f reaching out without bound

The roof outline grows the walls' centreline polygon by half a wall, mitring each corner by
distance / (1 + dot) of the two edge normals. As the edges fold back on each other the mitre
runs away: a 30 m sliver with a 1° point came out with a 41.6 m roof segment, 0.2° with 87 m,
and an edge folding exactly onto itself divided by zero into NaN points. A corner whose mitre
would pass four times the distance (under about 29°, the limit an SVG stroke uses) now gets the
two edge-offset points instead; right angles and the ordinary 45° to 135° corners mitre as
before.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Owner decision: only what takes part in bundle 2 stays in the PRs. locate_photo (ranking a
floor's corners for a photo) leaves; the facade photo straightening tools stay.

- core: agent-tools/photo-location.ts and agent-operations/photo-location.ts, their entries in
  the tool and operation registries and the batch list.
- mcp: its shared-tool registration; the tool leaves the annotation test, the OpenAI
  annotation policy and plugin-evals/tool-annotation-justifications.json.
- straightening's two warnings no longer name create_reference_elements.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Owner decision: only what takes part in bundle 2 stays in the PRs. The checker, its
dispute_coherence_item tool and everything that reported its items leave; the checkpoints and
the session's made list, which verify_scene's `since` reads, stay.

- core: coherence/* (the checks, the disputes, the checklist), the dispute operation and tool
  and their registrations; the wall's `roofJunction` declaration (only the checker read it;
  an old scene that has it still loads, the key is not part of the schema).
- apply-outcome no longer answers a write with a `coherence` delta; verify_scene returns no
  `coherence` checklist and `hasIssues` is its issues again; set_checkpoint no longer
  counts open issues; a checkpoint is taken with no issues and a review lists no coherence
  items. The parser still reads the `issues` older records carry, and a node's fingerprint
  still leaves out the `metadata.coherence` notes older versions kept.
- mcp: the tool's shared-tool registration and verify_scene's `coherence` output field; the
  tool leaves the annotation test, the OpenAI annotation policy and
  plugin-evals/tool-annotation-justifications.json.
- the reference inventory drops the row for the checker's metadata key.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e_surface and add_steps

Owner decision: only what takes part in bundle 2 stays in the PRs. The three site tools and
the fence ironwork nothing else called leave; the fence, stair and site schemas that main
already has stay.

- core: agent-tools/site-works.ts and agent-operations/site-works.ts with their registry,
  operation and batch entries; systems/fence/fence-ornament.ts (referenced by nothing).
- mcp: the three shared-tool registrations; the tools leave the annotation test, the OpenAI
  annotation policy and plugin-evals/tool-annotation-justifications.json.
- the reference inventory drops the `flatwork` row only add_site_surface wrote.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… 2; remove them

Owner decision: only what takes part in bundle 2 stays in the PRs. create_stairs_and_lifts,
add_entry_doors, propose_unit_layouts, apply_unit_layout and name_units leave, with the unit
layout engine and the reachability reader they stand on. The Unit and Guide schemas, the
create/list/set unit tools and the native stair tools that main already has stay.

- core: agent-operations/{circulation,entry-doors,name-units,reachability,unit-layouts},
  agent-tools/{circulation,entry-doors,unit-layouts,units} and building/unit-layout*.ts, with
  their registry, operation and batch entries and the exports only they fed (the layout
  judge type, the plan mask, the in-scene proposer). The plans guide's "copies",
  "interiors" and "apartments" steps leave with their files.
- mcp: the five shared-tool registrations; the tools leave the annotation test, the OpenAI
  annotation policy and plugin-evals/tool-annotation-justifications.json.
- the reference inventory drops the `guessed` rows only the unit guesser wrote.
- Kept for the plan commit, which still imports them: agent-operations/{level-balconies,
  unit-rooms} and building/room-habitable.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… 2; remove them

Owner decision: only what takes part in bundle 2 stays in the PRs. Reading, calibrating,
matching, surveying and vectorising plans, building from them (create_reference_elements,
furnish_from_plan, correct_plan_reading) and the reference inventory (record_reference) leave,
with the reference/plan engines under them. The guide node and the native schemas main already
has stay; so do the facade photo straightening tools, create_roof and its gap bridging.

- core: agent-operations/{furnish-from-plan,plan-calibration,plan-reading,plan-survey,
  reference-elements,reference-items,vectorize-plan,level-balconies,unit-rooms,created-summary},
  agent-tools/{furnish-from-plan,inventory,plan-reference,vectorize-plan,guides},
  building/{balcony*,plan-*,reference-*,room-habitable,wall-support-in}.ts, and
  lib/preview-id, systems/wall/{wall-batch,wall-coverage}, which only those called; their
  registry, operation and batch entries and the context fields (planAnswers, planVector)
  only they filled.
- the plans guide and the connect-time reference-inputs guide leave with the tools they taught;
  the MCP server's instructions are main's again, plus the agent-guide pointer.
- scene-view: a view's note is main's text again (no inventory invitation); paint no longer
  registers build paths for the inventory.
- wall-gaps keeps what create_roof's outline needs (gapBridges); the door/outside
  classification and swing reading only plan reading used leave.
- mcp: tools/reference-construction.ts, the plan-reading hosts (judge, rasteriser,
  vectoriser, raster decoder and the options that lent them), the open-world annotation
  constant only they used; the tools leave the annotation test, the OpenAI annotation policy and
  plugin-evals/tool-annotation-justifications.json.
- the reference inventory drops the rows for the plan, outline, unit-plan and balcony keys only
  those sources wrote.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…he resolver type

Owner decision: only what takes part in bundle 2 stays in the PRs. request_upload (a hosted
tool: a file the agent uploads and passes as asset:<id>) leaves; the server's own storage
resolver and attached-file handling are the host's and stay there.

- mcp: tools/source-resolver.ts (the SourceResolver type and the upload hint) and its
  export from the package index.
- view_scene's photo input is main's text again, and the agent guide and the pascal-3d
  skill no longer tell an agent to upload a photo with request_upload; the guide test no
  longer expects the tool or lists hosted-only tools.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ed on

Owner decision: tests protect an observable result or an invariant, not a number from an
animation curve or a sentence of copy. Eleven files were named by both audits; where they
disagreed on a block, it stays.

- point-ask: choreography.test.ts (constants equal to themselves) is deleted; spring loses
  its pair, closed-form and snap/rest/new-parameter blocks; region loses the label,
  size-format and nearest-edge helpers; placement loses the side-order and tail-pixel
  blocks; suggestions loses the per-kind copy, placeholder and kind maps, and the room
  and wall chips assert that they change with state rather than their words.
- viewer: reveal-pose loses the drop-feel curves; reveal-schedule loses the scatter, natural
  pace and group-key blocks; construction-dust loses the base/foot placement block;
  construction-reveal loses the wall-puff, roof-hair-lift and warm-start dust blocks, and its
  post, plate, impact and finale tests keep settlement and event order without the
  millisecond and centimetre figures.
- core: snap-ease loses the eased-out curve block; the corner hint test asserts the click
  and Alt rows and distinct reasons, not their sentences.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ol contracts

Owner decision: only what takes part in bundle 2 stays in the PRs. The contracts of
search_visual_references and inspect_visual_references (and the reference types they
return) were added by this branch and are not on main; nothing in the editor registers or
calls them.

- core: agent-tools/visual-references.ts and its export from the agent-tools index.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
None is on main and none has a caller or a test: building/material-swatches.ts,
systems/wall/wall-loop.ts, utils/repetition.ts, and lib/exterior-wall-loops.ts's
freeWallEnds (with its type), which only the removed plan engine would have called.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/core/src/agent-operations/door-clearance.ts Outdated
…tion and offset

itemNodePlanCorners sized a scripted item from the manifest's raw bounds and the node's scale
only. The renderer, and scriptItemBoundsInLevel which measures the vertical bounds, put the
asset's own corrective scale, rotation and offset inside the node's scale, so an item whose
asset carries any of them was missed or invented in the plan footprints behind item
collisions, door clearance, mid-room checks and chair slots. The corners now take the
same pose; a catalog item, whose dimensions are a plan box, is as before.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

const next = { ...node } as Record<string, unknown>
if (ref) next[field] = ref
else delete next[field]
written[id] = next as AnyNode

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Roof erase leaves finish field

Medium Severity

Erasing a roof or roof-segment finish deletes the role field on a copied node, then changedData records that key as undefined. applySceneChanges spreads the patch, so the preset stays on the node as undefined instead of being removed. Slot erase replaces the whole slots object and does clear. A roof erase: true can therefore leave the finish in place for later reads, batch simulation, and any store that ignores undefined patches.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2143eec. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Real. paint's roof erase records the lost field as undefined, which the store's updateNode removes, but applySceneChanges (and mergeSceneChanges when a node is created and changed in one batch) spread the patch and left an own undefined key, so the batch's working scene, achieved counts and previews disagreed with the store. Fixed in 799e8f6: both now remove a field the patch sets to undefined. Test: 'erasing a roof's finish takes the field off the node, as the store does'.

Comment thread packages/core/src/agent-operations/add-object.ts
AxiomeCG and others added 2 commits October 13, 2026 01:19
…as the store does

paint's roof erase deletes the role's preset from a copy and records it as `undefined` in the
update; the store's updateNode removes such a key, but applySceneChanges (the batch's working
scene, achieved counts, previews) and mergeSceneChanges (a node created and then changed in one
batch) spread the patch and left the key on the node as an own `undefined`. Both now remove a
field the patch sets to `undefined`, so the simulated node is the one the store holds.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…used like an object

add_object refuses a compile whose bounds are not finite numbers before anything is built, with
the code unmeasurable_object. A door, a window and a column take their width, height and
position from the same bounds and had no such check: NaN (null after a JSON hop) reached the
node as a non-finite size, or failed the schema with a raw validation array. The check moves
to lib/geometry-script-node as requireMeasured and add_door, add_window (new and rebuilt) and
add_column call it as add_object does, each naming its own tool and whether anything was
changed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Aymericr added a commit that referenced this pull request Oct 12, 2026
…scene materials (#1034)

* refactor(core): the wall paint plan lives in core

planWallPaint and plannedWallIds move from nodes/src/wall/paint-plan.ts to
core/src/systems/wall/wall-paint-plan.ts, unchanged, and core exports them.
The editor's wall paint imports them from core; the agent's paint operation
(next commit) writes a wall's faces through the same plan instead of a copy.

Co-authored-by: Adam NAILI <adam.naili.pro@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(core,editor): view_scene looks inside a floor

view_scene takes interior: { levelId }. The plan checks the level
(interior_level_invalid; interior_hidden for a hidden level or ancestor;
interior_transform_unsupported for a building tipped off the vertical) and,
with no target, eye or camera given, frames the floor from 55 degrees above.
The host gets only the level id: the MCP passes it to its SceneViewHost, and
the editor's capture works the cutaway out from its own scene with core's
sceneViewInterior. For that one render it hides the other floors and this
floor's roofs and ceilings, cuts the floor's walls and their shadows at 0.65
of the storey (a ClippingGroup on the level), draws its walls whole whatever
the wall mode, and lifts the user's solo and isolation on it
(temporarilyShowPresentation in the viewer). Everything is put back after
the render, through the capture's existing restore list.

The plan now frames buildings where they stand: what is under a building is
drawn in its frame, so its position and yaw move and turn the boxes, and an
opening is seen from its outside on the site. A roof, or a roof level holding
only roofs, frames its segments from its seat to its peak instead of
refusing nothing_to_view.

An authored snapshot pose passes the walls' finish refs when it swaps their
materials, as the wall renderer does, so painted faces keep their finishes.

From Adam's #1024. This keeps the capture on the snapshot
path view_scene already uses (it waits for the scene to build and holds the
viewport's last frame) instead of #1024's read-only transaction (per-node
commit tracking in NodeRenderer, a frame-loop freeze, control and object
state snapshots, asset readiness refusals).

Co-authored-by: Adam NAILI <adam.naili.pro@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(core,mcp): paint and search_materials, with native scene materials

paint gives walls, columns, slabs, ceilings, fences, doors, windows and roofs
a finish in one call, as the editor's Paint does: the slots its slot paint
writes, a wall's faces through the shared wall paint plan, a roof's or a
segment's top, edge or wall role field (refused shadowed_field when the
roof's segments have their own). exterior and interior resolve to a wall's
faces from the rooms on its sides. The finish is a colour (the nearest
library colour, said with how near), a library or scene ref, a native
material, or erase. Any unknown target, role or material refuses the whole
call.

A native material is kept as a scene material, reused when the scene holds
one the material schema reads as equal. The operation returns it in
SceneChanges.materials and needs the scene's materials in
AgentContext.materials; a host without them refuses a native material
(materials_unavailable) and still paints refs and colours. Scene refs are
checked against those materials, and an unknown library material's refusal
points to search_materials. get_node returns the scene materials a node's
finishes use.

search_materials finds library refs by words, as the library writes them
(timber is wood, render is stucco), by shade and by surface, and names the
words no material answers.

On the MCP both are shared tools. SceneOperations gains
supportsMaterialUpserts and getMaterials: the bridge lends its materials as
the context and applies SceneChanges.materials as upsert_material patches,
written before the nodes in the same history step. upsert_material is not
part of apply_patch's schema.

From Adam's #1024, without the run_batch and plan-reference
hooks it was written with.

Co-authored-by: Adam NAILI <adam.naili.pro@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(core): an interior view refuses a target on another floor

Every other floor is left out of an interior view, so a target on one framed
nothing the picture shows. view_scene with interior and a target off that
floor is refused interior_target_elsewhere, naming both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(core): paint shows over a room's finish, search matches whole words, interiors refuse what they leave out

- paint: a wall face is painted whole, its slot and an override in each room
  whose wall finish shows over it, as the editor's Paint writes a click on
  that room's face; painted parts keep theirs and the note names them. A
  curtain wall's exterior or interior is refused with its roles.
- search_materials: query words match whole words of a material (or begin
  one, from four letters), short and common words are dropped, and a flat
  colour suits every surface filter.
- view_scene interior: a level holding only roofs and ceilings is refused,
  a target the picture leaves out (a roof, a ceiling) is
  interior_target_elsewhere, and a hidden site no longer blocks the floors
  on it. The contract says what is cut: everything on the floor at 0.65 of
  the storey height.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(viewer,editor): a capture draws walls with the renderer's palette and keeps the perspective it asks for

- An authored pose or an interior view asks for whole walls on
  thumbnail:before-capture ({ wholeWalls: true }), and WallCutout swaps them
  with the resolver the renderer mounted it with (wallsForCapture). The
  capture's own swap used the plain wall palette, so a curtain wall's glass
  came out opaque.
- A capture with a perspective view of its own (view_scene's) stays
  perspective when the user's camera is orthographic
  (capturesOrthographically); it took the user's orthographic view while
  the result reported the planned camera.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Adam NAILI <adam.naili.pro@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

There are 5 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 232a5b1. Configure here.

Comment thread packages/core/src/agent-operations/create-roof.ts
Comment thread packages/core/src/agent-operations/wall-object-observations.ts
AxiomeCG and others added 2 commits October 13, 2026 01:37
create_roof checked materialPreset against the library so a finish it lacks could not be
stored and drawn as the default; fasciaMaterialPreset went to the segments unchecked, so an
unknown fascia finish was persisted and silently rendered as the default. It now takes the
same check, naming the field and the nearest library ids.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…rer draws it on

The wall object observer reads a missing `side` as the back face. The item system, the
plan symbols and the editor's floor plan all place a side-less wall-side item on the back
face (`side === 'front' ? a : b`), so the observer reports where the item is drawn; new
objects and placements write the side. The rule is now stated where it is read and pinned
against the saved screen with its side removed (and with the side flipped, which differs).

Co-Authored-By: Claude Sonnet 5.5 <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