@routerhub/agent-rules 1.5.152 → 1.5.153
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.base.md +46 -0
- package/package.json +1 -1
- package/rules/global.md +46 -0
package/AGENTS.base.md
CHANGED
|
@@ -188,6 +188,52 @@
|
|
|
188
188
|
- ⚠️ 提交前按「最坏情况」自检:边界值、空值、异常路径、并发/重复触发、重试、依赖不可用(Redis/DB/网络)时,行为是否仍正确、会不会出错或重复执行。
|
|
189
189
|
- ⚠️ 沉淀反模式:每次 review 暴露的设计问题(锁粒度、职责划分、重复实现等),提炼成通用的「反模式」记录,下次写同类代码时自发对照,避免重犯。
|
|
190
190
|
|
|
191
|
+
## ⚠️ 反模式清单(写代码前对照,防止「换件衣服又踩一遍」)
|
|
192
|
+
|
|
193
|
+
「代码质量 → 沉淀反模式」给出机制,本清单是已沉淀的可迁移反模式。写代码前对照一遍,命中即自查。每条都是「同一类坑换框架/换字段名再现」的通用模式,不局限于本例。发现新的坑,当场按此格式回填。
|
|
194
|
+
|
|
195
|
+
### ① 一次性资源消费陷阱:读了就不能再读
|
|
196
|
+
|
|
197
|
+
- ⚠️ 对「同一份输入」做两次读取(读取 + 再读取 / 读取 + 绑定 / 读取 + 解析),必须确认第一次读取不会把源头「消耗掉」。
|
|
198
|
+
- **类比:一瓶汽水只能喝一次。喝完后想再倒一杯,瓶子里已经空了。**
|
|
199
|
+
- 典型反例:Gin `ShouldBindJSON` 会消费 request body,之后再 `GetRawData()` 读到的是空——必须先 `GetRawData()` 把原样存下、还原 body 再 bind。
|
|
200
|
+
- 自查:凡对同一数据做了「先 X 再 Y 的两次读取」,先问 X 是否消费了源头;拿不准就把原样先存一份。
|
|
201
|
+
|
|
202
|
+
### ② 守卫旁路:校验只守了一个入口
|
|
203
|
+
|
|
204
|
+
- ⚠️ 状态变更/停用/删除等「有前置条件的写操作」,必须确保**所有能到达该状态变更的入口**都经过同一套守卫,不能只守 UI/常规路径。
|
|
205
|
+
- **类比:小区只有正门有保安,侧门没锁——坏人从侧门就进去了。**
|
|
206
|
+
- 典型反例:停用守卫只放在 `PATCH .../status`,但 `PUT /vendors/:id` 的请求结构体仍有 `status` 字段,裸 API 传 `status=disabled` 直接绕过守卫。
|
|
207
|
+
- 自查:给某个「状态/权限/开关」加守卫时,先列出**所有能改这个状态的写路径**(PUT/POST/PATCH/脚本/批量),逐个确认都过了守卫。
|
|
208
|
+
|
|
209
|
+
### ③ 字段语义分离:「不带」≠「传空」
|
|
210
|
+
|
|
211
|
+
- ⚠️ 区分「请求没带这个字段」(保持原值)和「带了字段但值为 null/空」(清空/置空)是两种不同的语义,必须分开处理。
|
|
212
|
+
- **类比:顾客没点饮料(维持现状)≠ 顾客点了「不要饮料」(明确要求不给)。**
|
|
213
|
+
- 典型反例:`vendor_id` 不带应保持原归属,显式传 `null` 才清空;若混为一谈,旧调用方不带字段时归属被静默清空。
|
|
214
|
+
- 自查:字段可选时,用「键是否存在」判断语义,而不是「值是否为 null」;两条路径各测一遍。
|
|
215
|
+
|
|
216
|
+
### ④ 缓存失效 ≠ 视图状态恢复:清缓存要连带恢复展开/选中态
|
|
217
|
+
|
|
218
|
+
- ⚠️ 清空缓存后,如果界面上仍有「基于旧缓存展开/选中」的视图状态,必须同步重拉或复位,否则出现「展开但空白」的假象。
|
|
219
|
+
- **类比:刷新冰箱时把饮料全部拿出来,但购物单还勾着「已补货」——你盯着空架子以为饮料没了,其实只是没放回去。**
|
|
220
|
+
- 典型反例:`refreshVendorSidebar` 清空 `vendorAccounts` 缓存但 `expandedVendors` 仍为 true,展开的分组不触发重拉,显示空白像「账户全没了」。
|
|
221
|
+
- 自查:任何「清空缓存」操作,顺手列一遍哪些 UI 状态依赖这份缓存,对激活中的(展开/选中/滚动位置)逐个重拉或复位。
|
|
222
|
+
|
|
223
|
+
### ⑤ 加载状态用独立标记,别拿「数组长度/结果为空」推断
|
|
224
|
+
|
|
225
|
+
- ⚠️ 「是否已加载」要单独用一个布尔标记记录,不要用「数据长度 === 0」推断「还没加载」——真的空数据会被误判成「未加载」,导致重复请求或永不刷新。
|
|
226
|
+
- **类比:柜台空着 ≠ 没营业。营业了但没顾客,和没开门,是两回事,看柜台空判断会搞混。**
|
|
227
|
+
- 典型反例:`if (list.length === 0) fetch()`——真实 0 条数据时每次展开都重复请求;反过来列表非空时切 tab 又永不刷新计数。
|
|
228
|
+
- 自查:用 `loaded` 布尔标记区分「加载过(含空)」与「未加载」;不要用数据本身的有无/长度推断。
|
|
229
|
+
|
|
230
|
+
### ⑥ 白名单优先于黑名单:状态判断写「只允许 active」而非「排除 disabled」
|
|
231
|
+
|
|
232
|
+
- ⚠️ 判断某状态是否可用时,用白名单(`status === 'active'`)而非黑名单(`status !== 'disabled'`)——状态枚举一扩展,黑名单就漏放新状态。
|
|
233
|
+
- **类比:安检只查「名单上列的违禁品」会漏掉新违禁品;「只放行持有效票的人」才兜得住。**
|
|
234
|
+
- 典型反例:`.filter(v => v.status !== 'disabled')` 在将来新增第三种状态(如 `suspended`)时会被误放行,保存必被后端拒绝。
|
|
235
|
+
- 自查:凡「可选/可用/合法」判定,写成「只保留允许的那些」而不是「排除不允许的那些」。
|
|
236
|
+
|
|
191
237
|
## ⚠️ 数据链路改动核对铁律
|
|
192
238
|
|
|
193
239
|
- ⚠️ **给一个数据结构(interface/struct/DTO)新增或修改字段后,必须顺着这份数据流转的每一个转发/序列化点逐一核对,不能只改了数据结构定义或链路两端就算完成**。典型漏改位置是中间层"手写请求体字面量"(如 `JSON.stringify({ a, b })`、手动拼接的 `params`/`payload`),新增字段不会自动带过去,也不会报错,只会表现为"下游一直是空的"这种沉默失败。
|
package/package.json
CHANGED
package/rules/global.md
CHANGED
|
@@ -188,6 +188,52 @@ name: "通用规则"
|
|
|
188
188
|
- ⚠️ 提交前按「最坏情况」自检:边界值、空值、异常路径、并发/重复触发、重试、依赖不可用(Redis/DB/网络)时,行为是否仍正确、会不会出错或重复执行。
|
|
189
189
|
- ⚠️ 沉淀反模式:每次 review 暴露的设计问题(锁粒度、职责划分、重复实现等),提炼成通用的「反模式」记录,下次写同类代码时自发对照,避免重犯。
|
|
190
190
|
|
|
191
|
+
## ⚠️ 反模式清单(写代码前对照,防止「换件衣服又踩一遍」)
|
|
192
|
+
|
|
193
|
+
「代码质量 → 沉淀反模式」给出机制,本清单是已沉淀的可迁移反模式。写代码前对照一遍,命中即自查。每条都是「同一类坑换框架/换字段名再现」的通用模式,不局限于本例。发现新的坑,当场按此格式回填。
|
|
194
|
+
|
|
195
|
+
### ① 一次性资源消费陷阱:读了就不能再读
|
|
196
|
+
|
|
197
|
+
- ⚠️ 对「同一份输入」做两次读取(读取 + 再读取 / 读取 + 绑定 / 读取 + 解析),必须确认第一次读取不会把源头「消耗掉」。
|
|
198
|
+
- **类比:一瓶汽水只能喝一次。喝完后想再倒一杯,瓶子里已经空了。**
|
|
199
|
+
- 典型反例:Gin `ShouldBindJSON` 会消费 request body,之后再 `GetRawData()` 读到的是空——必须先 `GetRawData()` 把原样存下、还原 body 再 bind。
|
|
200
|
+
- 自查:凡对同一数据做了「先 X 再 Y 的两次读取」,先问 X 是否消费了源头;拿不准就把原样先存一份。
|
|
201
|
+
|
|
202
|
+
### ② 守卫旁路:校验只守了一个入口
|
|
203
|
+
|
|
204
|
+
- ⚠️ 状态变更/停用/删除等「有前置条件的写操作」,必须确保**所有能到达该状态变更的入口**都经过同一套守卫,不能只守 UI/常规路径。
|
|
205
|
+
- **类比:小区只有正门有保安,侧门没锁——坏人从侧门就进去了。**
|
|
206
|
+
- 典型反例:停用守卫只放在 `PATCH .../status`,但 `PUT /vendors/:id` 的请求结构体仍有 `status` 字段,裸 API 传 `status=disabled` 直接绕过守卫。
|
|
207
|
+
- 自查:给某个「状态/权限/开关」加守卫时,先列出**所有能改这个状态的写路径**(PUT/POST/PATCH/脚本/批量),逐个确认都过了守卫。
|
|
208
|
+
|
|
209
|
+
### ③ 字段语义分离:「不带」≠「传空」
|
|
210
|
+
|
|
211
|
+
- ⚠️ 区分「请求没带这个字段」(保持原值)和「带了字段但值为 null/空」(清空/置空)是两种不同的语义,必须分开处理。
|
|
212
|
+
- **类比:顾客没点饮料(维持现状)≠ 顾客点了「不要饮料」(明确要求不给)。**
|
|
213
|
+
- 典型反例:`vendor_id` 不带应保持原归属,显式传 `null` 才清空;若混为一谈,旧调用方不带字段时归属被静默清空。
|
|
214
|
+
- 自查:字段可选时,用「键是否存在」判断语义,而不是「值是否为 null」;两条路径各测一遍。
|
|
215
|
+
|
|
216
|
+
### ④ 缓存失效 ≠ 视图状态恢复:清缓存要连带恢复展开/选中态
|
|
217
|
+
|
|
218
|
+
- ⚠️ 清空缓存后,如果界面上仍有「基于旧缓存展开/选中」的视图状态,必须同步重拉或复位,否则出现「展开但空白」的假象。
|
|
219
|
+
- **类比:刷新冰箱时把饮料全部拿出来,但购物单还勾着「已补货」——你盯着空架子以为饮料没了,其实只是没放回去。**
|
|
220
|
+
- 典型反例:`refreshVendorSidebar` 清空 `vendorAccounts` 缓存但 `expandedVendors` 仍为 true,展开的分组不触发重拉,显示空白像「账户全没了」。
|
|
221
|
+
- 自查:任何「清空缓存」操作,顺手列一遍哪些 UI 状态依赖这份缓存,对激活中的(展开/选中/滚动位置)逐个重拉或复位。
|
|
222
|
+
|
|
223
|
+
### ⑤ 加载状态用独立标记,别拿「数组长度/结果为空」推断
|
|
224
|
+
|
|
225
|
+
- ⚠️ 「是否已加载」要单独用一个布尔标记记录,不要用「数据长度 === 0」推断「还没加载」——真的空数据会被误判成「未加载」,导致重复请求或永不刷新。
|
|
226
|
+
- **类比:柜台空着 ≠ 没营业。营业了但没顾客,和没开门,是两回事,看柜台空判断会搞混。**
|
|
227
|
+
- 典型反例:`if (list.length === 0) fetch()`——真实 0 条数据时每次展开都重复请求;反过来列表非空时切 tab 又永不刷新计数。
|
|
228
|
+
- 自查:用 `loaded` 布尔标记区分「加载过(含空)」与「未加载」;不要用数据本身的有无/长度推断。
|
|
229
|
+
|
|
230
|
+
### ⑥ 白名单优先于黑名单:状态判断写「只允许 active」而非「排除 disabled」
|
|
231
|
+
|
|
232
|
+
- ⚠️ 判断某状态是否可用时,用白名单(`status === 'active'`)而非黑名单(`status !== 'disabled'`)——状态枚举一扩展,黑名单就漏放新状态。
|
|
233
|
+
- **类比:安检只查「名单上列的违禁品」会漏掉新违禁品;「只放行持有效票的人」才兜得住。**
|
|
234
|
+
- 典型反例:`.filter(v => v.status !== 'disabled')` 在将来新增第三种状态(如 `suspended`)时会被误放行,保存必被后端拒绝。
|
|
235
|
+
- 自查:凡「可选/可用/合法」判定,写成「只保留允许的那些」而不是「排除不允许的那些」。
|
|
236
|
+
|
|
191
237
|
## ⚠️ 数据链路改动核对铁律
|
|
192
238
|
|
|
193
239
|
- ⚠️ **给一个数据结构(interface/struct/DTO)新增或修改字段后,必须顺着这份数据流转的每一个转发/序列化点逐一核对,不能只改了数据结构定义或链路两端就算完成**。典型漏改位置是中间层"手写请求体字面量"(如 `JSON.stringify({ a, b })`、手动拼接的 `params`/`payload`),新增字段不会自动带过去,也不会报错,只会表现为"下游一直是空的"这种沉默失败。
|