cx-pr-review

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CultureX PR Review

CultureX PR审查

How to use this skill

如何使用本技能

You are the reviewer; this skill is a companion, not a substitute for judgment. This file holds everything universal — process, TS/JS/Express-level lenses, router rows, and framework-agnostic traps. Repo-specific mechanics live in per-repo supplements (§0 dispatch):
saas-server.md
,
creator-services.md
, and future repo files following the same pattern.
Operating rules:
  • Checklists are a floor, not a ceiling. Reason from first principles about this change, then cross-check the lists. The best findings are edge cases no checklist covers yet.
  • The trap library is examples, not the whole job. A PR can dodge every listed trap and still be broken. New recurring bug class → add it (§9).
  • Adapt to the diff. Spend effort where the risk is — a docs-only PR needs no SQL generator; a cron PR needs idempotency thinking beyond the table's hints.
  • Adapt to the repo. Confirm the base branch (§0a), identify the repo (§0), load its supplement, and apply only checks that match. Never apply another repo's mechanics — e.g. don't flag a missing
    agencyId
    in a repo without tenancy; find that repo's equivalent scoping and check that.
  • Flag real bugs, risks, design problems — don't nit style (§8 What to skip).
你作为审查者,本技能是辅助工具,不能替代独立判断。本文件包含所有通用规则——流程、TS/JS/Express层面的分析视角、路由检查项、与框架无关的陷阱库。仓库专属的规则在各仓库补充文档(§0 调度)中:
saas-server.md
creator-services.md
,以及后续遵循相同模式的仓库文档。
操作规则:
  • 检查清单是基础,而非上限。从第一性原理出发分析本次变更,再对照清单验证。最有价值的发现往往是清单未覆盖的边缘场景。
  • 陷阱库是示例,而非全部工作。PR可能避开所有列出的陷阱,但仍存在问题。若发现新的重复Bug类型,请添加到陷阱库(§9)。
  • 适配差异内容。将精力放在风险点上——仅修改文档的PR无需检查SQL生成逻辑;定时任务PR需要思考清单提示之外的幂等性问题。
  • 适配目标仓库。确认基准分支(§0a)、识别仓库(§0)、加载对应补充文档,仅应用匹配的检查项。切勿应用其他仓库的规则——例如,在无租户模式的仓库中不要标记缺失
    agencyId
    ,而是找到该仓库对应的范围限制规则并检查。
  • 标记真实Bug、风险、设计问题——不要纠结风格细节(§8 可忽略内容)。

0a. Confirm the base branch (ASK before checking anything)

0a. 确认基准分支(检查任何内容前先询问)

Ask the user which branch the PR merges into before any
git
/
grep
— every diff, log, and re-review delta is computed against it (stale base → reviewing already-merged commits; wrong base → missing or phantom changes). Don't assume
main
. Detect candidates, then confirm:
bash
gh pr view --json baseRefName 2>/dev/null            # if a PR exists this IS the base — still confirm
git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null # repo default branch
git branch -r                                         # what exists (main? master? develop? release?)
  • baseRefName
    found → state it, ask confirm/override (devs sometimes branch off a feature/release branch, not the PR base).
  • No PR → ask explicitly, presenting the detected default as the suggestion.
  • Record as
    <base>
    ; use it in every command below (
    <base>..HEAD
    ). For a re-review, also pin the last-reviewed SHA (§6).
Use AskUserQuestion (or a plain question) — don't proceed to §0/§1 until confirmed.
在执行任何
git
/
grep
命令前,询问用户PR将合并到哪个分支——所有差异、日志和重新审查的增量都是基于该分支计算的(基准分支过时→审查已合并的提交;基准分支错误→遗漏或出现虚假变更)。不要默认是
main
分支。先检测候选分支,再确认:
bash
gh pr view --json baseRefName 2>/dev/null            # 若PR已存在,这就是基准分支——仍需确认
git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null # 仓库默认分支
git branch -r                                         # 现有分支(main? master? develop? release?)
  • 找到
    baseRefName
    →告知用户,询问是否确认/覆盖(开发者有时会从功能分支/发布分支创建分支,而非PR基准分支)。
  • 无PR→明确询问,将检测到的默认分支作为建议。
  • 记录为
    <base>
    ;在以下所有命令中使用(格式为
    <base>..HEAD
    )。若为重新审查,还需记录上次审查的SHA(§6)。
使用AskUserQuestion(或直接提问)——在确认前不要进入§0/§1。

0. Identify the repo & load its supplement (once, first)

0. 识别仓库并加载补充文档(首次执行一次)

