代码改动第一性审查
目标不是替改动者证明方案正确,也不是为了显得严格而强行找问题。先把自己放在“需求交给我实现”的位置,独立形成一份开发方案,再让这份方案和改动者方案互相补漏、互相证伪。无论审查同事还是自己的代码,都要在有限时间内回答七件事:这块功能负责什么、实际出了什么问题、根因是什么、如果由我开发会怎样解决、改动者怎样解决、两套方案谁遗漏了什么、最终代码是否合理。
默认把读者视为第一次接触该项目的新手。报告主体只保留会影响理解、提交、合并决定或后续验证方式的信息;不要用一串术语和方法名代替解释。具体 Case 走读不设机械字数上限,以读者能沿真实输入、判断、状态变化和下游结果完整听懂为准,但不要重复粘贴同一份图、表和代码。
输入与边界
最低输入是一个可定位的审查对象,例如:
- CodeUp PR 链接;
- 当前仓库的 staged、unstaged 和 untracked 改动;
- 指定 worktree 路径或本地分支;
- 明确的 commit、patch 或比较范围。
需求背景通常应提供,但若当前对话、关联工作项、提交说明或代码能可靠恢复,就先自行整理;无法恢复时仍可做实现质量审查,但要明确“需求符合性未验证”。可选输入包括 OA 需求/缺陷 ID、project ID、Chat ID、环境、复现步骤、日志、改动者判断的根因和修复方案。
缺少可选输入时继续做能完成的静态审查,不把可自行检查的问题反问用户。只有审查对象无法访问、仓库或 worktree 无法定位、比较基线存在多个合理选择,或某个缺失选择会实质改变审查范围时才请求补充。
本 Skill 默认只读:
- 不修改代码、配置、数据库或文档;
- 不在 CodeUp 上评论、通过、关闭或合并 PR;
- 不 commit、push 或部署;
- 数据库只允许 SELECT;日志和配置只读取与当前假设有关的最小范围;
- 不在报告中回显 Token、Cookie、凭据、完整 Prompt 或未脱敏业务数据。
事实获取顺序
按成本从低到高取证,证据足够支撑结论就停止,不为低风险改动制造不成比例的调查。
1. 固定审查对象
先识别审查模式并在报告顶部写明“审查对象、基线、终点和包含范围”:
- CodeUp PR:解析仓库、源分支、目标分支、当前 patch set 和 commit。优先通过已登录页面、只读 OpenAPI 或对应 Git 远端锁定不可变 diff;先只记录范围和需求入口,把 patch hunk、改动者方案、讨论结论和测试结论留到独立方案冻结后再读。
- 当前未提交改动:默认审查
HEAD → 当前工作区。分别读取git diff --cached、git diff和git ls-files --others --exclude-standard;相关 untracked 文件必须读取,不能因普通git diff看不到就漏审。生成物、二进制或明确无关文件可以排除,但要在范围中说明。 - worktree 或本地分支:先用
git worktree list、git status、当前分支、HEAD 和远端信息确认实际路径。默认以用户指定目标分支为基线;未指定时只在仓库默认分支明确时使用其最新远端引用,并以 merge-base 为起点,审查merge-base → HEAD,再叠加该 worktree 的 staged、unstaged 和相关 untracked 改动。基线不唯一时不要擅自选择。 - commit 或 patch:按用户明确给出的不可变范围审查,并记录两端 commit 或 patch 来源。
若 PR 与本地改动同时存在,默认把 PR 作为主审查对象;检查本地分支是否还有未进入 PR 的提交或工作区改动,并单独标为“PR 外本地改动”,不要混入 PR 结论。用户明确要求审查发布前全部内容时,再合并两部分范围。
涉及远端基线或 PR 时,先检查 git status、分支和 worktree,再 git fetch,使用最新远端引用判断;只审查当前工作区相对 HEAD 的未提交内容时不必为了形式强制 fetch。全程不得切换分支、改 index、stash、commit 或清理文件。
记录审查开始时的 HEAD、状态和 diff 范围;交付前重新检查。若用户或其他会话在审查中改变了 HEAD、index 或工作区,重新读取受影响 diff,或者明确报告审查基于哪个快照。无法取得真实 diff 时,不根据标题或描述假装完成代码审查。锁定 diff 不等于提前研究改法;同事 Review 和自审都应尽量避免用目标版本的实现反推“自己本来会怎样设计”。
2. 还原需求而不是复述需求
用大白话分别写清:
- 需求背景:哪块功能本来负责什么;
- 问题描述:在什么条件下,哪一步出现了什么偏离,造成什么实际影响;
- 改动者根因假设:对同事改动,使用同事明确写出的判断;对自审,使用用户说明的判断。未明示时可从实际改动位置和方案反推,但必须注明“从代码改动反推,原文未明示”。
背景不要提前塞入根因;问题描述不要只写“逻辑有问题”“体验不好”或函数名。
3. 建立新手可读的最小词汇
只解释理解本次问题、根因和改动所必需的术语。术语第一次出现时,先给业务白话,再保留原词,例如:“幂等(同一条消息重复处理,也只能产生一次业务结果)”。缩写第一次出现时同时写出全称或实际用途。
对仍可能混淆的 3~6 个关键术语,使用“关键概念”小表格说明:
| 术语 | 白话解释 | 在本次问题里代表什么 |
|---|---|---|
<term> |
<不用另一个陌生术语循环解释> | <它在哪一步影响结果> |
不要罗列与结论无关的框架名、设计模式或基础语法。不要照抄注释和文档定义;先通过真实代码、配置或官方契约确认含义。项目自定义词语无法确认时注明“推断,未验证”。
4. 建立最小完整机制
从业务目标出发,追踪与改动有关的真实链路:
输入/触发
→ 生产者
→ 状态或数据变更
→ 消费者
→ 用户可见结果
→ 错误恢复、异步回填或终态
只检查有真实调用关系的集成点,重点包括:
- 入口、调用方和下游消费者;
- 状态生命周期、缓存、并发、幂等和恢复路径;
- API/schema、数据库脚本、配置、提示词、前后端和跨仓依赖;
- 权限、环境差异、兼容路径、监控、测试和上线配套;
- 项目内同类实现、现行规范和历史修复先例。
如果当前目录属于 GoalfyAI,按需读取最新 goalfy-coding 规范、知识库导航和 goalfy-insight 的当前根因/证据合同;这些资料用于形成和校验假设,不能代替本次审查对象的代码与运行证据。
共享配置 × 构造参数门禁
当一个配置对象或字段被多个构造器、工厂或 wrapper 共用,而调用方还传入 bucket、tenant、region、namespace、资源 ID 等会改变语义的参数时,不能用默认构造器的一条测试推断全部消费者正确。先列出所有生产调用方,记录“共享配置值、构造参数、最终消费者”三者的组合,区分配置归属与当前 client/资源归属;至少覆盖默认消费者、一个非默认消费者和 fallback。若实现会根据当前参数改写共享配置,必须用不同参数的同一配置构造反例,确认不会把默认资源信息带入另一个消费者。
这类检查的完成标准不是“所有构造器都能编译”,而是每个有不同语义参数的可达构造路径都产生正确的最终地址、标识、权限范围或状态。生产调用方中存在未验证的非默认组合时,不能无条件通过;已有真实配置可复现错误时形成正式 finding。
参考优先门禁:先证明为何复用或偏离
审查改动前,必须按风险找出项目中最接近的现有代码、测试和必要历史,理解项目为什么这样设计、曾规避过什么问题。简单局部改动通常对照同模块最邻近的一处实现即可;涉及协议、Schema、序列化、跨服务接口或兼容行为时,再扩大到真实消费者、相同注册点、相关测试和历史修复。
优先选择与本次改动共享真实消费者、协议链路或业务约束的样本,不要只因函数名或字段名相似就类比。现有代码是高价值参考,不是必须照搬的规则:它也可能过时、只覆盖另一场景或本身存在缺陷。若新写法与项目惯例不同,检查差异是否来自明确的新需求、版本变化或消费者差异,并要求用针对性测试证明;不能只以“更标准”“更通用”或外部文档允许为理由。
找不到可靠样本时,记录实际搜索过的模块和关键词,再依据真实消费者、官方当前契约或最小运行探针判断;不要为了凑参考随便找一段不相关代码,也不要仅因项目没有先例就阻塞低风险改动。报告只需在“验证情况”中简要写明真正影响结论的参考样本及其启示,避免展开成代码考古记录。
通过结论前必须形成明确的参考判定:最接近的实现是什么、哪些约束相同、当前改动直接复用了什么、偏离之处为何必要。若现有机制已经能满足同一验收和失败语义,而 diff 另建了一套状态、入口或恢复逻辑,应作为正式 finding;找不到参考时则写清搜索范围,不以“项目里应该没有”代替查询。
等价正常路径与改动归属:先证明“需要改”
当需求新增入口、数据来源、协议或触发方式,不要把“新入口”直接推导为“下游需要新能力”。先找到一条能产生相同业务结果的已知正常路径,并比较两条路径的标准契约和真实消费者:
| 需求变化 | 已有正常路径 | 首次差异点 | 最早可汇合点 | 必须修改的层 | 其他层为何不改 |
|---|---|---|---|---|---|
<新入口/新来源> |
<已运行的等价入口> |
<第一个不同的字段/状态/分支> |
<可转成已有契约的位置> |
<有证据的最小改动层> |
<已复用什么> |
如果新路径能在上游转换成消费者已支持的同一契约,下游默认应为零修改,只做必要的联调和回归。只有证明已有契约无法承载新语义,才允许让消费者增加新分支。在“需要前端/后端/Agent/Core 修改”之类责任表中,每一层都必须指出当前哪个输入、状态或消费动作不满足需求;无证据的层不得列为改动方。
出现以下信号时,将“可能在制造第二套逻辑”作为高优先级反证:
- 消费者开始判断数据来自哪个页面、卡片或分享入口;
- 新字段与已有字段表达同一业务对象,只是生产来源不同;
- 同一资产、状态或动作出现两套加载、授权、恢复或幂等逻辑;
- 新增一个入口却要求多个无差异的下游一起修改。
此时优先检查生产者或适配层是否没有完成归一化,而不是立即给消费者添加专用处理。若已有正常路径完整覆盖新需求,应主动告知用户“该层无需修改”,不要为了形式上的分工给每层都分配任务。
代码中的提交标题、测试文件名或注释里出现工单编号,只能证明代码使用了该标签;在 OA 或其他外部系统验证前,不得宣称对应工单真实存在或把代码标签当作需求事实。
5. 独立判断根因
不要从“改动者改了这里”倒推“这里一定是根因”。依次确认:
- 现象:用户或系统最终看到了什么;
- 触发条件:本次什么事件启动了异常链路;
- 首次有意义的偏离:最早哪个值、状态、分支或结果偏离预期;
- 直接原因:什么机制直接造成问题;
- 系统根因:现有约束、门禁、恢复或消费链为什么没有阻止直接原因;
- 反事实:若移除候选根因,是否应观察不到当前问题。
至少检查一个竞争解释,例如配置/版本不一致、调用方错误、外部依赖、权限、数据、模型决策或证据不足。结论分为:
- 一致:改动者判断和独立核验指向同一首次偏离与机制;
- 部分一致:改动者找对了直接原因,但漏了更早根因或影响范围;
- 不一致:实际修改点不能解释现象,或证据支持另一原因;
- 无法判断:缺少会改变因果判断的决定性事实。
最新 main 只说明当前设计,不能证明某环境已经部署;代码默认值也不能冒充 Nacos、数据库、Hub 或环境变量的当前值。结论依赖运行时事实而输入未提供环境时,明确写“运行时未验证”。
6. 冻结独立开发方案,再与改动者方案对照
完成需求、基线机制、项目参考和独立根因判断后,先假设这个需求现在交给自己开发,形成一份独立开发方案。这一阶段不读取 patch hunk,也不采用改动者的根因、文件选择和测试结论;如果 PR 页面把问题与方案写在一起,只抽取可由任务事实核验的问题描述,把方案部分留到下一阶段。若没有独立任务材料,需求只能从 patch 恢复,明确标注“独立方案受 patch 信息影响”,不能把它包装成真正的 diff-blind 方案。
独立方案不是一份脱离项目的架构畅想,而是复用日常开发流程得到的最小交付蓝图:
对于现有代码任务,读取 $project-aware-coding,仅复用步骤 1~5 的目标确认、调用链、项目参考、方案设计和验证标准;按实际触发条件组合其他领域 Skill。已有调查材料可复用,但关键主张须独立核验。本轮 Review 自身承担审查职责,不调用该 Skill 的步骤 6,不执行修改、自动修复或外部写操作。
- 调查与 Skill 路由:需要哪些诊断、项目开发、协议、安全或领域 Skill;要查哪些代码、配置、日志、数据库、历史和运行证据。只写会改变方案的选择,不罗列形式化工具清单。
- 机制与改动归属:首次偏离在哪里,最小应修改哪一层、哪些生产者和消费者,哪些层能复用既有契约所以不改。
- 实现方案:需要调整的入口、状态、协议、错误处理和终态;覆盖正常、失败、恢复、重复、并发及权限边界中真实可达的部分。
- 验证方案:哪些单元、集成、契约和真实 QA 证据才能证明旧问题被抓住、正常路径未回归、副作用次数正确;配置、工具可见性、部署或环境差异必须指定运行时事实源。
- 自审闭环:假设实现完成后,应基于完整 diff 独立自审,修复原需求范围内有证据的必须项,重新验证并全量复审;这里只用它检查改动者方案是否具备同等交付完整性,Review 本身仍不修改代码。
把方案压缩成能用于比较的决策基线,不提前写成正式 finding。随后读取改动者说明、完整 diff、测试、讨论和运行证据,按下表逐项比较:
| 对照维度 | 独立开发方案 | 改动者方案 | 复核动作 |
|---|---|---|---|
| 根因与首次偏离 | 自己从基线推出的机制 | 作者声明或 diff 反映的机制 | 用复现、调用链和反事实判断谁成立 |
| 改动层与责任归属 | 最小应修改的生产者/消费者 | 实际修改的服务、配置和文件 | 检查能否更早汇合或复用已有入口 |
| 正常/失败/恢复 | 应保持的控制流和终态 | 当前实现与测试覆盖 | 寻找双方各自遗漏的可达路径 |
| 契约与副作用 | 字段、状态、次数、权限和兼容约束 | 实际 schema、调用和写入 | 追到真实消费者并核对完整输出 |
| 验证与自审 | 应有的测试、QA 和复审证据 | 作者现有证据 | 判断证据是否真的覆盖风险 |
比较结果允许五种状态:一致且完整、方向一致但改动者遗漏、改动者方案更完整、两套方案实质不同、双方共同遗漏。独立方案不是标准答案:改动者用更好的项目参考或更强运行证据补上独立方案遗漏时,更新自己的判断,不把差异包装成问题;两套方案不同则分别构造能推翻它们的最短反例。
运行证据反转门禁
当差异涉及部署版本、动态配置、工具可见性、数据库状态、权限、真实调用次数或外部依赖时,局部静态代码通常不足以决定结论。若用户给了环境、project ID、记录 ID 或日志线索,先查询对应运行事实,再形成 finding。
真实请求参数、实际消费者调用和成功/失败结果能直接推翻“工具不可见”“路径不可达”“配置未生效”等静态推断时,应撤回候选问题并说明独立方案漏看了什么。反过来,单个成功样本只能证明它覆盖的入口和状态;若要保留条件性风险,必须再找到另一个真实可达且未被该证据覆盖的上下文,不能用“理论上可能”维持阻塞结论。
7. 绘制代码链路与改动对比
先还原真实调用链和数据流,再绘图;不要从文件 diff 拼出一张看似完整但没有调用证据的图。图用于帮助零背景读者理解改动,不代替根因判断和代码证据。
优先使用 Mermaid flowchart LR 展示每条代码链路。采用两层结构:
- 主链路:把“改动前”和“改动后”拆成两张独立图,改动前在上、改动后在下,形成纵向对比;每张图内部仍按
LR从左到右展示链路。两图使用一致的业务阶段、节点编号和粒度,便于上下逐项比较。每条主链保留 5~8 个节点,只表达入口、关键生产者、状态变化、决定性判断、消费者和最终结果。 - 关键判断展开:主链中的判断较复杂时,另画一张局部图,只展开本次修改的判断、造成问题的判断,以及影响成功、失败或恢复路径的判断。未修改且与结论无关的分支合并为“既有校验(未修改)”或“其他正常路径保持不变”。
主图使用以下骨架。必须输出两个独立 Mermaid 代码块,依靠文档顺序实现稳定的上下对比;不要把两个版本合并进同一个 subgraph 或泳道图。根据真实代码替换节点,不要照抄示意内容。
改动前
flowchart LR
B1["① 入口"] --> B2["② 状态生成"] --> B3{"③ 关键判断"} --> B4["④ 下游结果"]
改动后
flowchart LR
A1["① 入口"] --> A2["② 状态生成"] --> A3{"③ 关键判断"} --> A4["④ 下游结果"]
每张图最多放 3 个判断节点;超过时拆成主链和一个或多个局部判断图,不画完整控制流图。跨文件、模块或仓库时使用 subgraph 标出边界,但仍保持一条从左到右的业务主线。节点标签必须先写业务作用,再换行补真实方法或状态名,例如 确认支付结果<br/>confirmPayment();不能只写陌生代码标识符。
图中用编号 ①②③ 关联后面的代码证据,并保持统一语义:
- 灰色:未修改链路;
- 红色或虚线:原问题路径、被删除路径;
- 绿色:新增或修复路径;
- 黄色菱形:关键判断;
- 虚线边框:根据改动推断但未由调用链或运行证据确认的节点,并明确标“推断,未验证”。
每个关键节点都要能追溯到锁定审查对象中的真实代码。对影响问题链路的 3~6 个关键方法,不能只列方法名;必须根据方法体、调用方和被调用方说明:
- 谁在什么条件下调用它;
- 输入的业务含义,不必机械抄 Java/TypeScript 类型;
- 它做的核心事情;
- 返回值、状态写入、消息发送等输出或副作用;
- 它为什么与本次问题或改动有关。
若方法名与实际行为不一致,以真实实现为准。仅从名字或注释推断用途时标注“推断,未验证”。代码证据注明仓库、文件、函数和行号;改动前引用基线 commit,改动后引用 PR 源 commit、当前 HEAD 或明确标记的工作区快照。片段每段通常 5~12 行、最多 3 段,只截取决定行为的部分。不得把伪代码冒充真实代码;确需简化时标注“伪代码示意”。
若改动只有一个简单条件或单点字段调整,图不会增加理解价值,可以省略主图,直接用链路对比表和代码证据;不要为满足格式强行画图。
8. 用一个具体 Case 把链路走通
每次 Review 都选一个最能解释本次问题的具体 Case,直接带读者从入口走到最终结果。审查对象和基线足以继续后,不要为了教学先让用户回答问题,也不要只丢一句“例如某请求会失败”;首次交付应先把这一条完整链路讲清楚。Case 的优先级是:真实复现输入、脱敏日志或运行记录 → 已有测试样例/fixture → 能由代码契约推出的代表性输入 → 明确标注为“假设场景”的示例。不得把构造示例包装成线上真实数据,也不得为了讲故事补造代码中没有的状态、调用或结果。
默认使用同一份输入分别推演改动前和改动后,让读者看到两条链路在哪里第一次分叉、状态怎样变化、修复为何能影响最终结果。即使链路较长,也要保留理解所需的完整因果闭环;只省略与本次结果无关且行为完全相同的内部细节。简单单点改动同样给出一个最小 Case,用一两步讲清条件成立与不成立时的差异,不因“改动小”跳过。
开始前先用白话固定:谁触发、带了什么关键输入、初始状态是什么、预期结果是什么。随后按真实执行顺序逐步说明:
| 步骤 | 进入本步的输入/状态 | 代码位置与方法 | 这一处是做什么的 | 改动前怎样走 | 改动后怎样走 | 交给下一步什么 / 最终结果 |
|---|---|---|---|---|---|---|
| ① | <关键字段或状态> |
<repo/path:line> · <method()> |
<先用业务白话解释职责> |
<判断、状态变化或副作用> |
<判断、状态变化或副作用> |
<下游输入或用户可见结果> |
表中每一步都必须来自锁定版本的真实方法体和调用关系,并与前面的链路图使用相同编号;首次出现的方法要说明“它为什么存在”,不能只翻译方法名。复杂条件写成“满足什么 → 走哪条分支 → 状态从什么变成什么”,必要时在表后附决定分支的短代码片段并注明文件、方法、行号和版本。运行时值不可得时写“基于代码静态推演,运行时未验证”。
走读最后用两句话收束:第一句说明改动前为什么会得到错误结果,第二句说明改动后从哪一步截断问题。若 Case 暴露修复只能覆盖部分路径,应把遗漏升级为正式 finding,而不是在讲解中轻描淡写。
用户看完仍表示“似懂非懂”“这里为什么”或要向别人复述时,再吸收苏格拉底式单问题追问:先定位最上游的理解断点,每轮只问一个能区分理解是否正确的问题,并等待回答。用于补齐审查事实或选择 Case 的澄清问题,能从代码自查就不要问用户;用于检验理解的教学问题,可以询问代码中已有答案的机制,但要写成围绕当前 Case 的预测或推演。回答后只补足当前断点,再让用户沿这个 Case 继续推演,不一次抛出整套问卷。需要系统性掌握和迁移检验时,可继续使用 $explain-to-master。
9. 审查改动与遗漏
把当前审查范围内的改动视为一个待证伪方案:
- 改动是否截断了已确认的因果链,而不是只隐藏症状;
- 正常路径、失败路径和恢复路径是否都保持正确;
- 设计写到的检查是否全部实现;
- 是否混入无关重构、未来兼容或另一个需求;
- 新增状态、字段、API 或表是否有完整生产者和消费者;
- 测试是否覆盖复现条件、正常路径和关键回归;
- CI 通过是否真的覆盖该风险,还是只有形式上的绿灯。
先寻找能推翻每条候选 finding 的反证。只有满足以下条件才写进正式问题:
- 能指向具体文件、行号、配置、日志或运行事实;
- 有可达路径或明确消费者;
- 能说明触发条件和实际影响;
- 修正它会改变提交、合并决定或验证方式。
不要规定必须找到问题。默认最多展开 3 个正式问题,按“阻塞 / 建议”分级;其余不影响正确性的观察可省略。
专项门禁:按真实风险加载
根据本次完整 diff、需求和调用链判断以下条件;命中多项时分别读取,不把未命中项作为默认阅读材料。条件无法确认时先查入口与消费者,再决定加载;不可因尚未加载而跳过真实风险。各参考文件继承本 Skill 的只读边界。
| 触发条件 | 必读检查 |
|---|---|
| 已产生持久副作用后仍可能返回失败 | 部分成功与同资源恢复 |
| 新增或收紧校验、解析、Schema、序列化、路由或兼容层 | 跨层契约等价性 |
| 修改 MCP、OpenAPI、RPC 等对外 Schema | 广播必传与运行时校验 |
| 返回或上报 actual/effective/final/runtime 等事实字段 | 实际值来源 |
| 多入口、角色、协议分支或可共存状态 | 验收覆盖矩阵 |
| 重试、重连、取消、超时、流式响应或错误正文提取 | 重试与取消 |
复审基于最新完整 diff 重新判断全部触发条件,不能只加载上一轮 finding 对应的检查。
过度设计门禁:先尝试删掉或复用,再逐 hunk 证明范围合理
正确性审查完成后,必须再做一次简化攻击:逐项尝试删除或内联新增抽象,合并重复状态和分支,改用项目现有入口或已经在用的依赖,并重点检查新 manager、helper、后台任务、缓存、兼容层、重试和配置项。若更小方案能保持相同验收、正常/失败/恢复语义及测试保护,多出的设计就是 finding;新增共享状态、并发控制、契约或长期维护面时通常应阻塞,少量纯噪音可列为建议。这里的“更小”不是比较代码行数,必要的权限、并发、资源所有权、错误恢复和可观测性不能为简化而删掉。
正确性通过不代表 diff 可以无限扩张。逐个 diff hunk(同一处连续修改)回答“它直接满足哪条需求、修复哪个已证实问题,或为哪项必要测试提供支撑”;无法给出具体对应关系的改动默认视为范围外,而不是用“顺手优化”“更干净”“以后可能有用”证明合理。
- 禁止无意义格式噪音:多余空行、无关缩进/换行、引号或 import 排序、全文件格式化、与目标无关的命名调整都会增加冲突并掩盖真实逻辑。只有项目强制格式化工具对本次触碰区域的必要输出才能保留,并说明证据。
- 保护仍正确的注释和文档:删除或改写注释前,必须证明它已错误、重复、误导,或因对应代码被删除而失效。注释仍准确且对约束、历史原因、调用方或异常策略有解释价值时应保留;“代码已经能看懂”不是删除理由。
- 拒绝顺手重构和未来设计:无关 helper 抽取、目录搬迁、接口泛化、兼容层、低概率兜底、性能优化和依赖升级,若不是当前需求的必要条件,应从本 PR 移除或拆分并另行授权。不要因已经写完或测试通过就降低范围标准。
- 审查增删两侧:不仅检查新增代码是否有消费者,也要检查删除的函数、注释、测试、错误处理和兼容行为是否确实无用。删除仍正确内容属于真实回归风险,不是代码精简。
将范围外改动作为正式 finding:纯噪音或少量无关清理通常列为“建议”;会删除正确契约、扩大行为边界、增加维护面或显著提高冲突风险时列为“阻塞”。通过结论前应能把所有保留 hunk 映射到需求、根因、必要测试或强制工具输出。
10. 形成可执行的建议改动
在报告最末尾输出“建议改动”,用编号逐条写完整文案,默认最多 3 条。每条通常写 2~4 句话,并包含:
- 改哪里:指出文件、方法、配置或测试场景;
- 为什么改:说明当前行为、触发条件和实际风险;
- 怎么改:给出符合项目现有结构的最小调整方向,必要时附少量伪代码;
- 改完怎样算正确:写清预期行为和最短验证方式。
建议与正式 finding 一一对应时标明“对应问题 1/2/3”,不要重复大段问题描述。不要只写“补测试”“加判空”“优化逻辑”或“注意幂等”这类过度精简的短句,也不要在只读 Review 中直接修改代码。
若没有必须修改的问题,仍保留本节并明确写“本次未发现必须修改项”,不要为了填满模板制造建议;可以补充一条有证据且明显降低风险的验证建议。若证据不足,优先建议获取哪项事实或执行哪项测试,不要在根因未确认时建议猜测性改代码。
结论口径
- 合理:未发现阻塞问题,根因和改动链路有足够证据;
- 基本合理,需补充:主体正确,但有非阻塞遗漏、测试或说明需要补齐;
- 需要修正:存在可复现或可达的正确性问题,提交或合并前应修复;
- 证据不足:真实 diff 不可得、比较基线不明确,或缺少会实质改变根因判断的运行时事实。
“证据不足”不是找不到问题时的兜底。静态证据足以判断的内容仍要给出明确结论,只把依赖缺失事实的部分标记未验证。
输出格式
使用下面结构,但让内容自然、紧凑。没有正式问题时删除“发现的问题”;没有必要代码片段时不贴代码;“建议改动”始终作为最后一节保留。
# Review 结论
结论:合理 / 基本合理,需补充 / 需要修正 / 证据不足
仓库:<repo>
审查对象:<CodeUp PR / 当前工作区 / worktree / 分支 / commit / patch>
范围:<base commit → head commit;staged / unstaged / untracked;必要时注明排除项>
PR:<url;没有则删除本行>
## 需求背景
<两三句话,让不了解需求的人知道是哪块功能>
## 问题描述
<触发条件 → 哪一步出错 → 实际影响>
## 关键概念
<按步骤 3 解释必要术语;无则省略。>
## 根因判断
<按步骤 5 给出独立结论、与改动者判断的关系及证据缺口。>
## 独立方案与改动者方案对照
<按步骤 6 比较最小方案、遗漏和验证;简单一致时压缩成一段。>
## 代码链路与改动对比
### 主链路
#### 改动前
<独立的 `flowchart LR` Mermaid 图;简单单点改动可省略,并说明原因>
#### 改动后
<独立的 `flowchart LR` Mermaid 图;与改动前使用相同阶段、编号和粒度>
### 关键判断展开
<仅在主链包含复杂判断时绘制局部横向图;否则删除本节>
| 链路 | 改动前 | 改动后 | 判断 |
|---|---|---|---|
| <关键节点> | <原逻辑> | <新逻辑> | <是否截断根因及副作用> |
### 关键方法与代码证据
| 节点 / 方法及来源 | 谁调用、何时调用 | 输入 | 白话作用 | 输出或副作用 | 为什么与问题有关 |
|---|---|---|---|---|---|
| <① `method()` · `repo/path:line` · 版本> | <上游与触发条件> | <业务含义> | <这个方法实际做什么> | <返回、写状态、发消息等> | <它决定了哪一步结果> |
<按需附最多 3 段关键代码;每段注明来源和版本>
## 具体 Case 走读
Case:<一个真实复现、测试样例、代表性输入或明确标注的假设场景>
来源:<日志 / 测试 / PR 描述 / 基于代码静态构造;注明是否经过运行验证>
初始状态与预期:<谁触发、关键输入、初始状态、正常应得到什么>
| 步骤 | 进入本步的输入/状态 | 代码位置与方法 | 这一处是做什么的 | 改动前怎样走 | 改动后怎样走 | 交给下一步什么 / 最终结果 |
|---|---|---|---|---|---|---|
| ① | <关键值/状态> | `<repo/path:line>` · `<method()>` | <业务白话> | <原判断或状态变化> | <新判断或状态变化> | <下游输入或最终结果> |
<两句话收束:改动前为什么出错;改动后从哪一步截断问题。>
## 发现的问题
1. [阻塞/建议] `<file:line>`:<问题、触发条件和影响>。建议:<最小修正或验证方式>。
## 验证情况
已验证:<diff、调用链、规范、测试、日志或配置;按需注明参考的同类实现及其启示>
参考与简化:<最接近的项目实现、复用或偏离结论;尝试过的更小方案及保留当前设计的必要理由>
未验证:<只列可能改变结论的缺口;没有则写“无关键缺口”>
## 建议改动
1. [必须/建议,对应问题 N] 建议调整 `<file:line / method()>`。<当前行为在什么条件下会造成什么影响>。<推荐采用的最小改法及其与现有实现的关系>。完成后应通过 <测试或可观察结果> 确认问题已解决且正常路径未回归。
<没有必须修改项时写:本次未发现必须修改项。现有实现已覆盖……;如需进一步降低风险,建议……,验证标准是……。>
先给结论,不复述调查过程,不罗列完整检查清单。专业词第一次出现时立即用白话解释;finding 也要说明业务影响,不能只写技术名词。图、表和代码片段只服务于理解根因与改动;除“具体 Case 走读”需要贯穿同一输入外,如果其他模块表达的是同一件事,保留最清楚的一种,不重复堆砌。
完成检查
交付前按步骤 1~10 核对本轮适用要求与门禁是否完成,不另起一轮相同调查。结论必须对应锁定的完整 diff 和证据强度,关键未知不能包装成通过;报告应让零背景读者借助具体 Case 理解改动,并据此决定通过、退回或补证。