搜索/维护多平台 UI 库,且不破坏其包的分层

维护多平台 UI 库,且不破坏其包的分层

用于参与一个按层拆分、面向多个平台的 UI 库:保持包的分层和已发布的导出不被破坏,按标准文件结构编写外部服务适配器,并遵守发布和评审的规则。

要使用这个AI规则
## 决定每段代码属于哪一个包层 将代码库组织为一小组分层结构,每一层只承担一项职责:以无界面资源形式运行框架钩子的响应式原语;把这些原语桥接到框架状态系统的绑定层;掌管领域逻辑的、与框架无关的核心运行时;以及每个目标平台(Web、原生、终端)各一个的发行包,它重新导出核心并补充该平台特有的 UI 原语。一个私有的组件套件保存 UI 的权威源码,这些源码被复制进使用方项目,而不是作为运行时依赖发布。共享的构建工具链独立成一个内部包。 放置规则: - 与框架无关的逻辑属于核心包。与平台耦合的运行时属于发行包。 - 与框架耦合的运行时放在核心包的一个子路径中,由每个发行包重新导出,这样只存在一份实现,而不是每个平台各一份副本。 - 使用方绝不直接安装核心包;他们只安装恰好一个发行包。 - 把外部提供方映射到核心运行时的集成包自成一层,绝不分叉核心逻辑。 ## 公开导出只增不减,不移除已发布的导出 每个已发布包的公开接口都是只增不减的。当某个符号迁移位置时,把它的导出重新指向新文件;绝不删除已经发布过的导出。仓库内部的审查看不到下游使用方,因此即使移除一个看似无人使用的类型,也属于破坏性变更。真正的行为变更要作为一次独立且有意为之的变更来交付,绝不夹带在移动或重命名之中。 当一次长期迁移用新架构替换遗留实现时,包的入口点在整个迁移期间同时重新导出两条路径,并且依然保持只增不减。在这类迁移期间: - 新行为加在权威接口上,绝不加在已废弃的路径上。 - 复用共享的核心原语,而不是为每个集成各写一份副本。 - 优先复用,而不是再引入一个可选退出开关。 ## 按标准文件结构编写外部服务适配器 集成适配器通过一个入口钩子、一组访问器钩子和若干纯转换函数,把外部提供方映射到核心运行时。基本准则是:运行时文件负责编排,纯模块负责转换,控制器(如果存在)负责归约,钩子只负责读取。 - 核心运行时的选择。当消息派生自提供方所拥有的数据源时,基于外部存储运行时构建;当提供方不持有会话线程状态时,基于本地运行时加聊天模型适配器构建。若需要线程列表,则用共享的多线程运行时把二者之一包裹起来。复用已交付的运行时内核;不要手写一个平行的线程运行时状态持有者。 - 状态暴露。通过共享的运行时扩展工厂把运行时状态暴露给访问器钩子,而不是手写唯一符号标记与守卫。 - 标准文件集。一个只负责编排的运行时入口文件、一个运行时扩展实例、一个访问器与动作钩子文件、一个双向的纯消息转换文件,以及一个类型文件。当适配器自身持有线程状态时,再加一个线程控制器和一个纯状态归约器;当协议自身定义传输格式时,再加一个独立的服务端或 node 子路径入口。 - 仅服务端代码。把仅服务端代码和提供方 SDK 代码放在服务端或 node 子路径内,不要放进默认入口,也不要放进原生入口。 - 测试。测试与各模块放在一起,覆盖双向转换、归约器或控制器,以及每一个访问器钩子。 由提供方决定的选择保持灵活:基于哪个核心原语构建、采用薄封装还是累加器还是控制器、是否使用定制传输、人机协同支持做到多丰富,以及线程列表支持做到多深入。 ## 让各适配器功能一致,转换时不抛出异常 跨运行时的能力对齐。让各适配器之间的能力保持对齐。当某项能力在一个运行时落地时,就要在概念适用的每个运行时上都提供它,否则写清楚其中某个为何有所不同。无声的能力差距,会变成切换适配器的人所遭遇的破坏性预期。 防御式转换。转换函数和内容块渲染器绝不能因字段未定义、字段缺失或平台能力不可用而抛出异常。要防御不符合规范的提供方载荷,并把仅浏览器可用的平台 API 排除在同样会运行于服务端、终端渲染器或原生目标的代码路径之外。 绝不引入的做法: - 与已交付运行时内核重复的、平行的线程运行时状态持有者。 - 用变更通知加版本计数器强行重渲染,以此把非响应式状态桥接进组件树的取巧手法。 - 通过创建原型把方法嫁接到对象上。 - 猴子补丁式地修改调用方所拥有的对象。 ## 用共享构建工具构建,并为每次变更写发布说明 每个可发布的包都通过共享构建工具进行构建。不要添加各包独立的构建配置,也不要直接改用其他打包器或编译器命令行。导出映射只面向模块并以类型优先,类型声明列在默认条件之前,并且各包声明自身无副作用。 - 唯一权威的 UI 源码目录。模板与示例通过编译器路径映射指向它,不保留逐字节相同的副本,除非某个极简起步模板有意自带一份副本。同步脚本让该副本保持逐字节一致,任何有意的差异都要在脚本的覆盖清单中声明。 - 目录形态的约定随意图而不同:当每个原语在两种形态下都存在时使用并行目录;当只有部分文件存在第二种形态时使用稀疏的变体后缀,并以无后缀文件作为默认源。 - 升级编译器工具链或其插件时,重新运行编译器插件的验证任务。仅仅构建通过,并不能证明工具链依然完好。 - 已发布产物在运行时会导入的任何包,都属于运行时依赖,而不是开发依赖,这样打包器才会把它视为外部依赖。放错位置会把该包内联进来,并把无法解析的传递导入拖进使用方的构建。 - 可复制安装的组件入口必须自成一体:把每一个内部组件导入和样式表导入都列为注册表依赖,这样安装器就绝不会落下一个带有无法解析导入的文件。 - 对已发布包的每一次变更都要附带一条发布说明条目。始终使用修订号递增;次版本号和主版本号递增需要维护者批准。私有包不受此限。 ## 不手动修改自动生成的 API 参考文档 整棵生成的 API 参考文档树由其生成脚本所有,而不是靠手工维护。 - 带有整页生成标记的页面完全归生成器所有。生成块内部的手工修改会在下一次运行时被销毁;需要长期留存的说明文字应写进源码文档注释里。 - 必须在重新生成后依然存续的手写内容,只能放在指定的手动槽位或示例槽位中。 - 完全由手工维护的页面必须在其前置元数据紧后面带上跳过自动生成的标记。该标记让页面继续列在版块导航中,并使其免于被清理;缺少它时,一个意料之外的非生成页面会被报告为无人管理且已过期,严格模式随之失败。 - 该树下的版块导航与根导航元数据文件同样是生成的。绝不要手工编辑它们。 - 持续集成会以严格模式重新生成,并在出现任何偏差时失败,因此提交之前先在本地重新生成。 ## 格式交给 lint 和格式化工具,注释尽量少 运行仓库的静态检查工具,并对机械性问题使用其自动修复模式。格式由格式化工具掌管;绝不手工排版,也绝不在评审中挑剔空格或格式的细枝末节。 由于响应式资源建立在框架钩子之上,依赖数组与钩子调用顺序的规则由静态检查工具内置的钩子规则强制执行。要让这些规则能分析某个函数体,该函数必须命名得让框架的工具链把它识别为钩子:把资源提取到一个以钩子前缀命名的函数中并传给资源工厂,并给根创建辅助函数使用具名函数表达式而不是箭头函数。 默认不写任何代码注释。凡是复述代码、记录变更历史或指向当前变更请求的注释一律删除。只有当注释记录了未来读者无法从代码本身还原的原因时才保留它,例如隐藏的不变量、不显而易见的约束,或对上游问题的绕行处理,并且用中性的陈述句写出来。 ## 测试放在模块旁边,不用快照断言 测试与被测模块放在一起,并以相对路径导入该模块,绝不通过包名导入,这样测试始终检验本地源码,而不是解析出来的构建产物。 - 在各自独立的测试文件中覆盖双向转换、归约器或控制器,以及每一个访问器钩子。 - 使用测试运行器的提升式模拟辅助函数来构建模拟,并且始终把原模块展开合并进模拟对象,让未被改动的导出保持真实行为。 - 不要使用快照断言;对真正重要的具体值做断言,这样失败信息才能指明实际的回归点。 ## 每次提交只做一件事,并附上说明和复现步骤 - 一次变更请求只处理一个议题。把大型或多目的的变更拆成若干独立提交,使每一个都小到便于评审、能凭自身价值获得批准,并在出现回归时干净地回滚。 - 写一份真正的说明,而不是自动生成的占位文字。写清楚这次变更是什么、为什么需要、怎么做的,让评审者无需反向推敲代码差异就有足够上下文。标题用一行说明变更内容;取舍与偏离之处写在正文里。 - 对任何偏离仓库既有做法的地方给出理由。如果代码库已经用某种方式解决了某个问题,就沿用它;那种做法是有人刻意选择的,因此换一种做法需要书面理由,而不是个人偏好。 - 不要把无关变更混进代码差异。顺手删除、重构、重新排版以及"既然来了就一起改"的追加内容会淹没真正的变更并拖慢评审。另开一次提交;如果只是想指出某个问题,就开一个 issue。 - 在做非琐碎的功能变更之前先开 issue。一段话就能让维护者确认这个方向是否被需要,从而避免投入大量时间。诸如错别字或小幅文档修改之类的琐碎修复无需开 issue。 - 附上最小复现。每一份缺陷报告或修复都需要在确切版本上可运行的复现。如果维护者无法复现所报告的行为,该报告将被关闭。 - 确认修复针对的是根本原因。验证所报告的失败在应用变更后消失、去掉变更后重现,并说明其机理。靠替换某个值、砍掉某个功能或放宽某个类型来掩盖症状的变更会被拒绝。 - 让文档与生成的参考文档在同一次提交中与代码保持同步。重新生成参考文档,并更新每一个因该变更而结论失效的页面。自动化应当在文档出现偏差时直接失败,而不是任由某个 API 悄无声息地消失,或被推迟到以后的变更中处理。 ## 不做评审会拒绝的改动,如重造框架已有的功能 - 重新发明框架已经提供的东西。基于共享原语来构建,而不是重新实现运行时状态、响应式机制或其他底层管道;平行的机制会与代码库其余部分逐渐脱节,并让所有人付出维护代价。 - 随意添加笨重或非开源的依赖。商业授权的库在开源项目中不可使用,而笨重的依赖或新增的构建步骤会影响每一位贡献者,因此需要理由和维护者的签字同意。 - 交付一个与既有示例重复的新示例应用。先浏览现有示例;如果已经有示例覆盖了该集成,那么一个近乎重复的示例并无新意,除非维护者主动要求,否则它应放在个人的独立仓库中。 - 靠引入用户体验退化来修复缺陷。为掩盖渲染或抖动缺陷而禁用功能、去掉动画或放宽接口,都不是可接受的修复,即便原始报告确有其事。 - 添加工具链已经强制保障的防御性检查。在最严格的编译器设置下(包括精确的可选属性检查),针对空值和可选属性的守卫是多余的。 - 为了合并而绕过必需的检查。在检查清单显示每一行都通过或明确跳过之前,不要强行合并;把通过和跳过的行过滤掉,确认没有剩余项,因为曾经有一份被截断的清单掩盖了一个失败的检查。当某个仓库特有的检查失败或处于等待状态时,要去解决它,而不是绕过它。 ## 不覆盖同一工作区中其他智能体的修改 要假定其他自动化智能体和人类正在同时编辑同一个工作树。编辑之前先查看工作树的当前状态,避免覆盖不是你所做的更改。让每次变更的范围保持紧凑,这样并发进行的工作才能被干净地区分开。 遵守项目的版本控制工作流,而不是想当然地认为可以直接使用原始的版本控制命令。当工作分支表明该项目是通过叠加在版本控制之上的工作流工具来管理时,除非有明确要求,否则不要用直接命令创建分支、暂存文件、提交或改写历史。