RepoFingerprintSupplement
cx-saas-server
sequelize
+
mysql2
in
package.json
;
packages/cx-datastore/
;
config/rbac.config.js
read
saas-server.md
cx-creator-services
mongoose
+
@qdrant/js-client-rest
in
package.json
;
src/db/qdrant/
read
creator-services.md
cx-analytics-backendpackage name
cx-analytics-backend
;
bottleneck
+
axios-retry
+
openai
, no ORM/DB driver;
client/creator-service/
read
analytics-backend.md
cx-workerpackage name
cx-worker
;
sqs-consumer
+
@aws-sdk/client-sns
in
package.json
;
helper/main.helper.js
no supplement yet — SQS consumer, no DB; calls back to saas-server/creator-services via axios. Review = message-contract both sides (§4 Cross-repo), idempotency on redelivery, §5 stuck-task trap
cx-pdf-generationpackage name
cx-creator-service
(sic);
puppeteer
+
serverless-http
in
package.json
no supplement yet — Express+Puppeteer React-SSR PDF service, no DB; universal checks only
cx-partners / saas-super-admin / cx-saas-dashboardReact frontends (
react
+ Radix/TanStack, no server framework)
frontend — this skill's backend mechanics don't apply; §3 lenses + §5 agnostic traps only
any other repofingerprint belowno supplement yet — use the mapping table below + universal checks; propose a new supplement file (§9)
bash
undefined
仓库特征标识补充文档
cx-saas-server
package.json
中包含
sequelize
+
mysql2
;存在
packages/cx-datastore/
;存在
config/rbac.config.js
阅读
saas-server.md
cx-creator-services
package.json
中包含
mongoose
+
@qdrant/js-client-rest
;存在
src/db/qdrant/
阅读
creator-services.md
cx-analytics-backend包名为
cx-analytics-backend
;包含
bottleneck
+
axios-retry
+
openai
无ORM/数据库驱动;存在
client/creator-service/
阅读
analytics-backend.md
cx-worker包名为
cx-worker
package.json
中包含
sqs-consumer
+
@aws-sdk/client-sns
;存在
helper/main.helper.js
暂无补充文档——SQS消费者,无数据库;通过axios调用saas-server/creator-services。审查需注意:消息契约双向一致性(§4 跨仓库)、重投递时的幂等性、§5 任务阻塞陷阱
cx-pdf-generation包名为
cx-creator-service
(拼写如此);
package.json
中包含
puppeteer
+
serverless-http
暂无补充文档——基于Express+Puppeteer的React-SSR PDF服务,无数据库;仅应用通用检查项
cx-partners / saas-super-admin / cx-saas-dashboardReact前端(
react
+ Radix/TanStack,无服务端框架)
前端项目——本技能的后端规则不适用;仅应用§3 分析视角 + §5 通用陷阱
其他任意仓库特征标识如下暂无补充文档——使用下方映射表 + 通用检查项;建议新增补充文档(§9)
bash
undefined

ORM / DB driver — the fleet only uses sequelize+mysql2 (saas-server) and mongoose (creator-services); anything else is a red flag to investigate, not a stack to accommodate

ORM / 数据库驱动——当前仅使用sequelize+mysql2(saas-server)和mongoose(creator-services);其他任何情况需调查,而非适配

grep -iE '"(sequelize|mongoose|mongodb|mysql2?)"' package.json 2>/dev/null ls migrations 2>/dev/null
grep -iE '"(sequelize|mongoose|mongodb|mysql2?)"' package.json 2>/dev/null ls migrations 2>/dev/null

Tenancy / auth convention (what plays the role of a tenant key / rbac config)

租户/认证约定(租户标识/权限配置的对应字段)

grep -rIlE 'agencyId|tenantId|orgId|workspaceId|accountId' src 2>/dev/null | head grep -rIl 'rbac|checkRBAC|authorize|can(' src middleware 2>/dev/null | head

For a repo with no supplement, map what fingerprinting finds — the **principle** is constant, only the mechanism changes. The fleet has exactly two DB stacks:

| Concept | Sequelize + MySQL (saas-server) | Mongoose + Mongo (creator-services) | No DB (analytics-backend, cx-worker, cx-pdf-generation) |
|---|---|---|---|
| Schema change | `migrations/*.js` | none — shape lives in the model file; check back-compat of reads on old docs | N/A — check payload contract vs the owning repo |
| Tenant scoping | `agencyId` in every `where:` | `platform`+`username`/`profile_id` | social identifiers / message fields — never `agencyId` |
| Negation + NULL | `NOT IN (NULL,…)` → zero rows (saas-server.md) | `$ne`/`$nin` with `null` semantics | N/A |
| Soft delete | `paranoid` + `deleted_at` | no global convention — check per-model | N/A |
| Partial indexes | MySQL silently drops them | `partialFilterExpression` quirks (creator-services.md) | N/A |
| Query verification | regenerate SQL (saas-server.md) | reason about aggregation pipeline / `$match` | read the outbound HTTP payload vs the consumer's schema |

With no supplement, the review is §3 lenses + §2 universal rows + §5 agnostic traps. Still thorough — the universal lenses carry it.
grep -rIlE 'agencyId|tenantId|orgId|workspaceId|accountId' src 2>/dev/null | head grep -rIl 'rbac|checkRBAC|authorize|can(' src middleware 2>/dev/null | head

对于无补充文档的仓库,根据特征标识映射对应规则——**核心原则不变,仅实现机制不同**。当前共有三种数据库架构:

| 概念 | Sequelize + MySQL(saas-server) | Mongoose + Mongo(creator-services) | 无数据库(analytics-backend, cx-worker, cx-pdf-generation) |
|---|---|---|---|
|  Schema变更 | `migrations/*.js` | 无——结构定义在模型文件中;检查旧文档读取的向后兼容性 | 不适用——检查负载契约与所属仓库的一致性 |
| 租户范围限制 | 每个`where:`条件中包含`agencyId` | `platform`+`username`/`profile_id` | 社交标识/消息字段——绝不会是`agencyId` |
| 非空判断 + NULL | `NOT IN (NULL,…)` → 返回0行(saas-server.md) | `$ne`/`$nin`的NULL语义处理 | 不适用 |
| 软删除 | `paranoid` + `deleted_at` | 无全局约定——需按模型检查 | 不适用 |
| 部分索引 | MySQL会静默丢弃 | `partialFilterExpression`的特性(creator-services.md) | 不适用 |
| 查询验证 | 生成SQL语句(saas-server.md) | 分析聚合管道 / `$match`逻辑 | 对比出站HTTP负载与消费者的Schema |

