gstack /review 的 Maintainability 专项审查清单:机器可读的 JSON 审查契约与并行子代理机制
本篇技术指南围绕 gstack 的 /review 工作流中的可维护性专项审查清单(review/specialists/maintainability.md),拆解它作为"常开专项"(always-on specialist)的定义、六类可维护性检测项、一行一 JSON 的机读输出契约,以及它如何被 /review 主流程并行派发、指纹去重、置信度过滤并汇入 Fix-First 修复管线。读完你可以理解 gstack 如何用一份纯 Markdown 清单把一个 LLM 子代理约束成结构化、可去重、可统计的审查器,并能在自己的代码审查流程中复用这套"清单 + JSON 契约 + 指纹去重"的模式。
定位:Review Army 中常开的专项审查器
gstack 的 /review 是一个"落地前 PR 审查"技能(见 review/SKILL.md)。它的 Step 4.5 "Review Army — Specialist Dispatch" 会根据 diff 规模把审查任务拆派给多个并行子代理(specialist),maintainability 是其中的成员之一。从 review/SKILL.md 的派发规则看:
- 常开(Always-on):每次变更行数(
DIFF_LINES)达到 50 行的审查都会派发 Testing 和 Maintainability 两个专项,对应分别读取review/specialists/testing.md与review/specialists/maintainability.md;DIFF_LINES < 50时跳过全部专项并打印 "Small diff (N lines) — specialists skipped." - 条件派发(Conditional):Security(
SCOPE_AUTH=true或后端变更且DIFF_LINES > 100)、Performance(后端或前端变更)、Data Migration(迁移变更)、API Contract(API 变更)、Design(前端变更,使用 review/design-checklist.md)。 - 强制标志:用户提示词中带
--maintainability(或--all-specialists等)时,无视范围与门控强制派发该专项。
派发逻辑的模板来源可以从源码结构看到:scripts/resolvers/review-army.ts 中的 generateSpecialistSelection / generateSpecialistDispatch / generateFindingsMerge / generateRedTeam 四个函数分别生成了 SKILL.md 中 Step 4.5~4.6 的指令文本,并且该 Resolver 在 ctx.host === 'codex' 时直接返回空串——即 Codex 宿主不运行 Review Army。对应的端到端测试见 test/skill-e2e-review-army.test.ts。
每个专项子代理的 prompt 由四部分组成(scripts/resolvers/review-army.ts generateSpecialistDispatch):
- 该专项的完整清单内容(本文件的正文);
- 技术栈上下文,如 "This is a ruby/node/python 项目"(由
Gemfile、package.json、requirements.txt/pyproject.toml、go.mod、Cargo.toml探测); - 该领域的历史 learnings(
gstack-learnings-search --type pitfall --query "{specialist domain}" --limit 5); - 固定指令:子代理自行执行
DIFF_BASE=$(git merge-base origin/<base> HEAD) && git diff "$DIFF_BASE"拿到完整 diff,对 diff 应用清单,每发现一个问题输出一行 JSON;没有发现则输出NO FINDINGS,除此之外不得输出任何前言、总结或评论;若该问题可被测试捕获,则在test_stub字段给出最小测试骨架(使用探测到的测试框架:jest/vitest/rspec/pytest/go-test)。
子代理配置要求 subagent_type: "general-purpose" 且显式传 run_in_background: false(自 Claude Code v2.1.198 起子代理默认后台运行,必须显式置 false 才能保证所有专项在合并前完成);任一专项失败或超时时记录失败并继续,"部分结果优于没有结果"。
输出契约:一行一个 JSON finding
清单开头(review/specialists/maintainability.md 第 3~7 行)规定了硬性输出契约:
- Scope:Always-on(every review)。
- Output:JSON 对象,一个问题占一行,schema 为:
{"severity":"INFORMATIONAL","confidence":N,"path":"file","line":N,"category":"maintainability","summary":"...","fix":"...","fingerprint":"path:line:maintainability","specialist":"maintainability"}
- 可选字段:
line、fix、fingerprint、evidence、test_stub。 - 无发现时:只输出
NO FINDINGS,不输出任何其他内容。
注意 maintainability 清单与 testing 清单(review/specialists/testing.md)的差异:maintainability 的 severity 固定为 INFORMATIONAL(可维护性问题本质上是工程卫生问题,不阻塞落地),而 testing 清单允许 CRITICAL|INFORMATIONAL。这一设计让主流程可以按严重程度排序:CRITICAL 优先呈现。
这个契约直接服务于 Step 4.6 的机器解析:主代理对每个专项输出逐行 JSON.parse,跳过非 JSON 行;解析失败或 NO FINDINGS 的专项被跳过。也就是说,清单文本本身就是给 LLM 的"接口定义",而主代理是消费该接口的运行时。
六大可维护性检测类别
以下六类检测项完整继承自 review/specialists/maintainability.md,每一项都给出了判定口径。
Dead Code & Unused Imports(死代码与未使用导入)
- 变更文件中被赋值但从未被读取的变量;
- 被定义但从未被调用的函数/方法——清单明确要求跨仓库 Grep 验证(不能只看 diff 内是否有调用,因为调用方可能在未变更的文件里);
- 变更后不再被引用的 import/require;
- 被注释掉的代码块:要么删除,要么解释其存在原因。
值得对照的是主清单 review/checklist.md 中的分工:Pass 2 的 "Dead Code & Consistency" 只保留"版本号/CHANGELOG 一致性"这一项,并明确注明 "other items handled by maintainability specialist"。即"死代码检测"的职责从主检查清单迁移到了本专项,避免同一问题被主代理和子代理双重报告。
Magic Numbers & String Coupling(魔法数字与字符串耦合)
- 逻辑中直接出现的裸数值字面量(阈值、上限、重试次数)——应提取为命名常量;
- 错误信息字符串被其他地方的查询过滤器或条件判断当作匹配条件使用(字符串耦合:改一处报错文案会让别处逻辑悄悄失效);
- 硬编码的 URL、端口、主机名——应移入配置;
- 多个文件中重复出现的字面量值。
Stale Comments & Docstrings(过期注释与文档串)
- diff 修改了代码但注释仍在描述旧行为的注释;
- 引用已完成工作的 TODO/FIXME;
- 参数列表与当前函数签名不一致的 docstring;
- 与代码流程已不符的注释内 ASCII 流程图。
DRY Violations(重复代码)
- diff 内多处出现的相似代码块(3 行以上);
- 显然应抽取共享 helper 的复制粘贴模式;
- 跨测试文件重复的配置/初始化逻辑;
- 可以用查找表(map/lookup table)替代的重复条件链。
Conditional Side Effects(条件分支副作用遗漏)
- 按条件分支但某一分支漏掉副作用的代码路径;
- 日志声称某动作已执行、但该动作在某个条件下被跳过的情况;
- 状态迁移中一个分支更新了关联记录、另一个分支没有;
- 只在正常路径触发、遗漏错误/边界路径的事件发射。
这类问题与主清单 Pass 1 的 "Race Conditions & Concurrency" 形成互补:并发问题在 CRITICAL 通道处理,而"分支间副作用不对称"这种结构性遗漏由 maintainability 以 INFORMATIONAL 级别报告。
Module Boundary Violations(模块边界违规)
- 伸手进入其他模块的内部实现(访问约定上"私有"的方法);
- 本应经由 service/model 层完成的直接数据库查询出现在 controller/view 中;
- 本应通过接口通信的组件之间紧耦合。
汇总管线:指纹去重、置信度门控与质量分
所有专项完成后,review/SKILL.md 的 Step 4.6 定义了统一的合并规则(模板见 scripts/resolvers/review-army.ts generateFindingsMerge):
指纹去重。 每条 finding 计算指纹:若 fingerprint 字段存在则直接使用,否则按 {path}:{line}:{category}(有 line 时)或 {path}:{category} 计算——这正是清单中 "fingerprint":"path:line:maintainability" 的用途。同指纹的多条 finding 只保留置信度最高的一条,打上 MULTI-SPECIALIST CONFIRMED (maintainability + testing) 标记,置信度 +1(上限 10)。也就是说,当 maintainability 与 testing 两个专项同时命中同一处问题时(例如一段 DRY 违规同时缺失负路径测试),该发现会被强化而非重复呈现。
置信度门控。 与主检查清单的置信度校准表一致:7 分以上正常展示;5-6 分附注 "Medium confidence — verify this is actually an issue";3-4 分移入附录;1-2 分完全抑制。maintainability 的 finding 绝大多数会落在 INFORMATIONAL + 中等置信度区间,实际展示时受此门控影响较大。
PR Quality Score。 合并后计算 quality_score = max(0, 10 - (critical_count * 2 + informational_count * 0.5)),上限 10。由于 maintainability 全部产出 INFORMATIONAL,每条 finding 对质量分的扣分是 0.5——这为"死代码多不多"提供了一个可跨 PR 比较的量化信号。
自适应门控(adaptive gating)。 这是 maintainability 专项的一个特有机制:每次派发前,主流程运行 bin/gstack-specialist-stats 统计本项目历史命中率。从源码看,该脚本读取 ~/.gstack/projects/<slug>/*-reviews.jsonl(剥离 ---CONFIG--- 尾注),解析每条 review-log 的 specialists 对象,累计每个专项的 dispatched 次数与 findings 总数,然后:
- 对命中集
NEVER_GATE = {'security', 'data-migration'}内的专项打[NEVER_GATE]标签——无论命中率多低都必须派发(清单原文称之为 "insurance policy specialists"); - 对"派发 ≥ 10 次且 0 发现"的专项打
[GATE_CANDIDATE]标签,主流程将其跳过并打印 "[specialist] auto-gated (0 findings in N reviews)."
关键点:maintainability 不在 NEVER_GATE 集合中。从源码结构看,这意味着如果一个项目连续 10 次审查 maintainability 专项都零发现,该专项会被自动门控跳过,直到用户用 --maintainability 强制启用。这是一个基于历史命中率的自学习闭环:Step 5.8 通过 gstack-review-log 持久化每个专项的 {"dispatched":true,"findings":N,"critical":N,"informational":N} 统计,下次审查时又被 gstack-specialist-stats 读回。
下游消费:finding 如何进入 Fix-First 管线
maintainability 的 finding 与 CRITICAL 通道的发现一起进入 Step 5 "Fix-First Review",分类规则定义在 review/checklist.md 的 Fix-First Heuristic 中:
- AUTO-FIX(直接修复,不再询问):死代码/未使用变量、魔法数字提取为命名常量、与代码矛盾的过期注释、"被赋值但从未读取的变量"等——即 maintainability 六类中的前三类(Dead Code、Magic Numbers、Stale Comments)在修复机械且资深工程师会毫不犹豫执行时,会被直接自动修复;
- ASK(批量询问用户):设计决策、较大修复(>20 行)、删除功能、改变用户可见行为等。DRY 重构、模块边界调整这类"合理工程师可能有分歧"的修复会归入 ASK;
- test_stub 覆盖规则:任何携带
test_stub字段的 finding(专项生成)无论原分类一律重分类为 ASK,向用户展示建议的测试文件路径与测试代码,批准后才写入 fix + test 文件。
Step 5.0 还有跨审查去重:通过 gstack-review-read 读取本分支历史 review 记录,凡用户此前选择 skip 且相关文件自那次审查后未变更的 finding(按指纹匹配)会被抑制,且只抑制 skipped 而从不抑制 fixed/auto-fixed(后者可能回归,需要重新检查)。审查结束后,Step 5.8 将 specialists 统计对象(含 maintainability 的 dispatched/findings/critical/informational 计数)与逐条 finding 的 action(auto-fixed/fixed/skipped)写入 review-log,构成下一轮自适应门控的数据源。
适用前提与边界
- 本清单生效的前提是运行 gstack 的
/review技能(或其/ship变体,ship/sections/review-army.md 注入了相同机制),且 diff 达到 50 行门槛;小 diff 场景下 maintainability 不会运行,但主检查清单 Pass 2 的少量工程卫生项(版本/CHANGELOG 一致性)仍会执行; - 清单本身是纯 Markdown 提示词,不含可执行逻辑;所有"跨仓库 Grep""执行 git diff""JSON 解析"等动作由宿主代理(Claude Code 的 Agent 工具 + Bash)在运行时完成,
test_stub的框架适配依赖宿主机器上探测到的测试框架; - 清单固定输出
severity: INFORMATIONAL,因此它永远不会单独触发 Red Team 派发(Red Team 仅在DIFF_LINES > 200或任一专项产出 CRITICAL 发现时激活),其发现主要经由 Fix-First 与 PR Quality Score 发挥作用; - 自适应门控基于本地
~/.gstack/projects/<slug>/*-reviews.jsonl历史,仅在积累了足够审查次数(≥10 次派发)后才可能对 maintainability 生效。
从 test/skill-e2e-review-army.test.ts 与 test/run-in-background-guidance.test.ts 等测试的存在可以看出,专项派发、run_in_background: false 指令、门控文案等行为都有回归测试保护。整体上,review/specialists/maintainability.md 展示了 gstack 的一种工程化思路:用一份短小的 Markdown 清单同时充当人类可读的检测规格和 LLM 子代理的运行时接口,再配合指纹去重、置信度门控与命中率门控三层过滤,把"可维护性审查"从主观意见变成可统计、可抑制、可自学习的结构化流程。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00