Code Review Skill
awesome-skills/code-review-skill
Provides comprehensive code review guidance for React 19, Vue 3, Angular 17+, Svelte 5, Rust, TypeScript, Java, Java 8, PHP, Ruby, Rails, Python, Django, FastAPI, Go, C/.NET, Kotlin, Swift, Dart…
Reviews a pull request against the Pascal editor's architectural rules: package boundaries, registry-driven node composition, hook hygiene and selector performance.
$ npx skills add pascalorg/editor --skill review-architecture -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install pascalorg/editor review-architecture --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/pascalorg/editor.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/review-architecture .claude/skills/review-architecture && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "review-architecture" agent skill from https://github.com/pascalorg/editor/tree/main/.agents/skills/review-architecture into .claude/skills/review-architecture/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "review-architecture", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/pascalorg/editor/tree/main/.agents/skills/review-architectureType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add pascalorg/editor --skill review-architecture -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install pascalorg/editor review-architecture --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pascalorg/editor.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/review-architecture .agents/skills/review-architecture && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "review-architecture" agent skill from https://github.com/pascalorg/editor/tree/main/.agents/skills/review-architecture into .agents/skills/review-architecture/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "review-architecture", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add pascalorg/editor --skill review-architecture -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install pascalorg/editor review-architecture --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pascalorg/editor.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/review-architecture .cursor/skills/review-architecture && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "review-architecture" agent skill from https://github.com/pascalorg/editor/tree/main/.agents/skills/review-architecture into .cursor/skills/review-architecture/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "review-architecture", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/pascalorg/editor.git --path .agents/skills/review-architecture--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add pascalorg/editor --skill review-architecture -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install pascalorg/editor review-architecture --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pascalorg/editor.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/review-architecture .gemini/skills/review-architecture && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "review-architecture" agent skill from https://github.com/pascalorg/editor/tree/main/.agents/skills/review-architecture into .gemini/skills/review-architecture/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "review-architecture", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install pascalorg/editor review-architectureInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add pascalorg/editor --skill review-architecture -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/pascalorg/editor.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/review-architecture .github/skills/review-architecture && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "review-architecture" agent skill from https://github.com/pascalorg/editor/tree/main/.agents/skills/review-architecture into .github/skills/review-architecture/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "review-architecture", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add pascalorg/editor --skill review-architecture -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install pascalorg/editor review-architecture --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pascalorg/editor.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/review-architecture .opencode/skills/review-architecture && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "review-architecture" agent skill from https://github.com/pascalorg/editor/tree/main/.agents/skills/review-architecture into .opencode/skills/review-architecture/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "review-architecture", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
review-architectureReviews a pull request against the Pascal editor's architectural rules: package boundaries, registry-driven node composition, hook hygiene and selector performance.
The agent first loads the architecture wiki pages as the source of truth, always reading the core set on layers, systems, renderers, tools, viewer isolation, node definitions, plugin authoring and agent surfaces, and further pages only when the diff touches their area. It then fetches the diff with the gh CLI or git, lists the changed files to map each to a rule, and classifies each by layer before running the checklist.
Checks cover package boundaries between core, viewer, editor and nodes, the three-part registry composition of geometry, renderer and system, regressions to legacy dispatch, the slots and world-scale-UV convention for new nodes and geometry, hook hygiene around useEditor, useScene and useViewer, selector performance, and limits on inspector field bounds. It takes a PR URL, a branch name or the current branch.
6 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit a860e19. It shows what the files ask for, not the result of running them.
Pre-approves these tools, so the agent can use them without asking each time:
Bash(git *)Bash(gh *)ReadGrepGlobFrom allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
ghgitFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use gh and git, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Pascal Architecture PR Review loads about 7.5k tokens when it runs. Until then it costs about 119 tokens; SKILL.md has 3,510 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from pascalorg/editor at commit a860e19, republished under its MIT licence (© pascalorg). 3,510 words, ~7,539 tokens.
.claude/skills/review-architecture/SKILL.md (or your agent's skills folder).Architectural review for Pascal PRs. The user will provide a PR URL, branch name, or ask to review the current branch.
Read these before reviewing any diff. They are the source of truth, not your training data:
wiki/architecture/layers.mdwiki/architecture/systems.md — core systems vs viewer systems, what each may dowiki/architecture/renderers.md — renderer responsibilities and prohibitionswiki/architecture/tools.md — editor tools live only in apps/editor/components/tools/ or packages/nodes/src/<kind>/wiki/architecture/viewer-isolation.md — viewer must stay editor-agnosticwiki/architecture/node-definitions.md — the three-checkbox composition model (geometry / renderer / system)wiki/architecture/plugin-authoring.md — public contract for external node packswiki/architecture/agent-surfaces.md — MCP ↔ hosted chat ↔ skill parity. Read whenever the diff touches packages/mcp/** or skills/**.Required on every review. Read the remaining pages on demand when the diff touches their subject area:
wiki/architecture/selection-managers.mdwiki/architecture/scene-registry.mdwiki/architecture/spatial-queries.mdwiki/architecture/node-schemas.mdwiki/architecture/inspector-field-limits.md — no arbitrary min/max on dimension fields. Read whenever the diff adds or edits parametrics.ts, a kind panel.tsx, or <SliderControl> bounds.wiki/architecture/events.mdwiki/architecture/interaction-scope.md — the interaction state machine + the unified snapping/modifier convention. Read whenever the diff touches a tool, a move-tool / selection / endpoint / reshape file, lib/interaction/**, lib/snapping-mode.ts, or use-interaction-scope.If anything in the diff looks like a new dispatch surface or registry concept, also read DECISIONS.md E-002 and wiki/architecture/node-definitions.md — they own the composition model and the capability vocabulary.
# If the user gave a PR URL or number:
gh pr diff <pr-number-or-url>
# If reviewing the current branch:
git diff main...HEADAlso list changed files so you can map each to the relevant rule:
gh pr view <pr> --json files --jq '.files[].path'
# or
git diff --name-only main...HEADFor every new file, new type, new store field, or new exported helper introduced by the diff, answer one question: which package does this belong to — core, viewer, editor, or nodes? If the answer is "editor" but the code lives in packages/core or packages/viewer (or vice versa), or if kind-specific code lands anywhere other than packages/nodes/src/<kind>/, flag it as a blocker. This is the most common and most damaging class of violation, and the checklist below won't reliably catch it on its own — do this pass explicitly.
packages/core — domain data + pure logic.
Owns: node schemas, the scene store (useScene), live transforms store, core systems (wall mitering, slab polygons, space detection), event bus, plain 2D/3D math helpers, sceneRegistry, the registry primitives (nodeRegistry, registerNode, loadPlugin, discoverPlugins/setPluginDiscovery, SceneApi, Plugin/NodeDefinition types). Consumed by every downstream package, including read-only embeds. Must not know about: Three.js/R3F, packages/viewer, apps/editor, packages/nodes, any rendering or UI concept, any tool/mode/phase concept, or any view-specific concept (floorplan, paint preview, cursor indicators, selection outline styling).
packages/viewer — the 3D canvas, shippable standalone.
Owns: <Viewer>, the generic <NodeRenderer> / <ParametricNodeRenderer> / <GeometrySystem> / <RegisteredSystems> / <FloorplanRegistryLayer> plumbing, viewer systems (cutouts, zones, level positions, scans), the viewer store (useViewer) for genuine presentation state only (selection path, camera/level/wall/view modes, theme, display toggles, hover id), useNodeEvents. Consumed by both the editor and the read-only /viewer/[id] route. Must not know about: editor state (useEditor, tools, phases, modes), editor-only names baked into presentation modes ('delete', 'paint-ready'), editor-only state types (material preview, active paint target, floorplan anything), packages/nodes.
packages/editor (and apps/editor) — the editing experience.
Owns: the tool framework (useDragAction, ParametricInspector, <MoveRegistryNodeTool>, the registry-aware dispatchers in tool-manager.tsx / MoveTool / panel-manager.tsx / helper-manager.tsx), useEditor, action menus, panels, the floorplan panel and its helpers, paint mode, selection-manager phase/mode logic, cursor badges, command palette, keyboard shortcuts — anything absent from the read-only viewer route. Injects itself into <Viewer> via children and props, never the reverse. Must not import from packages/nodes.
packages/nodes — the built-in plugin (pascal:core).
Owns: one folder per node kind (packages/nodes/src/<kind>/) containing definition.ts, schema.ts, optionally geometry.ts / renderer.tsx / system.tsx / floorplan.ts / tool.tsx / move-tool.tsx / panel.tsx / parametrics.ts / preview.tsx. Exports builtinPlugin. Depends on editor, viewer, and core via their public surfaces — the same surfaces a third-party plugin uses (peer-dep style). Nothing in core/, viewer/, or editor/ may import from @pascal-app/nodes. The dependency arrow is one-way: framework code consults nodeRegistry, never reaches into a specific kind's folder.
/viewer/[id] route need this? If no, it belongs in apps/editor / packages/editor.Floorplan, Paint…, Draft…, Marquee, CursorBadge, HoverMode, …Tool, Moving…, Curving….) Default to editor and justify loudly if it's anywhere else.'delete', 'paint-ready', 'material-paint', 'site'/'structure'/'furnish', 'build'/'edit'.) Belongs in useEditor, not useViewer or core.Floorplan prefix.setMaterialPreview in useViewer that only the editor would ever invoke.) That's a layering smell — the state belongs in the caller's layer.door-…, wall-…, item-…, etc.) Then it belongs in packages/nodes/src/<kind>/, not under packages/viewer/src/components/renderers/<kind>/, packages/viewer/src/systems/<kind>.ts, packages/editor/src/components/tools/<kind>/, or packages/editor/src/components/ui/panels/<kind>-panel.tsx. Those legacy locations were deleted at Phase 6 cleanup — reintroducing one is a regression to the dispatch model.import line read from '@pascal-app/nodes' inside core/, viewer/, or editor/? Blocker. The Biome noRestrictedImports overrides in biome.jsonc already ban this, along with the rest of the arrow (core → three/R3F/viewer/editor, viewer → editor); if one slipped through, a framework package is reaching down into the plugin or up the arrow.Write the classification down before writing findings. If core gains "Floorplan" types, the viewer gains paint-mode vocabulary, a renderer grows editor awareness, or a kind-specific file appears outside packages/nodes/src/<kind>/ — those are the blockers to lead with, not downstream symptoms.
packages/viewer/** does not import from @pascal-app/editor, apps/editor, or @pascal-app/nodes, and does not reference useEditor, tool state, phase, or mode.packages/core/** does not import Three.js, react-three-fiber, @pascal-app/viewer, @pascal-app/editor, or @pascal-app/nodes.packages/editor/** does not import from @pascal-app/nodes.packages/core/** does not introduce types or helpers named after an editor view (Floorplan*, Paint*, Draft*). Generic plan-geometry helpers are fine; view-specific vocabulary is not.case '<kind>': clauses (or equivalent kind-specific branching keyed on node.type) inside packages/viewer/** or packages/editor/**. Phase 6 deleted these; the dispatch happens via nodeRegistry. The exceptions left in tree are treeNodeByType (a lookup map, not a switch) and unit-formatting switches (centimeters / feet / inches). Any new case 'door'|'wall'|'item'… in a framework package is a blocker — the behavior belongs on the kind's NodeDefinition.useScene (committed state) and useLiveTransforms (ephemeral drag state); direct sceneRegistry mesh transforms are allowed only under the live-drag exception in wiki/architecture/tools.md. No business logic, no imports from packages/viewer.packages/nodes)If the PR adds or modifies a node kind, check against wiki/architecture/node-definitions.md and wiki/architecture/plugin-authoring.md:
def.geometry?: (node, ctx) => Object3D, def.renderer?: () => Promise<{ default }>, def.system?: () => Promise<{ default }>. There is no discriminator — presence is participation. Setting all three is fine if the kind genuinely needs them; setting a def.system whose only job is to rebuild geometry on dirty is a smell — collapse to def.geometry and let <GeometrySystem> do the work.def.geometry function must not import useScene, must not mutate the store, and must not depend on React context. Read other nodes via GeometryContext (ctx.resolve / ctx.children / ctx.siblings / ctx.parent).<ParametricNodeRenderer> binds <group position={liveTransform?.position ?? node.position}> in JSX. A builder that bakes world position into vertex coords, or a system that imperatively writes group.position / group.rotation, will desync R3F's prop binding — the node will snap to (0,0,0) after rebuild. Flag any imperative group.position.set(...) inside def.geometry or a registered system. (Tool-driven sceneRegistry.nodes.get(id).position.set(...) during a live drag is fine and is the documented pattern — see hook hygiene below.)<GeometrySystem> only disposes children carrying userData.__fromGeometry = true. Custom systems that imperatively add children to a registered group must follow the same convention if the group can host React-mounted children (e.g. shelf surfaces hosting items).sceneRegistry.nodes.get(id)).def.preview calls the geometry builder and then sets material.opacity = 0.5, but the builder caches materials at module scope (most do, keyed on material / materialPreset), the mutation leaks into every committed instance. Clone, mutate the clone, reassign mesh.material, dispose only the clone on unmount. Reference: nodes/src/shelf/preview.tsx.AnyNode in SceneState.setScene). A new field needs a Zod .default() / .optional(). A rename / removal / retype needs a migrateNodes entry in packages/core/src/store/use-scene.ts that rewrites the legacy shape before parse — a .default() alone silently drops the old value. A schema diff that does neither is a blocker: it breaks every existing scene. See wiki/architecture/node-schemas.md § Schema Evolution.children on the schema. If def.relations.hosts is set, the schema must declare children: z.array(z.string()).default([]) (and migrateNodes must patch existing scenes). Otherwise useScene.createNode(child, parentId) writes a parent.children entry into nothing and the host never sees the new child.MoveTool dispatches to MoveRegistryNodeTool only when def.capabilities.movable is set. Kinds with bespoke move semantics (wall endpoint drag with linked-wall cascade, slab vertex edit, etc.) deliberately omit movable and supply def.affordanceTools.move instead. Force-routing a bespoke-move kind through generic dispatch (nodeRegistry.has(kind) instead of def.capabilities.movable) is a regression — call it out. See wiki/architecture/node-definitions.md § "Opting out of a generic path".def.capabilities.paint. A paintable kind declares resolveRole / buildPatch / applyPreview (+ optional getEffectiveMaterial) on PaintCapability; the editor's selection-manager routes hover / click / preview through the generic dispatcher. A PR that adds an if (node.type === '<kind>') arm to paint-mode handling, paint-preview application, or material picker resolution is a regression — the behaviour belongs on the kind's paint capability. See packages/core/src/registry/types.ts (PaintCapability).slots record (slotId → MaterialRef, scene:/library:) on the schema, resolved via def.capabilities.paint — not ad-hoc per-surface material / materialPreset fields, and not a parallel store. A new paintable kind whose schema lacks slots (or whose duplicate / preset / clone path drops it) is a blocker: it silently loses painted materials. (Slots are plain data — generic clone/parse preserves them; bespoke draft-rebuild placement paths must thread them through explicitly. Reference bug: item duplicate rebuilt the draft from asset and dropped slots.)def.geometry producing a surface a finish can tile onto must generate UVs at the same world scale walls / slabs / roofs use, because catalog finishes set repeat as tiles-per-metre. Unitless, bounding-box-normalised, or hardcoded UVs that don't scale with the surface are a blocker — finishes won't tile consistently. Flat-colour-only surfaces need no UVs. GLB item authoring follows the same contract via slot_-prefixed materials (case-insensitive, slot_ → slot id). See wiki/architecture/materials-and-themes.md § "Texture world scale" and wiki/architecture/item-authoring.md.def.capabilities.floorPlaced. Kinds that rest on a level and lift over overlapping slabs declare a footprint (and optional applies predicate) on FloorPlacedConfig; the generic <FloorElevationSystem> writes slabElevation + node.position[1] onto the registered mesh on each dirty mark. A new per-kind useEffect / per-kind system that recomputes Y from slab overlap is a regression — the per-kind block was lifted out of ItemSystem in Phase 6.1. See packages/core/src/registry/types.ts (FloorPlacedConfig).def.surfaceRole. Solid / Rendered / Clay viewer modes look up the kind's surfaceRole on NodeDefinition to pick the right material strategy (clay overrides, edge passes, theme overlays). New if (node.type === '<kind>') arms inside the render-mode pipeline or theme application are a regression — the behaviour belongs on def.surfaceRole. See packages/core/src/registry/types.ts:529.movable / paint / floorPlaced / cuts / selectable describe what the node does. A new capability named after a host kind (slabAccessory, wallAccessory, siteAccessory, anything Xaccessory / Xhosted shaped) couples the registry's type surface to one specific host and reads as precedent for the next reviewer. Blocker. Push back: generalise into a paired host-side capability ("I merge subtractive accessories from my children") + accessory-side capability ("I provide a cut geometry, cascade my dirty mark to my host's parent"); cuts go through capabilities.cuts (cut intents, packages/core/src/schema/cut.ts). The single existing case — capabilities.roofAccessory (packages/core/src/registry/types.ts:791, consumed by packages/viewer/src/systems/roof/roof-system.tsx) — is documented tech debt; do not extend the pattern.viewer/src/components/renderers/<kind>/*, viewer/src/systems/<kind>-system.tsx, editor/src/components/tools/<kind>/*, editor/src/components/ui/panels/<kind>-panel.tsx, editor/src/components/ui/helpers/<kind>-helper.tsx, or inline useMemo floor-plan entry-builders inside editor/src/components/editor/floorplan-panel.tsx — all of these were systematically deleted at Phase 6. The behavior belongs on the kind's NodeDefinition (def.renderer / def.system / def.geometry / def.tool / def.affordanceTools / parametrics.customPanel / def.toolHints / def.floorplan).def.floorplan. New per-kind floor-plan rendering must return FloorplanGeometry from def.floorplan(node, ctx) and be rendered by <FloorplanRegistryLayer>. New inline branches in floorplan-panel.tsx are a blocker.plugin.materials, plugin.systems, plugin.panels, or making plugins extend host stores (useScene / useEditor / useViewer) — is out of scope for the v1 contract documented in wiki/architecture/plugin-authoring.md. Either the change belongs as a new field on NodeDefinition (additive, doesn't bump apiVersion) or it needs its own plan.useEditor, useScene, useViewer)useViewer must be presentation-only (selection, camera, level mode, display toggles). Editor-only state (active tool, phase, edit mode, paint preview, floorplan state) goes in useEditor.useScene directly. A kind's geometry / system / tool should read and write through SceneApi (passed in by the framework) or GeometryContext. Direct useScene.getState() calls inside packages/nodes/src/<kind>/ are a smell — they bypass the registry's IoC point and make the code harder to test.useLiveTransforms.set(...) per grid:move tick to animate registered parametric kinds — the selector path doesn't reliably re-render and the mesh visibly disappears mid-drag. Use sceneRegistry.nodes.get(node.id)?.position.set(x, y, z) instead, and commit once at the end via useScene.temporal.getState().resume() → updateNode → pause(). The reference implementation is MoveRegistryNodeTool. This is the only sanctioned use of imperative mesh transforms by a tool; flag any other location that does the same.useLiveNodeOverrides, never per-tick useScene. A kind whose geometry is recomputed from data fields (wall start/end, opening host-cut, endpoint reshape) previews by publishing field patches to useLiveNodeOverrides (merged by getEffectiveWall / getEffectiveNode), writing the scene store once on commit. A tool that calls useScene.updateNodes/updateNode on grid:move (or any per-pointer-move tick) is a blocker — it swaps the nodes map ref and re-renders every useScene(s => s.nodes) subscriber app-wide each frame. markDirty per tick is fine for bounded gestures (drag marks drain every frame); a useFrame/animation loop that marks dirty for as long as something animates is a blocker — the scene can then never settle to DIRTY 0. Animations signal rebuilds through their own records (useInteractive animations), marking dirty once on completion; see wiki/architecture/node-definitions.md § "geometry + system". Grep tell: updateNode(s)?( in an onGridMove/onMove/applyPreview path under packages/nodes/src/<kind>/. See wiki/architecture/tools.md § "Data-driven live drag".<Viewer> siblings) must not subscribe to large or frequently-changing slices — e.g. useScene(s => s.nodes), useScene(s => s). Flag these: they re-render the whole subtree on every mutation.s => ({ a: s.a, b: s.b }), s => s.items.filter(...)) without a custom equality function (shallow or custom) are re-render hazards.<XxxPanel> (legacy or parametrics.customPanel-mounted), avoid useScene(s => s.nodes[selectedId]) as a callback dep — it changes every tick and pushes useCallback into infinite-loop territory. The recipe is in wiki/architecture/node-definitions.md § "Custom panels: keep handlers stable during slider drags".FloorplanRegistryLayer → FloorplanRegistryEntry) must have each child subscribe to its own slice (useLiveTransforms(s => s.transforms.get(id)) / overrides.get(id)) and be memo'd with referentially stable props; the parent subscribes only to the stable id list. Subscribing the parent or a child to the whole transforms/overrides Map, dropping a memo, or passing unstable props re-renders all N children every drag tick — a flood that type-checks and passes tests. Sibling invalidation goes through a per-node epoch, not a whole-layer re-render. See wiki/architecture/tools.md § "Floorplan registry: per-node subscriptions".<Viewer>, not added inside the viewer package.<mesh> / <line*> / <points> / <sprite> an editor overlay or tool component adds to the 3D scene (gizmos, handles, guides, previews, cursor meshes, marquees) must set layers={EDITOR_LAYER} — or GRID_LAYER for the ground grid, ZONE_LAYER for zone fills. The thumbnail/snapshot camera renders only layer 0, so an untagged overlay leaks into exports (and gets inked / SSGI-darkened in the live view). Flag any overlay-component primitive that omits the layer assignment. See wiki/architecture/layers.md.packages/nodes/src/<kind>/ and registering its definition in builtinPlugin.nodes. Adding to a hand-maintained list elsewhere is a sign the registry hasn't absorbed that surface yet. A few such lists remain (AnyNode, treeNodeByType in the outliner, the allTypes seeds in editor/src/components/editor/selection-manager.tsx, the built-in tool unions in use-editor.tsx): extending one of those is expected; a new one is a finding.AnyNode is hand-maintained for now (full runtime derivation would lose static typing); packages/nodes/src/index.test.ts is the drift gate. If a PR adds a kind to AnyNode without adding it to builtinPlugin.nodes (or vice versa), the parity test catches it — but flag it in review too.Apply when the diff touches a tool, a move-tool / selection / endpoint / reshape file, lib/interaction/**, lib/snapping-mode.ts, or use-interaction-scope. Source of truth: wiki/architecture/interaction-scope.md and wiki/architecture/tools.md.
useEditor interaction flag. "What the user is doing" is owned by useInteractionScope (begin / update / end / endIf). A new useEditor boolean for an in-flight interaction (moving…, curving…, dragging…, editing…, …InFlight) is a blocker — it goes through the scope. The legacy mirror flags are being retired, not extended.move-tool / selection file that reads event.shiftKey, event.nativeEvent?.shiftKey, or modifiers.shiftKey to bypass snapping (raw cursor, skip grid, skip angle) is a blocker — the convention is Shift = cycle the mode, Alt = force/free. Snap state must come from isGridSnapActive() / isMagneticSnapActive() / isAngleSnapActive(). Grep tell: shiftKey near a snap / step / projectToAngleLock / alignment expression in packages/nodes/src/<kind>/{tool,move-tool,selection}.tsx. (Shift for multi-select in select mode, or a documented topology opt-out, is fine — confirm which it is.)isGridSnapActive() — always useEditor.getState().gridSnapStep, or a constant WALL_GRID_STEP / 0.5 / getSegmentGridStep() applied unconditionally — ignores the active mode and is a blocker. The gated form is const step = isGridSnapActive() ? useEditor.getState().gridSnapStep : 0.snapProfile. A kind whose tool snaps but whose NodeDefinition omits snapProfile ('item' | 'structural') gets no contextual chip and the wrong default mode-set — flag it (suggestion, blocker if it ships a bespoke per-kind snapping switch instead).moving scope. useMovingNode() reads the scope, and tool-manager mounts the generic MoveRegistryNodeTool whenever it's non-null. A bespoke move-tool.tsx that calls begin(movingScope(...)) or setMovingNode(node) re-creates the dual-path double-handling (FPS collapse / teleport on move). Blocker. Mode-driven snapping inside a bespoke mover must resolve the mode without a global moving / reshaping scope (see interaction-scope.md § "Snapping mode & modifiers").event.altKey is not an alignment bypass. A drafting/preview path that reads event.altKey to suppress Figma-alignment is a blocker in any new or touched tool — alignment follows the magnetic snap mode (bypass: !isMagneticSnapActive()). Alt is force/free for placement/move; it is not a snap/alignment modifier. The one sanctioned Alt use outside force is the wall/fence chain-mode toggle (clean Alt-tap → cycleWallChainMode / cycleFenceChainMode, via hooks/use-keyboard.ts isChainModeContext()), allowed only because wall/fence drafting has no force role. Grep tell: event.altKey near an align / bypass expression in a tool.tsx / floorplan preview path.wiki/architecture/interaction-scope.md (Known-legacy); a PR that touches one must migrate it, not extend it; a new tool on either legacy pattern is a blocker regardless. (1) shiftKey snap-bypass in the MEP move/endpoint tools (packages/nodes/src/{duct-segment,pipe-segment,liquid-line,lineset,duct-fitting}/{move-tool,selection}.tsx). (2) altKey alignment-bypass in the roof / polygon / slab pointer-move previews (components/editor/floorplan-panel.tsx) and the resolveSlabPlanPointSnap / resolveCeilingPlanPointSnap paths. Already migrated — do not regress: wall + fence drafting (both modifier patterns) and zone drafting (components/tools/zone/zone-tool.tsx — mode-driven grid/angle gates, no Shift bypass).Group findings by severity:
wiki/architecture/ or breaks a layer/package boundary. Must be fixed before merge.For each finding, include:
path/to/file.ts:42wiki/architecture/viewer-isolation.md, wiki/architecture/node-definitions.md)Skip formatting, import ordering, and anything CI already covers.
If the PR fully complies, say so explicitly — do not invent nits to appear thorough.
End with:
© pascalorg, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .agents/skills/review-architecture of pascalorg/editor.
Open the folder on GitHubat commit a860e19
Pascal Architecture PR Review next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Pascal Architecture PR Review this skillpascalorg/editor | 25k | — | ~7.5k | Automated safety check: Pass | MIT | |
| Code Review Skillawesome-skills/code-review-skill | 2.1k | — | ~2.8k | Automated safety check: Notes | MIT | |
| Code Review SkillRain-kl/OpenFlare | 288 | — | ~2.3k | Automated safety check: Notes | MIT | |
| Code Review Excellenceandrew-yangy/gru-ai | 155 | — | ~1.7k | Automated safety check: Notes | MIT | |
| Code Reviewerrevfactory/harness-100 | 1.3k | — | ~1.8k | Automated safety check: Pass | Apache-2.0 | |
| Conduct Code Review RoundFerroxLabs/wayland | 608 | — | ~3.9k | Automated safety check: Pass | Apache-2.0 |
awesome-skills/code-review-skill
Provides comprehensive code review guidance for React 19, Vue 3, Angular 17+, Svelte 5, Rust, TypeScript, Java, Java 8, PHP, Ruby, Rails, Python, Django, FastAPI, Go, C/.NET, Kotlin, Swift, Dart…
Rain-kl/OpenFlare
Provides comprehensive code review guidance for React 19, Vue 3, Angular 17+, Svelte 5, Rust, TypeScript, Java, PHP, Python, Django, Go, C/.NET, Kotlin, Swift, NestJS, C/C++, and more.
andrew-yangy/gru-ai
Provides comprehensive code review guidance for React 19, Vue 3, Rust, TypeScript, Java, Python, and C/C++.
revfactory/harness-100
Full pipeline for automated code review. An agent skill from revfactory/harness-100.
FerroxLabs/wayland
Orchestrates a thorough code review process by chaining four engineering skills into a structured review pipeline.
openinterpreter/openinterpreter
Watches an open GitHub pull request until it merges, handling review comments, diagnosing CI failures and retrying flaky checks along the way.
pascalorg/editor
Checks whether a sofa, table, bed or appliance fits in a measured Pascal room and reports only what the evidence supports, or asks for the missing measurements.
pascalorg/editor
Opens or refreshes a pull request on pascalorg/editor from the current branch, describing only what the branch's commits and diff actually contain.
pascalorg/editor
Connects an agent to Pascal's MCP tools to create, inspect, edit, validate and save editable 3D building scenes, then return a verified editor link.
Categories
Reviews a pull request against the Pascal editor's architectural rules: package boundaries, registry-driven node composition, hook hygiene and selector performance. The agent first loads the architecture wiki pages as the source of truth, always reading the core set on layers, systems, renderers, tools, viewer isolation, node definitions, plugin authoring and agent surfaces, and further pages only when the diff touches their area. It then fetches the diff with the gh CLI or git, lists the changed files to map each to a rule, and classifies each by layer before running the checklist.
Pascal Architecture PR Review fits situations like: reviewing a Pascal pull request for architectural violations; auditing a branch before merge against the package boundary rules; checking that a new node follows the registry composition model; looking for selector or hook performance problems in a diff.
Run `npx skills add pascalorg/editor --skill review-architecture -a claude-code`. Or copy the skill folder (.agents/skills/review-architecture in pascalorg/editor) into .claude/skills/review-architecture in your project. Claude Code loads it when a task matches its description.
Run `npx skills add pascalorg/editor --skill review-architecture -a codex`. Or copy the skill folder (.agents/skills/review-architecture in pascalorg/editor) into .agents/skills/review-architecture in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add pascalorg/editor --skill review-architecture -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/review-architecture, .gemini/skills/review-architecture, .github/skills/review-architecture and .opencode/skills/review-architecture in your project.
Going by SKILL.md and its folder, Pascal Architecture PR Review needs the command-line tools its instructions call (gh and git). Our summary lists: The gh CLI and git; The Pascal repository's architecture wiki pages. Its frontmatter pre-approves these tools: Bash(git *), Bash(gh *), Read, Grep, Glob.
SKILL.md contains no URLs. Its commands use gh and git, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Pascal Architecture PR Review is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 7.5k tokens (SKILL.md is roughly 30k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Pascal Architecture PR Review: Code Review Skill (awesome-skills/code-review-skill, 2.1k stars), Code Review Skill (Rain-kl/OpenFlare, 288 stars), Code Review Excellence (andrew-yangy/gru-ai, 155 stars) and Code Reviewer (revfactory/harness-100, 1.3k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
pascalorg (a GitHub organization) maintains it in pascalorg/editor, which has 24,695 GitHub stars. The repository holds 4 skills in this directory. The repository was last updated on October 6, 2026.
Source: pascalorg/editor on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.