无补充文档时,审查内容为§3 分析视角 + §2 通用检查项 + §5 通用陷阱。仍需全面审查——通用分析视角已覆盖核心要点。

1. Scope the diff & understand intent

1. 确定差异范围并理解变更意图

Run in parallel (
<base>
= branch confirmed in §0a):
bash
git log <base>..HEAD --oneline
git diff <base>..HEAD --stat
git status --short
gh pr view --json title,body,number 2>/dev/null || true
Before reading code line-by-line, answer:
  • What is this PR trying to do? (title/body/commits/ticket) — you can't judge correctness without intent.
  • Blast radius? One-module feature vs. shared middleware/model/config/cron — the latter breaks unrelated features.
  • Which categories does it touch? Classify each changed file: Migration/Schema · Model · Repository · Service/Helper · Controller/Route/Validation · Auth/Middleware · Cron/Background job · Cross-repo contract (SNS/SQS, service-to-service calls) · Config/Infra · Docs · Data backfill/DML · Spec-implementing code (formula/PRD → aggregation/ranking) · Test. The repo supplement maps concrete paths to these categories; the category determines which checklists and §2 rows apply.
并行执行以下命令(
<base>
= §0a中确认的分支):
bash
git log <base>..HEAD --oneline
git diff <base>..HEAD --stat
git status --short
gh pr view --json title,body,number 2>/dev/null || true
逐行阅读代码前,先回答以下问题:
  • 本次PR的目标是什么?(标题/描述/提交记录/工单)——脱离意图无法判断正确性。
  • 影响范围? 单模块功能 vs 共享中间件/模型/配置/定时任务——后者可能破坏无关功能。
  • 涉及哪些类别? 对每个变更文件分类:迁移/Schema · 模型 · 仓储层 · 服务/工具 · 控制器/路由/校验 · 认证/中间件 · 定时/后台任务 · 跨仓库契约(SNS/SQS、服务间调用) · 配置/基础设施 · 文档 · 数据回填/DML · 规范实现代码(需求文档→聚合/排名逻辑) · 测试。仓库补充文档会将具体路径映射到这些类别;类别决定适用的检查清单和§2检查项。

2. Universal router — deeper checks by what you see

2. 通用路由规则——根据变更内容深入检查

