Pascal Editor 的 Agent 协作架构:从 CLAUDE.md 理解包分层边界、架构守则与 AI 工作流
【免费下载链接】editorOpen-source 3D architectural editor with a local CLI, MCP tools, and practical workflows for humans and AI agents.项目地址: https://gitcode.com/GitHub_Trending/editor93/editor
本文以仓库根目录的 CLAUDE.md(Agent 指令文件)为主线,系统拆解pascalorg/editor这一开源 3D 建筑编辑器如何为人与 AI Agent 搭建一套可协作、可审查、可扩展的工程结构:包括@pascal-app/{core,viewer,editor,mcp}的包职责划分、wiki/architecture/架构文档体系、.agents/skills/即用工作流,以及面向架构敏感变更和 PR 审查的完整守则。读完本文,你将掌握在该仓库中"往哪层放代码、动架构前先读什么、如何按规范审查一个 PR"的完整路径,并能将同样的分层与守则方法论迁移到自己的开源项目。
一、仓库形态:一仓库、四包、一独立应用
CLAUDE.md开篇即明确了本仓库的定位:这是@pascal-app/{core,viewer,editor,mcp}四个 npm 包与独立编辑器应用的公共开源家园。它们既以 npm 包形式被消费,也(在pascalorg/private-editor中)以 git submodule 方式被引用——这决定了仓库必须保持严格的包边界,否则下游消费者将无法按需裁剪依赖。
仓库顶层形态可概括为如下五块(源自 CLAUDE.md 的 Repo Shape 表):
| 路径 | 职责 |
|---|---|
packages/core | 场景图(Scene graph)、节点 schema、store、事件总线、核心系统——纯逻辑,不含 Three.js |
packages/viewer | 独立 3D 画布:渲染器、viewer 系统、展示状态 |
packages/editor | 编辑器 UI 组件,供独立应用与嵌入式宿主复用 |
packages/mcp | MCP 服务器与场景存储适配器 |
apps/editor | 独立编辑器应用——组合viewer+editor+ 工具 |
实际仓库中,packages/目录下还包含packages/nodes(内置节点插件pascal:core)、packages/cli、packages/capture-protocol、packages/capture-viewer、packages/ui、packages/ifc-converter等(见 packages 目录);apps/下除apps/editor外还有apps/ifc-converter。这印证了 CLAUDE.md 的定位:packages/core是纯逻辑内核,被所有下游包消费;packages/viewer可独立发货;apps/editor是最终的组合层。
从 package.json 可以看出,仓库采用 monorepo 工作区(workspaces: apps/*, packages/*, tooling/*),以 Turbo 编排构建与测试(turbo run build、turbo run dev、turbo run test),代码检查与格式化统一交给 Biome(biome lint/biome check)。
二、"往哪里看":文档地图与技能体系
CLAUDE.md的第二部分给出了三条查资料路径,这也是任何 Agent 进入仓库后的第一份索引:
- 架构规则→
wiki/architecture/,按需阅读,索引在wiki/architecture/README.md; - 即用工作流(Skills)→
.agents/skills/<name>/SKILL.md,同名内容可通过.claude/skills/、.cursor/skills/、.codex/skills/等路径访问; - 人类阅读的仓库导航→
README.md、SETUP.md、CONTRIBUTING.md。
一个值得注意的工程细节:CLAUDE.md、GEMINI.md与.github/copilot-instructions.md都是指向同一内容的软链接,Codex 则直接读取该文件。在仓库根目录执行ls -la可以验证:CLAUDE.md -> AGENTS.md、GEMINI.md -> AGENTS.md、.github/copilot-instructions.md -> ../AGENTS.md,而 .claude/skills 也是指向.agents/skills的软链接。这意味着一套守则,多种 Agent 入口——Claude、Gemini、GitHub Copilot、Codex 无论以哪种方式接入,读取到的都是同一份规范,不会出现多份文档漂移的问题。这是一个非常值得借鉴的实践:把"给 Agent 看的指令"收敛成单一事实源,再通过符号链接暴露给不同工具。
架构文档体系的纵深
CLAUDE.md提到架构规则在wiki/architecture/,其索引页 wiki/architecture/README.md 给出了完整的页面清单,覆盖了:
- 分层与渲染:
layers.md(Three.js 图层常量与归属)、renderers.md(节点渲染器模式)、systems.md(core/viewer 系统架构); - 节点与注册表:
node-definitions.md(geometry/renderer/system 三勾选组合模型)、node-schemas.md(Zod schema 模式)、scene-registry.md(全局节点 ID → Object3D 映射); - 交互与工具:
tools.md(工具结构、2D↔3D 行为对齐)、interaction-scope.md(交互状态机)、spatial-queries.md(放置校验)、events.md(类型化事件总线); - 隔离与主题:
viewer-isolation.md(viewer 保持编辑器无关)、materials-and-themes.md、item-authoring.md(目录 GLB 内容创作契约)、plugin-authoring.md(外部插件公共契约); - 其它:
selection-managers.md、selection-groups.md、measurements.md、inspector-field-limits.md、vertical-model.md、capture-runtime.md、creating-rules.md。
该索引页还给出了架构审查的固定阅读顺序:layers→systems→renderers→tools→viewer-isolation为每次审查必读;当 diff 涉及放置/移动/手柄/重塑/框选/涂刷等交互时,再追加interaction-scope。
以 wiki/architecture/layers.md 为例,可以看到这套文档并非空泛原则,而是精确到常量与代码位置:SCENE_LAYER=0、OVERLAY_LAYER=1、ZONE_LAYER=2、GRID_LAYER=3、SHADOW_ONLY_LAYER=4、BATCHED_LAYER=5均定义于@pascal-app/viewer;而apps/editor只暴露EDITOR_LAYER作为OVERLAY_LAYER的别名再导出,从而"编辑器不感知 viewer 的 pass 编号,却能落在同一图层"。文档还解释了每个图层存在的"为什么":Zones 用半透明depthTest: false材质,必须通过独立zonePass合成以避免进入 SSGI/TRAA 深度缓冲;Overlays 需要保持"清晰 UI"而不得被屏幕空间描边或 AO 压暗;地面网格因为需要被墙体遮挡而必须进入场景 pass 共享深度缓冲;批量渲染源几何移到BATCHED_LAYER以去除重复的颜色与阴影提交。这些内容为下文"分层边界"提供了渲染层级的落地证据。
三、分层边界:一读即内化的三条铁律
CLAUDE.md用专门一节("Layer Boundaries (read once, internalise)")规定了三个包各自的职责与禁止项,这是整个仓库最重要的架构约束:
packages/core拥有领域数据与纯逻辑。它不得导入 Three.js、packages/viewer、apps/editor,不得涉及渲染/UI 概念、工具、模式、阶段,以及任何视图专属概念(如 floorplan 或 paint preview)。packages/viewer拥有独立 3D 画布。它不得知道useEditor、编辑器工具、阶段、模式、paint 模式、floorplan 状态,或任何编辑器专属的展示词汇。编辑器功能通过 props 与 children 注入<Viewer>,方向永远是从外向里。apps/editor拥有编辑体验:工具、useEditor、面板、floorplan 辅助、paint 模式、键盘快捷键、命令面板、动作菜单、光标徽标、编辑器专属覆盖层。
这些规则的细节与实例记录在 wiki/architecture/layers.md、wiki/architecture/viewer-isolation.md、wiki/architecture/systems.md、wiki/architecture/renderers.md、wiki/architecture/tools.md。
viewer 隔离:一个可操作的落地方案
wiki/architecture/viewer-isolation.md 把"viewer 必须编辑器无关"落成了具体可执行的模式:
// 正确模式:控制权从外部传入 —— apps/editor/components/editor-canvas.tsx import { Viewer } from '@pascal-app/viewer' import { ToolManager } from '../tools/tool-manager' import { useEditor } from '../../store/use-editor' export function EditorCanvas() { const { selection } = useViewer() return ( <Viewer theme="light" onSelect={(id) => useViewer.getState().setSelection(id)} onExport={handleExport} > {/* 编辑器以 children 注入工具,viewer 在画布内渲染它们 */} <ToolManager /> </Viewer> ) }而useViewerstore 只允许存放纯展示状态:selection、cameraMode、levelMode、wallMode、theme,以及showScans/showGuides/showGrid等显示开关。凡是只在编辑器内有意义的状态(活动工具、阶段、编辑模式)都必须放进useEditor,而非useViewer。文档还给出了三个自检问题:该功能在只读的/viewer/[id]路由下是否仍然成立?是否引用了useEditor、工具状态或阶段/模式?能否改成以 prop 或 child 传入?任何答案为"编辑器专属"的代码都应留在apps/editor。
这套边界让同一个@pascal-app/viewer既能服务完整的编辑器,也能服务只读预览路由与未来的任意嵌入场景——这是"可独立发货包"能否成立的关键。
四、架构敏感变更:动手前先读哪一页
CLAUDE.md明确要求:在写任何架构敏感的代码之前,先读wiki/architecture/中的对应页面。这是"守则先行"的强制性流程,原文的映射关系如下(可直接作为行动清单):
| 你要做的事情 | 必须先读的文档 |
|---|---|
| 新增节点类型 | node-schemas.md、renderers.md、systems.md |
| 新增工具 | tools.md、spatial-queries.md、events.md |
| 新增/修改放置或移动交互 | tools.md(尤其"2D ↔ 3D 行为对齐":适用行为必须在两个视图中都存在,且同一 PR 内同步移植到对应的 2D/3D 兄弟文件) |
| 新增系统 | systems.md、scene-registry.md |
改动packages/viewer内任何内容 | viewer-isolation.md、layers.md |
| 任何触及选择(selection)的内容 | selection-managers.md、scene-registry.md、events.md |
这条规则的意义在于:仓库把"隐性架构知识"显式化成了按主题索引的文档,Agent 无需靠训练数据里的模糊记忆,而是以仓库内文档为唯一事实源(review-architectureskill 中甚至明确写道 "They are the source of truth, not your training data")。
五、PR 审查:调用review-architecture技能
CLAUDE.md规定,审查 PR 时必须调用review-architecture技能(.agents/skills/review-architecture/SKILL.md)。该技能加载必需的架构页面、抓取 diff、按层分类每个新文件,并按严重程度分组报告发现。仓库中实际存在三个内置技能:.agents/skills/review-architecture/SKILL.md、.agents/skills/open-pr/、.agents/skills/open-pr2/。
从 .agents/skills/review-architecture/SKILL.md 可以看到这套审查流程的完整骨架:
- 加载规则(必做,不可跳过):先读
layers.md、systems.md、renderers.md、tools.md、viewer-isolation.md、node-definitions.md、plugin-authoring.md,再按 diff 涉及领域按需读selection-managers.md、scene-registry.md、spatial-queries.md、node-schemas.md、inspector-field-limits.md、events.md、interaction-scope.md; - 抓取 diff:
gh pr diff <pr>或git diff main...HEAD,再用git diff --name-only main...HEAD列出变更文件以映射规则; - 层级分类(在任何 checklist 之前做):对每个新文件、新类型、新 store 字段回答一个问题——它属于
core、viewer、editor还是nodes?这是最常见也最具破坏性的一类违规,必须在检查清单之前单独做一遍; - 逐项检查清单:包括包边界(
viewer不得导入@pascal-app/editor/@pascal-app/nodes,core不得导入 Three.js / R3F / viewer / editor / nodes)、节点注册表与组合模型、hook 卫生(useEditor/useScene/useViewer)、选择器性能、关注点分离、交互作用域与吸附修饰键约定; - 输出格式:按 Blocker / Suggestion / Nit 三档分组,每条发现包含文件与行号、违规片段、违反的规则(链接到 wiki 页)、具体修复建议;完全合规时也要明说,不虚构 nit;
- 最终总结:各档数量 + 一句结论(可直接合并 / 需要修改 / 需要讨论)+ 需要作者最先打开的文件列表。
审查中常见的 Blocker 类别
该 skill 详细列举了会被判为 Blocker 的典型情况,从中可以提炼出仓库真正的架构底线:
- 放错包:kind 专属代码出现在
packages/viewer、packages/core、packages/editor而非packages/nodes/src/<kind>/;框架包中出现case 'door'|'wall'|'item'…这种按节点类型分支的 switch(分派必须经由nodeRegistry);core/、viewer/、editor/中出现from '@pascal-app/nodes'的导入(依赖箭头是单向的)。 - 组合模型违规:
def.geometry/def.renderer/def.system三个字段是"存在即参与"(presence is participation),没有判别器;geometry 构建器必须纯函数,不得导入useScene、不得变更 store、不得依赖 React context,读取其他节点须经GeometryContext。 - Schema 演进破坏存量场景:新增字段需要 Zod
.default()/.optional();重命名/删除/改类型需要migrateNodes迁移条目,否则.default()会静默丢值——任何 schema 变更都必须保证旧场景仍可加载。 - 性能反模式:顶层组件订阅
useScene(s => s.nodes)这类大而频繁变更的切片;选择器每次调用返回新对象/数组引用;按节点列表渲染时父组件订阅整个transforms/overridesMap(应在每个子组件订阅自己的 ID 切片并保持 memo)。 - 交互契约违规:用
event.shiftKey绕过吸附(约定是 Shift = 循环切换模式、Alt = 强制/自由);未用isGridSnapActive()门控的硬编码网格步长;bespoke mover 打开全局movingscope 造成双重处理。
六、操作规则:人机一致的协作纪律
CLAUDE.md最后给出六条操作规则,它们同时约束人类开发者与 AI Agent,是保证仓库可持续演进的行为底线:
- 编辑前读完整文件;规划好所有变更后,一次性完成编辑;
- 用户纠正你时,停下来重新读对方的消息;
- 连续两次工具失败后,停止并换一种方法;
- 不引入向后兼容垫片(shims)、死代码或投机性抽象;
- 不写新注释,除非它解释一个不显而易见的"为什么"。
这五条中,"Don't introduce backwards-compatibility shims, dead code, or speculative abstractions"与"不写解释不了 why 的注释"尤其具有方法论价值——它把"代码整洁"从风格层面提升到了架构治理层面,与前面"不加无意义字段"的 schema 审查形成呼应。
七、从守则到落地:一篇文档如何驱动整个协作体系
回看 CLAUDE.md 全文,它本质上不是一份"项目介绍",而是一份给 Agent 的入职手册 + 架构宪法 + 工作流入口,三者通过清晰的指针串联:
- 入职:Repo Shape 表告诉你包在哪里、各自干什么;
- 宪法:Layer Boundaries 与"动手前先读文档"的映射表,把架构约束前置到编码之前;
- 入口:
wiki/architecture/(知识库)、.agents/skills/(可执行工作流)、README.md/SETUP.md/CONTRIBUTING.md(人类导航),并通过软链接机制让 Claude / Gemini / Copilot / Codex 共享同一事实源。
对于一个"同时服务人类与 AI Agent"的开源项目(如 README 所述:本地优先的 3D 建筑编辑器,浏览器或 CLI 运行,并通过 MCP 连接 AI Agent),这种"单一事实源 + 按主题索引的架构文档 + 可执行的审查技能 + 显式操作守则"的组合,正是让不同背景的协作者(人、Claude、Codex、Copilot)在同一套规则下高效共事的关键。如果你想为自己的项目建立类似的 Agent 协作体系,直接参照本仓库的AGENTS.md+wiki/architecture/+.agents/skills/三层结构即可快速起步。
【免费下载链接】editorOpen-source 3D architectural editor with a local CLI, MCP tools, and practical workflows for humans and AI agents.项目地址: https://gitcode.com/GitHub_Trending/editor93/editor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考