The snapping model (Shift = cycle mode, Alt = force/free, mode-driven reads via isGridSnapActive/isMagneticSnapActive/isAngleSnapActive, snapProfile-declared context) lived only in code and the plan; tools.md still preached the legacy "Shift = bypass snapping". Close the drift so the architecture review refuses tool changes that revert to the old pattern: - tools.md: replace the held-Shift-bypass manipulation policy with the unified mode-driven model + the single snap read path. - interaction-scope.md: new "Snapping mode & modifiers" section (contexts, read path, modifiers, the chip-needs-a-scope rule) + a Rules bullet + the known-legacy MEP movers (migrate-on-touch) incl. the dual-path constraint (a bespoke mover must not open a `moving` scope — it re-mounts the generic mover via useMovingNode). - review-architecture skill: add interaction-scope.md to the reads and a new "F. Interaction scope, snapping & modifiers" checklist — new shiftKey-bypass, ungated grid step, missing snapProfile, a new useEditor interaction flag, or a bespoke mover opening a moving scope are blockers; touching the legacy MEP movers forces migration. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Architecture
Canonical rules for code that touches packages/core, packages/viewer, packages/editor, packages/mcp, or apps/editor. Read on demand from AGENTS.md and from .agents/skills/review-architecture/SKILL.md.
Pages
| Page | Covers |
|---|---|
| layers | Three.js layer constants, ownership, and rendering separation |
| systems | Core and viewer systems architecture |
| renderers | Node renderer pattern in packages/viewer |
| node-definitions | Three-checkbox composition model for registry-driven kinds (geometry / renderer / system) |
| materials-and-themes | Surface colour: surface roles, colour presets, the textures axis, and scene themes (appearance / ground / clay tints) |
| item-authoring | Content-author contract for catalog item GLBs: slot_ material naming, authored defaults + pascal_material extras, the cutout reserved mesh, UV world scale, and the validated Blender/export recipe |
| plugin-authoring | Public contract for external plugins — Plugin shape, setPluginDiscovery, lifecycle, what's in and out of v1 |
| tools | Editor tools structure, 2D↔3D behavioral parity, manipulation constraints, and Shift bypass defaults |
| interaction-scope | The authoritative interaction state machine ("the spine"): InteractionScope union, the begin/update/end/endIf contract, the raycast hot-set, and the overlay scope matrix |
| viewer-isolation | Keeping @pascal-app/viewer editor-agnostic |
| selection-managers | Two-layer selection (viewer + editor), events, outliner |
| scene-registry | Global node ID → Object3D map and useRegistry |
| spatial-queries | Placement validation (canPlaceOnFloor/Wall/Ceiling) for tools |
| node-schemas | Zod schema pattern for node types, createNode, updateNode |
| events | Typed event bus — emitting and listening to node and grid events |
| creating-rules | How to add or update a page in this folder |
Reading order for an architecture review
- layers, systems, renderers, tools, viewer-isolation — required every review.
- When the diff touches placement / move / handle / reshape / box-select / paint or any overlay or picking behaviour, also read interaction-scope.
- The remaining pages on demand, based on what the diff touches.