Scan the diff for these signals; when present, run the linked checks. The repo supplement adds its own rows. Ordered by how often they bite.
If the diff contains…Then also verify
an ID from
req.body
/
req.query
used in a query
The entity belongs to the caller's tenant/scope (supplement defines the key —
agencyId
,
platform
+
username
, …). Guessed/foreign IDs must not cross that boundary.
bulkCreate
/
bulkWrite
/
insertMany
/
Promise.allSettled
/batch loops
Success counts reflect actual writes (dedupe/ignore options skip silently); rejections surfaced not swallowed; field shape consistent across paths. (§5)
an
await
to OpenAI/LLM/HTTP/heavy DB work inside an HTTP handler
Move to a queue (SNS→cx-worker) or respond
202
+ background; idempotency on retry. (§5 sync-job)
a new/changed routeInput validation (Zod) on body/query/params; authZ wired per the repo's convention; standard response shape (
{ success, message?, result? }
); public route intentional.
a change to shared middleware/configBlast radius across all consumers of that key/module.
an update call followed by returning a previously-fetched objectStale in-memory data — re-fetch, mutate, or assert the affected count. (§5)
a controller→service refactor / extractionSide-effect gating drift: analytics/audit events still gated behind the old early-return? (§5)
eslint-disable @typescript-eslint/no-explicit-any
JSON-key typos hidden by
any
(
advancedData
vs
advanceData
); audit
?? d?.foo_typo
fallbacks. (§5)
file upload / MulterDB payload column actually written (not the Multer field object); validation schema includes the column. (§5)
money / credits / payments / webhooksIdempotency keys, transaction boundaries, no double-charge on retry, correct unit, webhook signature verified. Reason hard — the trap table only hints.
a new external API call (another CultureX service / 3rd-party)Rate limits, timeouts, error handling, caching, N+1 (one call per item in a list endpoint → batch). (§5)
LLM prompt + a mapping/enum of its outputPrompt category strings exactly match mapping keys; accept both defensively. (§5)
date/time, timezone, or cron scheduleTZ assumptions, DST, cron overlap/re-entrancy, "today" server-side vs. tenant-side.
a spec/formula/scoring doc the code must implementReproduce the doc's worked examples against the code; check denominator per segment and distributions sum to ~100%; fix landed on every parallel branch. (§5 symmetric-fix gap)
a raw DML backfill / data migration (esp. prod / large table)Normalization parity with the app's matching logic, batching, soft-delete filter, idempotent/resumable, tenant key in the join, query plan checked. Mechanics per supplement.
Signal not in the table (or the supplement's) → expected; fall through to §3 and reason it out.
扫描差异内容,若出现以下信号,则执行对应的检查。仓库补充文档会添加专属检查项。按出现问题的频率排序。
若差异中包含…则需额外验证
使用
req.body
/
req.query
中的ID进行查询
该实体属于调用者的租户/范围(补充文档定义对应标识——
agencyId
platform
+
username
等)。猜测的/外部的ID不得跨越该边界。
bulkCreate
/
bulkWrite
/
insertMany
/
Promise.allSettled
/批量循环
成功计数反映实际写入数量(去重/忽略选项会静默跳过数据);需暴露拒绝错误而非吞掉;各路径的字段结构一致。(§5)
HTTP处理器内包含
await
调用OpenAI/LLM/HTTP/重型数据库操作
移至队列(SNS→cx-worker)或返回
202
+ 后台处理;重试时需保证幂等性。(§5 同步任务陷阱)
新增/修改路由对body/query/params进行Zod输入校验;按仓库约定配置授权;使用标准响应格式(
{ success, message?, result? }
);确认公开路由为有意设置。
修改共享中间件/配置检查该键/模块的所有消费者的影响范围。
更新调用后返回先前获取的对象内存数据过时——需重新获取、修改或断言受影响的计数。(§5)
控制器→服务的重构/提取副作用控制漂移:分析/审计事件是否仍受旧的提前返回逻辑限制?(§5)
eslint-disable @typescript-eslint/no-explicit-any
any
类型隐藏JSON键拼写错误(如
advancedData
vs
advanceData
);检查所有
?? d?.foo_typo
回退逻辑。(§5)
文件上传 / Multer数据库负载列已实际写入(而非Multer字段对象);校验Schema包含该列。(§5)
金额/积分/支付/Webhook幂等键、事务边界、重试时不重复扣费、单位正确、Webhook签名已验证。需仔细分析——陷阱表仅提供提示。
新增外部API调用(其他CultureX服务 / 第三方)速率限制、超时、错误处理、缓存、N+1问题(列表接口中每个条目发起一次调用→需批量处理)。(§5)
LLM提示词 + 输出映射/枚举提示词分类字符串与映射键完全匹配;需兼容两种写法。(§5)
日期/时间、时区或定时任务调度时区假设、夏令时、定时任务重叠/重入、服务器端与租户端的“今日”差异。
代码需实现的规范/公式/评分文档用文档中的示例代码验证实现;检查各分段分母和分布总和约为100%;修复已应用到所有并行分支。(§5 对称修复遗漏陷阱)
原始DML回填 / 数据迁移(尤其是生产环境 / 大表)与应用的匹配逻辑保持标准化一致、分批处理、软删除过滤、幂等/可恢复、连接条件包含租户标识、已检查查询计划。具体机制参考补充文档。
若信号不在本表(或补充文档)中→属正常情况;继续执行§3并自行分析。

3. Independent analysis lenses (EVERY PR)

3. 独立分析视角(所有PR必查)

Checklists are memory aids for known traps; these lenses find the unknown ones. Trace each meaningful change through them — out loud in your notes:
  1. Correctness / intent — does it do what the PR says? Happy path, then empty/zero/null/one/many. Off-by-one, empty array,
    undefined
    key?
  2. Data integrity — inconsistent DB possible? Partial writes without a transaction? Orphans? Lost updates? Wrong counts reported?
  3. Multi-tenancy / scoping — every query scoped to the repo's tenant/scope key? Can caller A reach caller B's data via a guessed ID/slug/UUID?
  4. AuthZ / security — authZ on the route? Privilege escalation path? Input validated (Zod)? Injection (raw SQL, NoSQL, path, SSRF on a URL param)? Secrets/PII in logs or responses?
  5. Concurrency / idempotency — run twice (retry, double-click, redelivered SQS)? Read-modify-write race? Unique constraint or transaction protecting it?
  6. Performance / scale — N+1 queries or service calls? Unbounded list in request/response? Missing index for a new filter/sort? Sync work that should be async?
  7. API contract / back-compat — response shape changed for existing clients? Field renamed without migrating callers? Nullable became required?
  8. Error handling / observability — failures swallowed (
    catch {}
    ,
    .filter('fulfilled')
    )? Success reported when work silently didn't happen? Errors actionable?
  9. Blast radius — shared model/middleware/config/cron: what else consumes it? Did fixing path A regress symmetric path B?
End each lens with: "What's the worst input or timing that breaks this, and is it handled?" That question, not the checklist, is where critical bugs come from.
检查清单是已知陷阱的记忆辅助工具;这些视角能发现未知问题。逐一跟踪每个有意义的变更,并在审查记录中明确说明:
  1. 正确性 / 意图——是否实现了PR描述的功能?验证正常路径,再验证空值/零值/空值/单个/多个场景。是否存在差一错误、空数组、
    undefined
    键?
  2. 数据完整性——是否可能出现数据库不一致?无事务的部分写入?孤立数据?更新丢失?计数错误?
  3. 多租户 / 范围限制——每个查询是否都限定在仓库的租户/范围标识内?调用者A能否通过猜测ID/别名/UUID访问调用者B的数据?
  4. 授权 / 安全——路由是否配置授权?是否存在权限提升路径?输入是否已校验(Zod)?是否存在注入风险(原生SQL、NoSQL、路径、URL参数导致的SSRF)?日志或响应中是否包含密钥/PII?
  5. 并发 / 幂等性——执行两次(重试、双击、SQS重投递)会怎样?是否存在读-改-写竞争?是否有唯一约束或事务保护?
  6. 性能 / 扩展性——是否存在N+1查询或服务调用?请求/响应中是否存在无界列表?新的过滤/排序是否缺少索引?是否有应异步处理的同步工作?
  7. API契约 / 向后兼容性——现有客户端的响应格式是否变更?字段重命名但未迁移调用方?可空字段变为必填?
  8. 错误处理 / 可观测性——是否吞掉错误(
    catch {}
    .filter('fulfilled')
    )?工作未执行却报告成功?错误是否可排查?
  9. 影响范围——共享模型/中间件/配置/定时任务:还有哪些消费者使用?修复路径A是否导致路径B回归?
每个视角结束时需思考:“什么最坏的输入或时序会破坏这个逻辑,是否已处理?” 这个问题(而非检查清单)是发现关键Bug的核心。

4. Universal checklists (the floor)

4. 通用检查清单(基础要求)

Run the ones matching §1. The repo supplement replaces/extends these with stack mechanics (migrations, models, repositories, auth).
执行与§1分类匹配的检查项。仓库补充文档会用架构机制(迁移、模型、仓储、认证)替换/扩展这些内容。

Service / Helper

服务 / 工具

  • Field-shape consistency across paths — path A pushes strings, path B objects → downstream
    .map(x => x.name)
    yields
    undefined
    s
  • No sync LLM/external/heavy calls in the request path — queue (SNS→cx-worker) or
    202
    + background
  • Idempotency on retry-prone work (LLM, payments, webhooks, SQS consumers)
  • Side-effect gating preserved across controller→service refactors
  • Services take specific params (
    agencyId
    /
    userId
    /
    role
    or the repo's equivalents), not whole
    req
    /user objects
  • 字段结构一致性——路径A传入字符串,路径B传入对象→下游
    .map(x => x.name)
    会返回
    undefined
  • 请求路径中无同步LLM/外部/重型调用——移至队列(SNS→cx-worker)或返回
    202
    + 后台处理
  • 易重试工作(LLM、支付、Webhook、SQS消费者)的幂等性
  • 控制器→服务重构时保留副作用控制逻辑
  • 服务接收特定参数(
    agencyId
    /
    userId
    /
    role
    或仓库对应标识),而非完整的
    req
    /用户对象

Controller / Route / Validation

控制器 / 路由 / 校验

  • Zod on
    req.body
    ,
    req.query
    ,
    req.params
  • Field names match what they store (
    postUUID
    shouldn't hold a Mongo
    _id
    )
  • Caller identity/scope (
    type
    /tenant key/role) forwarded from
    req.params
    /
    res.locals
    into the service
  • AuthZ wired per repo convention (supplement has specifics)
  • Standard response
    { success, message?, result? }
  • Public routes (declared before the auth middleware) intentionally public
  • req.body
    req.query
    req.params
    使用Zod校验
  • 字段名与存储内容匹配(
    postUUID
    不应存储Mongo
    _id
  • 调用者身份/范围(
    type
    /租户标识/角色)从
    req.params
    /
    res.locals
    传递到服务层
  • 按仓库约定配置授权(补充文档有具体规则)
  • 使用标准响应格式
    { success, message?, result? }
  • 公开路由(在认证中间件前声明)为有意设置

Cron / Background job

定时 / 后台任务

  • Re-entrancy/overlap — previous run not finished?
  • Tenant iteration scoped; one tenant's failure doesn't abort the rest
  • Idempotent; time/timezone assumptions explicit
  • Heavy work batched/chunked; no unbounded in-memory accumulation
  • 重入/重叠——前一次运行是否未完成?
  • 租户遍历范围受限;单个租户失败不会终止整个任务
  • 幂等性;时间/时区假设明确
  • 重型工作分批处理;无内存中无界累积

Cross-repo contract (SNS/SQS, service-to-service)

跨仓库契约(SNS/SQS、服务间调用)

  • Payload shape matches the consumer (cx-worker message types, …) on both sides
  • Field names/types align with the other store's schema (e.g. MySQL Creator ↔ Mongo)
  • Timeouts, retries, rate limits, error handling; N+1 batched
  • Versioning/back-compat if the message or API shape changed
  • 负载格式与消费者(如cx-worker消息类型)双向匹配
  • 字段名/类型与另一方存储的Schema对齐(如MySQL Creator ↔ Mongo)
  • 超时、重试、速率限制、错误处理;N+1问题已批量处理
  • 消息或API格式变更时的版本控制/向后兼容性

Data backfill / one-off DML (principles; DB mechanics per supplement)

数据回填 / 一次性DML(原则;数据库机制参考补充文档)

  • Normalization parity — the backfill's match/join mirrors exactly how the app matches (case, trimming, prefixes), normalized on the driving side so the indexed side stays usable
  • Batched — no single unbounded write on a hot table
  • Soft-delete filter applied to match app behavior
  • Idempotent / resumable — re-run safe, killed run resumes
  • Tenant key in the join/filter
  • Query plan checked before running
  • 标准化一致性——回填的匹配/连接逻辑与应用完全一致(大小写、修剪、前缀),在驱动端标准化以保持索引端可用
  • 分批处理——热表上无单次无界写入
  • 应用软删除过滤以匹配应用行为
  • 幂等/可恢复——可安全重跑,中断后可恢复
  • 连接/过滤条件包含租户标识
  • 运行前已检查查询计划

5. Framework-agnostic traps (learned the hard way — examples, not exhaustive)

5. 与框架无关的陷阱库(从实际问题中总结——示例,非全部)

Reach for these by name when the pattern matches; the library grows (§9). Stack-specific traps live in the repo supplements.
TrapWhere it bitesFix
Sync LLM / external job inlineHandler
await
s OpenAI / heavy bulk work → timeout + retry-cost amplification
Queue (SNS→cx-worker), or
202
+ background
Promise.allSettled
success theater
Logs rejection but still reports
stats.success = N
Split
succeeded
/
failed
; surface failures
Field-shape inconsistency between paths
[string]
in one path,
[{uuid,name}]
in another →
.map(x=>x.name)
gives
[undefined]
Normalize at source; one shape everywhere
Misleading field name
postUUID
holds a Mongo
_id
Rename to match what's stored
N+1 to another serviceOne
getPosts
call per profile in a list endpoint
Batch endpoint, or precompute/cache the flag
Unbounded id list in HTTP bodyFilter-by-label sends all matching mongo ids in the POST bodyChunk, or push the join into the owning service
LLM key mismatch with mappingPrompt says "Educational Content", mapping has "Educational Content & How-To Guides" → silent dropAlign prompt + JSON output; accept both keys defensively
Side-effect gating drift on extractionOld controller returned early before Mixpanel/audit; new one fires unconditionally → double-countRe-gate with the old early-return condition (e.g.
if (creatorList?.length)
)
JSON-key typo hidden by
any
eslint-disable no-explicit-any
lets
d?.advancedData?.x
(canonical:
advanceData
) silently be
undefined
Audit every
?? d?.foo_typo
fallback; narrow types / remove the disable
Multer field vs DB column mismatch
params.profileImage = imageRes.Location
writes the Multer.File input field; DB payload never gets
profile_image
Write to the DB payload; validation schema includes the column
Symmetric-fix gap (parallel branches)Fix lands on one sibling only — city/country denominator fixed to
totalFollowers
,
age_split
left per-bucket → age summed to 155%
Apply + re-verify ALL parallel branches; distributions must sum to ~100%
Stuck background task with no recoveryWorker chunk never calls back (crash/DLQ/lost SQS) → task
pending
forever; in-progress guard blocks retry — and the
findStuckWorkerTasks
recovery cron was written but never wired
Wire a stuck-task sweep (terminal-state / re-publish); fail the message if the SNS publish throws
当匹配到对应模式时,参考这些陷阱;陷阱库会持续扩充(§9)。架构专属陷阱在仓库补充文档中。
陷阱触发场景修复方案
处理器内同步LLM/外部任务处理器
await
调用OpenAI/重型批量工作→超时+重试成本放大
移至队列(SNS→cx-worker),或返回
202
+ 后台处理
Promise.allSettled
虚假成功
日志记录拒绝但仍报告
stats.success = N
拆分
succeeded
/
failed
;暴露失败信息
路径间字段结构不一致一个路径返回
[string]
,另一个返回
[{uuid,name}]
.map(x=>x.name)
得到
[undefined]
在源头标准化;统一使用一种结构
字段名误导
postUUID
存储Mongo
_id
重命名以匹配存储内容
对其他服务的N+1调用列表接口中每个用户发起一次
getPosts
调用
批量接口,或预计算/缓存标识
HTTP请求体中的无界ID列表按标签过滤时在POST请求体中发送所有匹配的Mongo ID分块处理,或让所属服务执行连接逻辑
LLM输出键与映射不匹配提示词为"Educational Content",映射为"Educational Content & How-To Guides"→静默丢弃对齐提示词+JSON输出;兼容两种键
提取时副作用控制漂移旧控制器提前返回,未触发Mixpanel/审计;新控制器无条件触发→重复计数用旧的提前返回条件重新控制(如
if (creatorList?.length)
any
类型隐藏JSON键拼写错误
eslint-disable no-explicit-any
导致
d?.advancedData?.x
(标准为
advanceData
)静默返回
undefined
检查所有
?? d?.foo_typo
回退逻辑;缩小类型范围 / 移除禁用规则
Multer字段与数据库列不匹配
params.profileImage = imageRes.Location
写入Multer.File输入字段;数据库负载从未写入
profile_image
写入数据库负载;校验Schema包含该列
对称修复遗漏(并行分支)修复仅应用到一个分支——城市/国家分母改为
totalFollowers
age_split
仍按桶计算→年龄分布总和为155%
应用并重新验证所有并行分支;分布总和需约为100%
无恢复机制的后台任务阻塞工作进程块从未回调(崩溃/死信队列/丢失SQS消息)→任务永远处于
pending
;进行中保护阻止重试——且
findStuckWorkerTasks
恢复定时任务已编写但未配置
配置阻塞任务清理(终端状态 / 重新发布);若SNS发布失败则标记消息为失败

6. Re-review protocol

6. 重新审查流程

"The dev fixed X, re-review" → re-read the actual files (
Read
/
grep
). Never trust commit messages, prior memory, or your own prior findings — re-verify each against resolved source (§8 evidence gate); a prior pass can be wrong. If new commits double down on something you flagged, re-check your claim, not escalate.
  • Previously flagged issue actually resolved in code?
  • Fix regress the symmetric/other path?
  • Fix itself introduce a new bug? e.g.
    [Op.not]: ""
    [Op.notIn]: [null, ""]
    turned a partial filter into "zero rows on MySQL".
  • New files/commits since last review?
  • Any query fix → regenerate and read the actual query (supplement's generator/one-liners).
Pin the delta. Record the last-reviewed SHA; re-review =
git diff <last-sha>..HEAD
+
git log <last-sha>..HEAD
, not a fresh
<base>..HEAD
. The start-of-session
gitStatus
snapshot and commit-message claims are not ground truth — reconcile with the PR's remote head (
gh pr view <n> --json headRefOid
): the dev may have committed locally (your HEAD advanced) or pushed (your checkout stale). Establish the real delta, then verify each prior finding against it.
Most common re-review miss: a fix applied to one of several parallel branches. Confirm every sibling case got it, and invariants still hold (distribution sums to ~100%). See §5 symmetric-fix gap.
“开发者已修复X,重新审查”→重新读取实际文件
Read
/
grep
)。永远不要信任提交信息、先前记忆、或你自己之前的发现——需针对已修复的源码重新验证(§8 证据验证);之前的审查可能有误。若新提交强化了你标记的问题,请重新检查你的结论,而非直接升级问题。
  • 之前标记的问题是否已在代码中实际修复?
  • 修复是否导致对称/其他路径回归?
  • 修复本身是否引入新Bug? 例如
    [Op.not]: ""
    [Op.notIn]: [null, ""]
    将部分过滤变为“MySQL返回0行”。
  • 自上次审查后是否有新文件/提交?
  • 任何查询修复→生成并读取实际查询语句(补充文档的生成器/单行命令)。
锁定增量范围。记录上次审查的SHA;重新审查范围为
git diff <last-sha>..HEAD
+
git log <last-sha>..HEAD
,而非重新执行
<base>..HEAD
。会话开始时的
gitStatus
快照和提交信息并非事实——需与PR的远程头(
gh pr view <n> --json headRefOid
)核对:开发者可能已本地提交(你的HEAD已更新)或推送(你的检出版本过时)。确定真实增量后,针对每个先前发现的问题进行验证。
重新审查最常见的遗漏:修复仅应用到部分并行分支。确认所有关联场景都已修复,且不变量仍保持(分布总和约为100%)。见§5 对称修复遗漏陷阱。

7. Verification

7. 验证

Don't reason about generated queries or call graphs from memory — verify cheaply with greps and query generators. Each repo supplement carries its stack's one-liners (saas-server.md: Sequelize→SQL generator, tenancy greps; creator-services.md: index-drift script, payload-index greps).
不要凭记忆推断生成的查询或调用图——用grep和查询生成器快速验证。每个仓库补充文档包含对应架构的单行命令(saas-server.md:Sequelize→SQL生成器、租户grep;creator-services.md:索引漂移脚本、负载索引grep)。

8. Categorize findings & output

8. 分类发现并输出结果

Severity labels (plain words, no emoji/icons):
  • Blocker — data loss, authZ/tenant bypass, security hole, production timeout, silent data corruption
  • High — perf regression, edge-case correctness, broken UX path, back-compat break
  • Lower priority — naming, structure, minor inconsistency
  • Verified OK — what you checked and confirmed (gives the dev confidence in the rest)
严重程度标签(纯文字,无表情/图标):
  • 阻塞——数据丢失、授权/租户绕过、安全漏洞、生产环境超时、静默数据损坏
  • 高优先级——性能回归、边缘场景正确性、用户路径断裂、向后兼容性破坏
  • 低优先级——命名、结构、轻微不一致
  • 验证通过——你已检查并确认的内容(给开发者信心)

Evidence gate — clear before writing ANY Blocker/High

证据验证——标记任何阻塞/高优先级问题前必须确认

A false blocker is worse than a missed bug — it burns trust and can launch a regression. Any absolute claim ("always null", "never called", "dead/unused", "field doesn't exist", "returns zero rows", "this regresses X") must clear:
  • Never infer a structure's full shape or a symbol's call graph from a diff hunk. Diffs are windowed — other keys, an
    exports
    line, another caller routinely sit just outside the shown context. A
    return {
    block cut off before
    }
    is not the full object. (Real case:
    cxScore.attributes
    mislabeled "always undefined" —
    attributes: payload
    sat three lines below the hunk, on
    main
    all along.)
  • Verify against resolved source, cheaply: grep where the symbol is assigned/returned/exported (not just read), then Read the full enclosing block. "X doesn't exist" → grep the producer side; "never called" → grep callers repo-wide; "zero rows" → regenerate the query (§7).
  • A finding that contradicts shipped
    main
    behavior flags your own premise first.
    If
    main
    already reads that field and shipped, disprove yourself before writing it. Dev "doubling down" → re-verify your claim.
  • Can't verify → can't assert. Downgrade to Lower-priority phrased as a question — "suspected X; verify by
    <exact grep/read>
    "
    . Confidence must match evidence.
Write findings to
<TOPIC>_REVIEW.md
at repo root.
Output style: plain, tight, scannable. No emoji, no bold/decorative headers — plain severity labels only. No TL;DR padding, no empty "none" sections. One finding per line; write only what the dev needs to act. Applies to the chat summary too.
Re-review: append-only file — new section on top stamped with the delta range (
<last-sha>..HEAD
), fixed vs. still-open; don't rewrite earlier rounds.
markdown
undefined
虚假阻塞比遗漏Bug更糟——会破坏信任,甚至导致回归。任何绝对断言(如“始终为null”、“从未调用”、“已废弃/未使用”、“字段不存在”、“返回0行”、“此变更导致X回归”)必须满足:
  • 切勿从差异片段推断结构的完整形态或符号的调用图。差异是局部的——其他键、
    exports
    行、其他调用者通常在显示范围之外。
    return {
    块在
    }
    前被截断并不代表完整对象。(真实案例:
    cxScore.attributes
    被错误标记为“始终未定义”——
    attributes: payload
    位于差异片段下方3行,一直存在于
    main
    分支。)
  • 基于已解析源码快速验证:grep符号的赋值/返回/导出位置(而非仅读取位置),然后读取完整的封闭块。“X不存在”→grep生产者端;“从未调用”→全仓库grep调用者;“返回0行”→生成查询语句(§7)。
  • 若发现与已上线
    main
    分支行为矛盾的问题,先质疑自己的假设
    。若
    main
    分支已读取该字段并上线,先推翻自己的结论再记录。开发者“坚持己见”→重新验证你的结论。
  • 无法验证→无法断言。降级为低优先级,以问题形式表述——如“怀疑存在X;请通过
    <具体grep/读取操作>
    验证”。信心需与证据匹配。
将发现写入仓库根目录的
<TOPIC>_REVIEW.md
输出风格:简洁、紧凑、易扫描。无表情、无加粗/装饰性标题——仅使用纯文字严重程度标签。无TL;DR冗余内容,无空的“无”章节。每个发现单独一行;仅记录开发者需要处理的内容。聊天摘要也需遵循此风格。
重新审查:文件采用追加模式——在顶部添加新章节,标记增量范围(
<last-sha>..HEAD
)、已修复vs仍未解决的问题;不要重写之前的审查记录。
markdown
undefined

<PR topic> — review (<base>..<head>)

<PR主题> — 审查记录 (<base>..<head>)

Verdict: <one line — good to merge, or the one thing blocking it>.
Blockers (must fix before merge)
  • <headline>.
    file:line
    (
    identifier
    ) — symptom → root cause → fix.
High
Lower priority
Verified OK
  • <what you checked and confirmed>
Deferred (per author)
  • <anything the user told you to ignore>

Each line: `- [ ]` checkbox (GitHub-pasteable) · `file:line` + offending identifier in backticks · symptom → cause → fix. State confidence honestly; if suspected-not-verified, say so with the exact check.
结论:<一句话——可合并,或阻塞合并的核心问题>.
阻塞问题(合并前必须修复)
  • <标题>.
    file:line
    (
    标识符
    ) — 症状 → 根本原因 → 修复方案.
高优先级
低优先级
验证通过
  • <你已检查并确认的内容>
延期处理(按开发者要求)
  • <用户要求忽略的内容>

每行格式:`- [ ]` 复选框(可粘贴到GitHub) · `file:line` + 反引号包裹的问题标识符 · 症状 → 原因 → 修复方案。如实记录信心;若为怀疑未验证的问题,需说明具体检查方式。

What to skip

可忽略内容

  • Style/formatting, comment typos (unless semantic), LSP-safe renames
  • "Pre-existing in
    main
    " issues — note, don't block
  • Anything explicitly deferred (track under "Deferred per author")
  • 风格/格式、注释拼写错误(除非语义错误)、LSP安全重命名
  • main
    分支已存在的问题——记录但不阻塞合并
  • 明确延期处理的内容(记录在“按开发者要求延期处理”下)

9. Keep the skill learning

9. 持续优化本技能

The skill improves by accumulating real traps, not theory.
  • New recurring bug class → one-line trap row (+ a router row if file-type-triggered): trap → where it bites → fix. Framework-agnostic → this file's §5; stack-specific → the repo's supplement.
  • Reviewing a repo with no supplement and finding repo-specific conventions worth encoding → propose a new
    <repo>.md
    supplement (fingerprint + stack mapping + checks by diff signal + one-liners, like the existing two) and add its row to the §0 dispatch table.
  • Trap stops applying (pattern removed, framework upgraded) → prune it.
  • Don't encode one-offs or anything lint/types/tests already enforce.
本技能通过积累实际陷阱而非理论来改进。
  • 新的重复Bug类型→添加一行陷阱记录(若为文件类型触发,需同时添加路由规则):陷阱→触发场景→修复方案。与框架无关的陷阱→添加到本文件的§5;架构专属陷阱→添加到对应仓库的补充文档。
  • 审查无补充文档的仓库时,若发现值得固化的仓库专属约定→建议新增
    <repo>.md
    补充文档(包含特征标识 + 架构映射 + 差异信号检查项 + 单行命令,参考现有文档),并添加到§0调度表中。
  • 陷阱不再适用(模式已移除、框架已升级)→删除。
  • 不要添加一次性问题或已被lint/类型检查/测试覆盖的内容。