pr-check-static
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePR Check — Static Analysis (B1–B21)
PR检查——静态分析(B1–B21)
Overview
概述
Checks for runtime bugs, silent logic errors, unused code, and test correctness — things that compile fine but break at runtime or make tests unreliable.
检查运行时bug、静默逻辑错误、未使用代码以及测试正确性——这些问题在编译时不会报错,但会在运行时崩溃或导致测试不可靠。
Required Workflow
必备工作流
Step 1: Understand the diff
步骤1:理解代码差异
Run or read the list of modified files to understand the scope.
git diff main...HEAD运行或查看修改文件列表,了解变更范围。
git diff main...HEADStep 2: Apply the Checklist
步骤2:应用检查清单
Work through each category. Flag every issue with file path and line number.
逐一检查每个分类。标记每个问题的文件路径和行号。
Checklist
检查清单
B1. Module Initialisation Timing
B1. 模块初始化时机
- Are any declarations at module top-level (outside a class)?
const FOO = process.env.BAR - Env vars must be read via at runtime, not
ConfigServiceat import time.process.env - Pattern to flag: at top of file (not inside a class method or factory).
const FOO = process.env.BAR
- 是否有这样的声明位于模块顶层(类外部)?
const FOO = process.env.BAR - 环境变量必须在运行时通过读取,而非在导入时通过
ConfigService读取。process.env - 需要标记的模式:文件顶层的(不在类方法或工厂函数内部)。
const FOO = process.env.BAR
B2. Silent Logic Bugs
B2. 静默逻辑错误
- chains between non-empty string constants always resolve to the first truthy operand — use
||array or union type instead.as const - Optional fields () used in comparisons like
field?: Tsilently returnfield <= 0whenfalse.undefined - Check if reordering a chain changed which value is evaluated first.
||
- 非空字符串常量之间的链式判断始终会解析为第一个真值操作数——请改用
||数组或联合类型。as const - 可选字段()用于
field?: T这类比较时,当值为field <= 0会静默返回undefined。false - 检查链式判断的顺序调整是否改变了优先求值的对象。
||
B3. Unused Code
B3. 未使用代码
- Unused imports — especially after refactors.
- Variables assigned an initial value that is always overwritten before first read — declare without initial value.
- 未使用的导入——尤其是重构之后的。
- 变量被赋予初始值,但在首次读取前总是被覆盖——请声明时不要加初始值。
B4. Test Assertion No-Ops
B4. 测试断言无操作
- Scan all test files for without
.toHaveBeenCalled.() - Same for ,
.toBeDefined,.toBeTruthy,.toBeFalsy— all require.toBeNull.()
- 扫描所有测试文件,查找不带的
()。.toHaveBeenCalled - 、
.toBeDefined、.toBeTruthy、.toBeFalsy同理——都必须带.toBeNull。()
B5. Flaky Tests from Random Data
B5. 随机数据导致的不稳定测试
- Tests where generates a value that could randomly collide with a hardcoded value in the mock or the system under test.
faker - Use explicit hardcoded values for boundary conditions.
- 测试中生成的值可能与模拟数据或被测系统中的硬编码值随机冲突。
faker - 边界条件请使用明确的硬编码值。
B6. Redundant Type Definitions
B6. 冗余类型定义
- New interfaces structurally identical to existing types — search before defining.
- 新定义的接口与现有类型在结构上完全一致——定义前请先搜索。
B7. Wrong Test Title
B7. 错误的测试标题
- Test description says "X fails" but the mock actually simulates "Y fails".
- Read the test name and then read the /
mockRejectedValuecalls — they must match.mockResolvedValue
- 测试描述写的是“X失败”,但模拟实际是“Y失败”。
- 阅读测试名称,再查看/
mockRejectedValue调用——两者必须匹配。mockResolvedValue
B8. Missing Critical Assertions
B8. 缺失关键断言
- Happy-path tests: are all critical side effects asserted (state changes, service calls, DB writes)?
- Guard/skip tests: are downstream methods asserted ?
.not.toHaveBeenCalled()
- 正常流程测试:是否所有关键副作用都有断言(状态变更、服务调用、数据库写入)?
- 守卫/跳过测试:是否断言下游方法?
.not.toHaveBeenCalled()
B9. URL Path Construction
B9. URL路径构造
- Same path string used for both signature generation and the actual HTTP call?
- Base URL missing a required path prefix (e.g., )?
/api
- 签名生成和实际HTTP调用是否使用了相同的路径字符串?
- 基础URL是否缺少必要的路径前缀(例如)?
/api
B10. Status Mapper Completeness
B10. 状态映射器完整性
- Does at least one status value map to each terminal internal state (,
'finalised')?'failed' - When a mapper returns or an unknown status, is that logged + Sentry notified?
null
- 每个终端内部状态(、
'finalised')是否至少有一个状态值与之映射?'failed' - 当映射器返回或未知状态时,是否有日志记录并通知Sentry?
null
B11. API Response Envelope Type Accuracy
B11. API响应信封类型准确性
- TypeScript type for each API response must match the actual envelope key from the API docs.
- Don't assume — verify:
{ data: [] },{ orders: [] },{ order: {} }, etc.{ shipments: [] }
- 每个API响应的TypeScript类型必须与API文档中的实际信封键匹配。
- 不要默认是——请验证:
{ data: [] }、{ orders: [] }、{ order: {} }等。{ shipments: [] }
B12. Webhook Controller Must Not Swallow All Exceptions
B12. Webhook控制器不得吞噬所有异常
- Blanket try-catch returning means failed webhooks are permanently lost.
200 OK - Unexpected errors should propagate or return 4xx/5xx so the provider retries.
- 用try-catch直接返回会导致失败的Webhook永久丢失。
200 OK - 意外错误应向上传播或返回4xx/5xx状态码,以便提供者重试。
B13. No-Op Stubs Must Have No Side Effects
B13. 无操作存根不得有副作用
- A deferred/stub method must not still call , fire notifications, or write DB records.
updateLastSync - A true no-op: only a comment or .
return
- 延迟/存根方法不得仍调用、发送通知或写入数据库记录。
updateLastSync - 真正的无操作:仅包含注释或语句。
return
B14. Duplicate Error Handling in Strategy and Caller
B14. 策略与调用者中重复的错误处理
- If a method catches, logs, Sentries, and rethrows — the caller must NOT catch and log the same error again.
- Double-catch = duplicate audit log entries + duplicate Sentry alerts.
- 如果某个方法捕获、记录、上报Sentry并重新抛出错误——调用者不得再次捕获并记录相同的错误。
- 双重捕获会导致重复的审计日志条目和重复的Sentry告警。
B15. Enum Constants in Comparisons
B15. 比较中使用枚举常量
- When a TypeScript enum exists for event types or status strings, use it — don't compare against string literals.
- 当存在事件类型或状态字符串的TypeScript枚举时,请使用枚举——不要直接与字符串字面量比较。
B16. Dead Type Checks in Catch Blocks
B16. Catch块中的无效类型检查
- in a DB/repository catch block will never be true — it's dead code.
instanceof AxiosError - Match the expected error type to what the wrapped operation actually throws.
- 在数据库/仓库的catch块中使用永远不会为true——这是无效代码。
instanceof AxiosError - 请将预期错误类型与被包装操作实际抛出的错误类型匹配。
B17. Database Transactions for Multi-Step Writes
B17. 多步骤写入的数据库事务
- Multiple sequential saves (2+) across entities must be wrapped in a TypeORM transaction.
- Without a transaction, partial failure leaves orphaned records.
- 跨实体的多个连续保存操作(2个及以上)必须用TypeORM事务包裹。
- 没有事务的话,部分失败会留下孤立记录。
B18. Race Condition / TOCTOU on State Guards
B18. 状态守卫中的竞态条件/TOCTOU
- Check-then-act is not atomic — two concurrent requests can both pass the check before either updates state.
- Fix: atomic conditional update — .
UPDATE table SET flag = false WHERE id = $1 AND flag = true - Or: wrap in a transaction with .
SELECT FOR UPDATE
- 先检查后操作不是原子性的——两个并发请求可能都通过检查,然后才更新状态。
- 修复方法:原子条件更新——。
UPDATE table SET flag = false WHERE id = $1 AND flag = true - 或者:用包裹在事务中。
SELECT FOR UPDATE
B19. Missing Null Check on findOne()
findOne()B19. findOne()
缺失空值检查
findOne()- returns
findOne()when no record matches — accessing without guard causes silent data corruption.null - Pattern:
if (!record) throw new NotFoundException('record not found');
- 在无匹配记录时返回
findOne()——未加守卫直接访问会导致静默数据损坏。null - 模式:
if (!record) throw new NotFoundException('record not found');
B20. Credentials in Documentation Files
B20. 文档文件中的凭证
- Real API tokens in files or API test transcripts are committed to git history permanently.
.md - Use clearly fake placeholders: ,
YOUR_BEARER_TOKEN_HERE.<api-token> - Applies to staging credentials too.
- 文件或API测试记录中的真实API令牌会被永久提交到git历史中。
.md - 请使用明确的假占位符:、
YOUR_BEARER_TOKEN_HERE。<api-token> - Staging环境的凭证也适用此规则。
B21. Input Normalization Consistency
B21. 输入归一化一致性
- New flows must normalize user-provided strings (email, username) the same way as existing flows.
- Check how the login/existing flow handles the same field — ,
trim().toLowerCase(), SQL@Transform.LOWER(TRIM(...))
- 新流程必须与现有流程以相同方式归一化用户提供的字符串(邮箱、用户名)。
- 检查登录/现有流程如何处理相同字段——、
trim().toLowerCase()、SQL@Transform。LOWER(TRIM(...))
Step 3: Report Findings
步骤3:报告发现
For each issue found:
[B1] path/to/file.ts:3
Severity: High
Issue: reads process.env at module-load time before ConfigModule bootstraps.
Fix: inject ConfigService, read inside method.对于每个发现的问题:
[B1] path/to/file.ts:3
Severity: High
Issue: reads process.env at module-load time before ConfigModule bootstraps.
Fix: inject ConfigService, read inside method.Step 4: Summary
步骤4:总结
- Total issues by severity (High / Medium / Low)
- Files most affected
- Verdict: ready / needs fixes
- 按严重程度分类的问题总数(高/中/低)
- 受影响最严重的文件
- 结论:可合并/需要修复