@routerhub/agent-rules 1.5.160 → 1.5.161
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 +16 -0
- package/package.json +1 -1
- package/rules/global.md +16 -0
package/AGENTS.base.md
CHANGED
|
@@ -206,6 +206,7 @@
|
|
|
206
206
|
- **类比:小区只有正门有保安,侧门没锁——坏人从侧门就进去了。**
|
|
207
207
|
- 典型反例:停用守卫只放在 `PATCH .../status`,但 `PUT /vendors/:id` 的请求结构体仍有 `status` 字段,裸 API 传 `status=disabled` 直接绕过守卫。
|
|
208
208
|
- 自查:给某个「状态/权限/开关」加守卫时,先列出**所有能改这个状态的写路径**(PUT/POST/PATCH/脚本/批量),逐个确认都过了守卫。
|
|
209
|
+
- 反例 2(普通编辑路径绕过守卫):状态类字段常可经多条 API 写入——「发布/下架」走专门的 `UpdateStatus` API 才做校验与联动清理,但**普通编辑 API**(如 `UpdateModel`)也能直接写入状态类字段(如布尔 `coming_soon`)、不与 status 冲突校验。运营在一个已发布(published)模型上误勾 Coming Soon 保存 → 官网列表按预告态渲染成置灰不可点击,详情页却在线,同一模型两种状态互相矛盾。凡状态/展示类字段能被多个写入口写入,每个入口都要做同套校验与联动,不能只守专门的 `UpdateStatus`。
|
|
209
210
|
|
|
210
211
|
### ③ 字段语义分离:「不带」≠「传空」
|
|
211
212
|
|
|
@@ -242,6 +243,21 @@
|
|
|
242
243
|
- 典型反例:官网模型列表原查询 `status='published' AND is_active=true`(白名单守卫正确),新增 coming_soon 预告字段时改成 `(status='published' AND is_active=true) OR coming_soon=true`,coming_soon 分支完全绕过 status/is_active 检查——已下架(archived)/已禁用(is_active=false)的模型仅靠预告标记仍被官网返回。修复要双层堵漏:查询分支补守卫 + 状态变更(发布/下架/启停)联动清预告标记。
|
|
243
244
|
- 自查(改旧代码与写新代码同样适用):凡是「白名单 + OR」查询,把每个 OR 分支单独拿出来逐个核对——它是否覆盖了原白名单的全部守卫字段?不满足新分支条件的行最终落在哪个分支、会不会从守卫上漏过去?动到展示查询的 OR 分支时,先确认旧分支的守卫没被新分支绕过。
|
|
244
245
|
- 变体:新增布尔标记字段参与「是否展示/放行」判定时,必须在引入时同时处理与既有状态字段的交叉——要么查询分支补守卫,要么状态变更时联动清理该标记,两者至少做一个、最好都做。
|
|
246
|
+
- 自查补强(状态交叉矩阵):新增展示/放行字段时,列出它与 status、is_active 的**全部组合**(如 published+标记 / draft+标记 / archived+标记 / 禁用+标记),逐个确认每个组合的展示语义——只盯着正常路径(published+标记=false)验证,会漏掉组合冲突:draft+coming_soon 的模型从未发布就被公开、published+coming_soon 列表置灰但详情在线,都是真实踩过的坑。
|
|
247
|
+
|
|
248
|
+
### ⑧ 调用成功判定要看返回体语义,不能只看状态码区间:2xx ≠ 一定成功
|
|
249
|
+
|
|
250
|
+
- ⚠️ 调用下游/内部接口后判断成败时,若返回体里有比 HTTP 状态码更精确的成功语义(`ok`/`success`/业务码/errors 字段),必须按返回体判断;禁止只看「状态码落在 2xx 区间」就当成功——部分 2xx(如 207 Multi-Status)和自定义状态码表达的是「路径不存在 / 部分成功 / 已被处理」,当成功处理会让「调用了但没生效」变成本质上的静默故障。
|
|
251
|
+
- **类比:给同事发消息请他把文件放到某文件夹,他回「收到」(2xx),你当办成了——但消息正文写着「这个文件夹不存在,放不了」。要看回复内容,不能只看他回没回。**
|
|
252
|
+
- 典型反例:Next.js `res.revalidate` 对不存在的路径直接 reject 并返回 HTTP 207;后端判断「状态码 < 300」就记「刷新成功」,实际 revalidate 路径与官网真实详情路径永远不一致,导致全站旗舰模型详情页静默最长 5 分钟不更新、日志零报错。
|
|
253
|
+
- 自查:对每处「调用后判断成败」的代码,先问——这个接口返回体里有没有比状态码更精确的成功/失败语义?「路径不存在 / 部分成功 / 已被处理过」这类既不报错、也不是真成功的边界,我的判断条件会不会误收进来?
|
|
254
|
+
|
|
255
|
+
### ⑨ 数组下标访问前先判空:`arr[0]` 不防「空数组」
|
|
256
|
+
|
|
257
|
+
- ⚠️ 对接口/数据库返回的数组做下标访问(`arr[0].field` / `arr[index]`)前,必须确认数组非空或加长度守卫——空列表是合法状态(全部下架、首次部署、筛选无结果、接口返回空集合),直接下标访问会崩溃(TypeError)或渲染 undefined。
|
|
258
|
+
- **类比:餐厅叫号,你以为第一组客人一定存在,直接喊「1 号顾客请用餐」——大厅一个客人都没有,广播系统当场崩了。**
|
|
259
|
+
- 典型反例:官网首页 hero 用「后端模型列表取前 N 个」的 `heroModels[0].video` 做渲染,后端返回空列表(全部模型下架 / 全新部署)时整页白屏,构建期直接构建失败。
|
|
260
|
+
- 自查:凡对动态数组做下标访问,先问「这个数组会不会是空」?会 → 加「长度 > 0」守卫或用 `.find()`/首元素判空兜底,空数组渲染空态而不是崩溃。
|
|
245
261
|
|
|
246
262
|
## ⚠️ 网关后端编码铁律(来自 PR 评审沉淀)
|
|
247
263
|
|
package/package.json
CHANGED
package/rules/global.md
CHANGED
|
@@ -206,6 +206,7 @@ name: "通用规则"
|
|
|
206
206
|
- **类比:小区只有正门有保安,侧门没锁——坏人从侧门就进去了。**
|
|
207
207
|
- 典型反例:停用守卫只放在 `PATCH .../status`,但 `PUT /vendors/:id` 的请求结构体仍有 `status` 字段,裸 API 传 `status=disabled` 直接绕过守卫。
|
|
208
208
|
- 自查:给某个「状态/权限/开关」加守卫时,先列出**所有能改这个状态的写路径**(PUT/POST/PATCH/脚本/批量),逐个确认都过了守卫。
|
|
209
|
+
- 反例 2(普通编辑路径绕过守卫):状态类字段常可经多条 API 写入——「发布/下架」走专门的 `UpdateStatus` API 才做校验与联动清理,但**普通编辑 API**(如 `UpdateModel`)也能直接写入状态类字段(如布尔 `coming_soon`)、不与 status 冲突校验。运营在一个已发布(published)模型上误勾 Coming Soon 保存 → 官网列表按预告态渲染成置灰不可点击,详情页却在线,同一模型两种状态互相矛盾。凡状态/展示类字段能被多个写入口写入,每个入口都要做同套校验与联动,不能只守专门的 `UpdateStatus`。
|
|
209
210
|
|
|
210
211
|
### ③ 字段语义分离:「不带」≠「传空」
|
|
211
212
|
|
|
@@ -242,6 +243,21 @@ name: "通用规则"
|
|
|
242
243
|
- 典型反例:官网模型列表原查询 `status='published' AND is_active=true`(白名单守卫正确),新增 coming_soon 预告字段时改成 `(status='published' AND is_active=true) OR coming_soon=true`,coming_soon 分支完全绕过 status/is_active 检查——已下架(archived)/已禁用(is_active=false)的模型仅靠预告标记仍被官网返回。修复要双层堵漏:查询分支补守卫 + 状态变更(发布/下架/启停)联动清预告标记。
|
|
243
244
|
- 自查(改旧代码与写新代码同样适用):凡是「白名单 + OR」查询,把每个 OR 分支单独拿出来逐个核对——它是否覆盖了原白名单的全部守卫字段?不满足新分支条件的行最终落在哪个分支、会不会从守卫上漏过去?动到展示查询的 OR 分支时,先确认旧分支的守卫没被新分支绕过。
|
|
244
245
|
- 变体:新增布尔标记字段参与「是否展示/放行」判定时,必须在引入时同时处理与既有状态字段的交叉——要么查询分支补守卫,要么状态变更时联动清理该标记,两者至少做一个、最好都做。
|
|
246
|
+
- 自查补强(状态交叉矩阵):新增展示/放行字段时,列出它与 status、is_active 的**全部组合**(如 published+标记 / draft+标记 / archived+标记 / 禁用+标记),逐个确认每个组合的展示语义——只盯着正常路径(published+标记=false)验证,会漏掉组合冲突:draft+coming_soon 的模型从未发布就被公开、published+coming_soon 列表置灰但详情在线,都是真实踩过的坑。
|
|
247
|
+
|
|
248
|
+
### ⑧ 调用成功判定要看返回体语义,不能只看状态码区间:2xx ≠ 一定成功
|
|
249
|
+
|
|
250
|
+
- ⚠️ 调用下游/内部接口后判断成败时,若返回体里有比 HTTP 状态码更精确的成功语义(`ok`/`success`/业务码/errors 字段),必须按返回体判断;禁止只看「状态码落在 2xx 区间」就当成功——部分 2xx(如 207 Multi-Status)和自定义状态码表达的是「路径不存在 / 部分成功 / 已被处理」,当成功处理会让「调用了但没生效」变成本质上的静默故障。
|
|
251
|
+
- **类比:给同事发消息请他把文件放到某文件夹,他回「收到」(2xx),你当办成了——但消息正文写着「这个文件夹不存在,放不了」。要看回复内容,不能只看他回没回。**
|
|
252
|
+
- 典型反例:Next.js `res.revalidate` 对不存在的路径直接 reject 并返回 HTTP 207;后端判断「状态码 < 300」就记「刷新成功」,实际 revalidate 路径与官网真实详情路径永远不一致,导致全站旗舰模型详情页静默最长 5 分钟不更新、日志零报错。
|
|
253
|
+
- 自查:对每处「调用后判断成败」的代码,先问——这个接口返回体里有没有比状态码更精确的成功/失败语义?「路径不存在 / 部分成功 / 已被处理过」这类既不报错、也不是真成功的边界,我的判断条件会不会误收进来?
|
|
254
|
+
|
|
255
|
+
### ⑨ 数组下标访问前先判空:`arr[0]` 不防「空数组」
|
|
256
|
+
|
|
257
|
+
- ⚠️ 对接口/数据库返回的数组做下标访问(`arr[0].field` / `arr[index]`)前,必须确认数组非空或加长度守卫——空列表是合法状态(全部下架、首次部署、筛选无结果、接口返回空集合),直接下标访问会崩溃(TypeError)或渲染 undefined。
|
|
258
|
+
- **类比:餐厅叫号,你以为第一组客人一定存在,直接喊「1 号顾客请用餐」——大厅一个客人都没有,广播系统当场崩了。**
|
|
259
|
+
- 典型反例:官网首页 hero 用「后端模型列表取前 N 个」的 `heroModels[0].video` 做渲染,后端返回空列表(全部模型下架 / 全新部署)时整页白屏,构建期直接构建失败。
|
|
260
|
+
- 自查:凡对动态数组做下标访问,先问「这个数组会不会是空」?会 → 加「长度 > 0」守卫或用 `.find()`/首元素判空兜底,空数组渲染空态而不是崩溃。
|
|
245
261
|
|
|
246
262
|
## ⚠️ 网关后端编码铁律(来自 PR 评审沉淀)
|
|
247
263
|
|