关于 fallow-review
fallow-review 是用于代码审查的技能,将变更集转为基于依赖图的审查摘要,帮助审查者聚焦需要判断的结构性问题。它通过图快照和信号标识验证 Agent 的审查意见,并与负责 CI 门禁的 audit 命令区分。
主要功能
- 01按注意力评分排列变更单元
- 02从审查判断中排除确定性发现
- 03提取结构性决策问题
- 04生成带图快照的 Agent 审查指南
- 05校验审查意见的信号与快照
适用场景
- 01合并前审查分支或差异
- 02梳理自动编码后仍需判断的问题
- 03生成可回传给代码作者的审查意见
MCP 相关
README 提及 MCP 工具 decision_surface,用于呈现与 CLI 相同的依赖决策。其余 MCP 交互细节未在摘录中说明。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ fallow-rs/ fallow-skills --skill fallow-review安装后在 Claude Code 中输入/fallow-review确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlreferences/production-runtime-data.mdtradeoff-elicitation.md详细介绍
Fallow Review:由图谱支撑的代码审查
fallow review 会将一组变更转换为一份确定性、基于图谱的审查简报,以及一个 Agent 合约闭环。它回答的是“我该看哪里?哪些调用确实需要人来判断?”,而不是“CI 会因此阻止合并吗?”(负责门禁的是 fallow audit)。review 始终以 0 退出,因此无论审查结果如何,都可以安全运行。
它依次完成以下四项工作:
- 排除:报告变更文件中的死代码、复杂度、重复代码和样式问题,并将其排除在判断闭环之外,避免把注意力浪费在已有确定性检查负责的内容上。
- 聚焦:按综合关注度评分(扇入/扇出、风险区域、变更形态)为变更文件中的单元排序,并标注
review-here/not-prioritized;同时提供完整的deprioritized逃生列表,确保没有内容被隐藏。 - 梳理结构:从差异中提取少数重要的结构性决策,将每项决策表述为判断问题;数量限制在适合工作记忆的范围内,并且每项都锚定到 fallow 发出的一个
signal_id。 - 指引:向 Agent 提供基于图谱生成的摘要、审查方向、图谱快照固定标识,以及需要返回的确切判断格式。
适用场景
- 合并前审查 PR、分支或差异(由 AI 生成或人工编写)。
- Agent 完成工作并移除其能处理的 fallow 发现项后,用于呈现剩下需要人或 Agent 判断的内容。
- 生成可进行行内审查的判断,并将其反馈给编写代码的 Agent。
不适用场景
- 为 CI 设置通过/失败门禁:请使用
fallow audit(失败时会以非零状态码退出)。 - 查看整个项目的健康状况、清理或死代码报告:请使用
fallow技能。
人工审查简报速览
# Auto-detect the base (merge-base against the upstream / remote default):
fallow review
# Pin the base, or scope to a precise diff:
fallow review --base origin/main
git diff --find-renames origin/main...HEAD | fallow review --base origin/main --diff-stdin
人工审查简报会显示概况信息、关注地图和决策面(“需要作出的决策”)。--format json 会输出完整的结构化封装(decisions、focus、deltas、impact_closure、partition、graph_facts)。--max-decisions N 可调整上限(限制在较小范围内)。--show-deprioritized 会展开关注地图中折叠的内容。
决策面
每项决策都是一个经过明确表述的问题,并锚定到 fallow 根据图谱确定性推导出的 signal_id(差异键或协同缺口键)。可发布的类别恰有三种:
- coupling-boundary:新增跨区域依赖边。
- public-api-contract:新增导出的公共 API 表面,或变更了被此差异之外的模块使用的契约(用于确认或协调的信号)。
- dependency:变更的
package.json添加了第三方条目,或将已声明条目升级了一个主版本(或一个0.x次版本)。每个清单文件、每种类别各生成一项批处理决策,并根据受影响包在仓库内的导入方数量加权:blast是导入方数量,internal_consumer_count是差异之外的导入方数量。两个计数都是批处理中所有包的并集。dependencies以外的条目会在问题中显示(dev)、(optional)或(peer)标签。digest.deltas会将键列为dependency_added(<manifest>::<name>)和dependency_major_bumped(<manifest>::<name>@<from>-><to>);这两个列表始终存在,也可能为空。次版本和修订版本升级,以及非数字范围(workspace、file、git、标签)均不会作为候选项。dependency决策没有suppress操作:其锚点是package.json,该文件无法附带注释,因此绝不要将// fallow-ignore行粘贴到清单中。decision_surfaceMCP 工具会显示与 CLI 相同的依赖项决策。表述规则:引用变更日志问题(“<from>到<to>之间记录的哪项变更会影响这 N 个导入方?”);不要猜测行为变更。
当决策的锚点文件在变更中被重命名时,它可能带有 previous_signal_id:这是同一决策在旧路径下对应的 signal_id,这样审查界面便可以通过 git mv 将先前的审查者评论重新关联起来。
引出更广泛的权衡(品味归属)
上面的决策范围是确定性部分:仅限 fallow 能从图中证明的权衡(三类)。实际的架构权衡范围更广(抽象级别、错误处理策略、数据模型形态、急切与延迟、状态归属、可扩展性与 YAGNI 的取舍、可测试性、信任边界),这些都无法通过图检测。要呈现这些内容,需要模型阅读差异,而不是静态检查。
对差异和指南运行 references/tradeoff-elicitation.md 中的权衡引出提示词。它遵循品味归属原则:模型让每个选择清晰可见,并提出真正开放的问题;由人来决定。提示词包含诚实约定(以差异为依据、observed / tradeoff / question 的划分、deterministic: false 的标记、来源信息、最多五项并诚实弃答、不重复确定性范围,以及 options 绝不替人做选择)。fallow 负责它能证明的内容,提示词覆盖其余部分,而标记可防止两者混淆。
将其作为审查步骤运行
若要在确定性决策旁呈现权衡范围:
- 获取指南并阅读差异:
fallow review --base <ref> --walkthrough-guide --format json > guide.json git diff <ref>...HEAD # or: git diff --cached for staged work - 对差异和
guide.json运行references/tradeoff-elicitation.md提示词。 - 保留封装结构(
{ graph_snapshot_hash, abstained, tradeoffs[] }形态)。下方的组合审查会用到它。
此步骤刻意不属于下方的验证后 Agent 契约循环。权衡封装绝不会通过 fallow review --walkthrough-file 往返处理(该命令只验证已输出的 signal_id),因此它不具备 fallow 级别的保证:每项内容都保持 deterministic: false,由 Agent 根据差异自行检查,而非通过图验证。不要将其说成已通过 fallow 验证。
Agent-contract 循环
该循环让 Agent 生成判断,再由 fallow 根据实时图谱进行后置验证。验证者是图谱,而不是第二个模型。
-
获取指南:
fallow review --base origin/main --walkthrough-guide --format json > guide.json指南包含:
digest(简述和决策范围)、direction(查找方向)、graph_snapshot_hash(防陈旧校验值)、agent_schema(需要返回的精确结构)以及injection_note。摘要仅根据图谱构建;PR 文本绝不会被纳入,因此该指南在设计上具有注入抵抗能力。 -
阅读
digest中的决策。每项决策都包含signal_id、category、拟定的question,以及anchor_file/anchor_line。 -
返回符合
agent_schema的判断:{ "graph_snapshot_hash": "<echo the value from the guide>", "judgments": [ { "signal_id": "<one signal_id fallow emitted>", "framing": "<your reasoning for the human reviewer>", "action": "<block | address | consider | fyi>", "concern": "<optional: one lens from agent_schema.concern_vocabulary>" } ] }每个
signal_id都必须是指南中由 fallow 发出的 ID(emitted_signal_ids)。未锚定的 ID 会被拒绝。原样回传graph_snapshot_hash。对于没有发现问题的变更区域,判断可以引用指南的
change_anchors中已发出的change_anchor(chg:ID),代替signal_id。每个锚点都包含file、start_line和line_count。未知 ID 会被拒绝,并标记为unknown-change-anchor。action表明该判断要求作者做什么:block和address是必需操作,consider是可选操作,fyi则无需采取行动。concern指明关注维度;优先使用指南的agent_schema.concern_vocabulary中的词汇,即 13 个 kebab-case 关注维度:abstraction、coupling、data-model、error-handling、control-flow、performance、dependencies、api-ergonomics、compatibility、state-ownership、extensibility、testability、trust-boundary。指南通过agent_schema.action_vocabulary和agent_schema.concern_vocabulary发布这两个列表;请从指南读取,而非从本文件读取。action_vocabulary会强制执行;concern_vocabulary仅供参考,接受任意字符串。 -
后置验证:
fallow review --base origin/main --walkthrough-file judgment.json --format json响应会将每项判断分类为:
accepted:signal_id已发出,且快照匹配;Agent 的framing会标记为非确定性内容(deterministic: false),绝不会作为门槛。action会与agent_framing一同回传,并以相同方式标记:它是给作者的指示,而非 fallow 事实。rejected,且reason: "unanchored-signal-id":signal_id从未发出(属于幻觉)。请删除或修正。rejected,且reason: "invalid-action"和invalid_value(fallow 拒绝的标签):action不在词汇表中。只有在锚点解析成功后才会报告此错误;如果锚点是幻觉,且标签也无效,则会改为以unanchored-signal-id/unknown-change-anchor拒绝。先修正锚点,再修正标签。rejected,且reason: "stale-snapshot"和stale: true:自获取指南以来,代码树已发生变化。请重新获取指南并重新作出判断。
生产运行时数据(Fallow Cloud)
当项目向 Fallow Cloud 发送生产覆盖率数据时,请据此检查变更函数。使用范围限定的读取,而不是完整的运行时上下文拉取:
- 编辑之前:
fallow coverage review-packet --repo <owner/repo> --base <ref>(MCP:get_cloud_review_packet)。读取hit_count、covered_by_test,以及有提供时的调用方信息。covered_by_test: null的高热度函数属于高风险编辑。 - 删除之前: 必须确认该时间段内的
period_tracking_state: "never_called",不能只看tracking_state,并读取evidence_window.observed_hours。观测时长较短或低流量界面意味着“未访问”,而非“已失效”。 - 部署之后:
fallow coverage deployment-changes --repo <owner/repo>(MCP:get_cloud_deployment_changes)。当comparable为false时,报告reason,并且不得声称发生了变化。 - 根据
repo_path打开文件,而不是file_path。
这些数据仅作上下文:在 framing 中引用,绝不可作为门槛。字段含义和完整流程:references/production-runtime-data.md。
撰写审查意见
验证不是评审。接受各项判断后,请按此固定顺序呈现给读者,让他们先看到会影响结果的内容,最后看到确定性的剩余项:
- 已接受的决策判断,按
direction.order排列。每项都逐字保留摘要中的question和tradeoff,接着给出你的framing,然后给出体现其权重的数值:internal_consumer_count、out_of_diff路径,或该单元的scoring_budget。 - 权衡项,取自征询步骤,按
consequence排序,最多五项。当信封为abstained: true时,打印一行说明此情况;不要填充此项。 - 扣除项:用一行列出简报中的计数:“已确定性处理:N 个死代码、N 个重复项、N 个复杂度问题、N 个样式问题;不在讨论范围内”。绝不要根据差异重新计算其中任何一项;这些数据以简报为准。
- 降低优先级的项目:用一行列出数量,并注明可通过
--show-deprioritized查看,这样既不会隐藏内容,也不会凑数。 - 没有决策且权衡项信封为已弃答的评审,仍是完整评审:“没有实质性影响;确定性发现:N”。这不是失败,也绝不是编造项目的理由。
三个图事实会影响评审内容。只有 test_reach 注记可以添加项目,且它必须锚定在 change_anchor 上:
- 每个方向单元都带有
test_adjacency:none(没有测试文件导入此单元)、untouched(存在导入它的测试文件,但不在差异中),或changed(导入它的测试文件在差异中)。测试文件以及未保留图时不会提供此字段。对于review-here单元,如果值为none,请在framing中询问作者如何验证。绝不要声称有覆盖;该值只表示测试是否导入此单元,并不表示测试是否执行了这项变更。 digest.test_reach给出每个已更改值导出的覆盖范围。status为measured或no-tests。counts包含所有类别。exports仅列出类别不是direct或framework的行,每行包含file、export_name、line和reach:transitive:某项测试覆盖了该导出,但没有测试直接指定它。mocked-only:测试用模拟对象替换了该文件(mocked_targets),因此实际代码不会运行。none:某项测试覆盖了该文件,但没有测试覆盖该导出。none-file:没有测试覆盖该文件。test_reach请遵循以下规则:- 缺失表示没有事实。较早的 fallow 版本不会输出此字段;当没有值导出发生变化时,此字段也会缺失。此时使用
test_adjacency。 - 对于文件中有
mocked-only、none或none-file行的review-here单元,请引用该行(文件、导出项、类别)。询问作者如何验证。 - 行中没有
signal_id。请将备注锚定在同一file中、范围与导出声明重叠的change_anchor上。如果没有重叠的锚点,则使用该文件中的第一个锚点。如果存在针对该文件的决策,请将备注写入其说明中。 - 当
test_reach与test_adjacency不一致时,使用test_reach,因为它针对每个导出项,且能识别模拟对象。 - 绝不要根据这些事实写“已测试”或“已覆盖”。
direct表示测试中点名了该导出项,不表示测试运行了发生变化的代码行。 reach集合是开放的。遇到未知值时需要留意。- 当
exports_omitted大于 0 时,列表不完整。某个文件没有对应行,并不能证明其reach为direct。请使用counts。 - 对于
status: "no-tests",写一条概述说明,而不是每个导出项各写一条。 - 对于
masking: "capped",图无法识别模拟对象。没有mocked-only行并不能证明任何事情。 - 端到端测试不会导入文件。在有端到端测试运行器的项目中,
none-file行仍可能对应端到端测试。请询问,不要妄下结论。
digest.partition.independent_slices列出单元间依赖图的连通分量,每项都是按排序排列的模块目录列表。仅当存在两个或更多切片时才会提供此字段;缺失表示此变更是一个连通整体。当digest.triage.risk_class为high且该字段存在时,请将其作为概述事实指出:“此变更沿着图证明的边界拆分为 K 个独立切片”。这绝不是要求拆分;是否拆分由作者决定。
两条规则决定行文。优先把握整体:一个结构性决策加十条简短备注,意味着该决策本身就是评审;这些备注可附在其下,也可以不写。用数字,不用形容词:每段 framing 都至少引用指南中的一个数字或路径(消费者数量、差异范围之外的路径、scoring_budget)。“可能会很慢”不算发现;“被 14 个模块导入,其中 9 个在此次差异范围之外”才算。
人在回路中的演练(终端)
上面的 agent-contract 循环承载 AGENT 的框架。同一个循环也承载 HUMAN 的裁定,并提供相同的、经图验证的反幻觉保证,因此终端审阅者可以留下能追溯锚点并往返传递的备注。该契约使用现有的 --walkthrough-guide / --walkthrough-file 往返流程,而非新增接口。
人类负责判断好坏;你只负责传递备注。fallow 验证的是 ANCHOR(信号或变更区域确实存在),而非备注是否正确;每条传递的备注都保持 deterministic: false,且绝不作为门槛。
-
呈现演练内容并查看锚点:
fallow review --base origin/main --walkthrough # the staged human tour fallow review --base origin/main --walkthrough-guide --format json > guide.jsonguide.json包含决策signal_id(框定的结构性问题)、每个变更区域对应的change_anchors集合(每项为{ "change_anchor": "chg:<hex>", "file", "start_line", "line_count" }),以及用于检测过时状态的graph_snapshot_hash固定值。向人类展示演练内容,并针对其选择标记的每一项,收集简短的裁定 / 备注,以及他们希望作者采取的行动。仅提供四种标签(block、address、consider、fyi);未知标签将被拒绝,并返回invalid-action。 -
将每条人类备注作为判断传递(原样回显哈希;引用 fallow 针对已标记决策发出的
signal_id,或引用人类备注所涉及的其他任何变更区域的change_anchor;将人类的行动作为action传递):{ "graph_snapshot_hash": "<echo from guide.json>", "judgments": [ { "signal_id": "<an emitted decision signal>", "framing": "<the human's verdict/note>", "action": "address", "concern": "<optional>" }, { "change_anchor": "<an emitted chg: id>", "framing": "<the human's note on this region>", "action": "consider", "concern": "<optional>" } ] } -
验证采集结果:
fallow review --base origin/main --walkthrough-file judgment.json --format json响应的排序方式与 agent-contract 循环相同,另有两项变化:
accepted包含anchor_kind("signal"或"change"),且 fallow 从未发出的change_anchor会被拒绝,并返回unknown-change-anchor。请重新锚定到真实信号或区域;不要编造锚点。 -
采取行动: 将已接受的人类裁定转交给编码会话。传递每条备注的
action,以便接收方 Agent 进行分类处理:block和address必须处理,consider可选处理,fyi无需更改。备注带有锚点和限制,不会被当作 fallow 级别的事实。
保证:人类无法将备注锚定到 fallow 未发出的信号或区域;如果备注针对的代码树已发生变化,备注会被拒绝,而不会被悄然错配。
此循环会拒绝的合理化说辞
| 合理化说辞 | 实际情况 |
|---|---|
| “我在差异中看到了死代码,马上标出来” | Subtract 已负责此事;再次推导只会制造噪声,也可能出错。请转达简报中的计数。 |
“这个 signal_id 看起来没问题” | 只有 emitted_signal_ids 才存在。其他任何值都是幻觉,fallow 会以 unanchored-signal-id 拒绝。 |
| “目标是五个权衡项” | 五个是上限。abstained: true 且列表为空,也是有效完整的答案。 |
| “问题是开放式的,我就直接提一下修复方案” | 指定修复方案是在开处方。请重新表述为开放式决策,或列出两个及以上确有成本的选项。 |
| “代码树只变了一点,演练指南应该还没问题” | 它已是 stale-snapshot。请重新获取指南并重新作出判断。 |
| “测试通过了,所以改动没问题” | 测试通过是验证事实,并非对决策面的裁定。仍需人类对这些决策作出判断。 |
| “有测试导入这个文件,所以它经过了测试” | test_adjacency 只检查导入关系。mocked-only、none 或 none-file 行表示没有测试通过导入触及真实导出。请询问验证方案。 |
| “生产环境从未调用它,所以删掉吧” | 请检查 period_tracking_state 和 evidence_window.observed_hours。观察窗口较短或路径罕见,意味着“未访问”,而不是“已废弃”。 |
备注
review是audit --brief的别名;--format与简报相互独立。- 全项目分析请参阅
fallow技能;完整标志列表请参阅其references/cli-reference.md。