mcpField
S
skill-code-review

skill-code-review

github.com/ctxr-dev测试与质量147 次安装20 周前更新
代码仓库

关于 skill-code-review

该 Skill 通过包含 15 个状态的 FSM 编排基于 git diff 的代码审查,调度专业审查器并给出 GO、CONDITIONAL 或 NO-GO 结论。它面向需要确定性去重、发布就绪门槛和持久化报告的代码审查流程。

主要功能

  1. 01通过 15 状态 FSM 编排审查
  2. 02分批调度专业审查器
  3. 03确定性合并重复发现
  4. 04综合八项发布就绪门槛
  5. 05生成 Markdown、JSON 与清单报告
  6. 06跟踪各状态的验证事件

适用场景

  1. 01审查不同 git 版本间的变更
  2. 02协调并行的专业审查任务
  3. 03依据明确门槛评估发布就绪度
  4. 04通过保存的报告和事件审计审查运行

MCP 相关

该 Skill 会引导注册 MCP 服务与客户端,并通过 `fsm.*` MCP 工具族驱动审查。它使用 FSM 工具启动运行、获取简报并提交工作者输出。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ctxr-dev/skill-code-review --skill skill-code-review

安装后在 Claude Code 中输入/skill-code-review确认已加载。

详细介绍

skill-code-review

一个包含 15 个状态的 FSM,可对 git diff 执行代码审查并给出 GO / CONDITIONAL / NO-GO 结论,具备确定性去重、8 道门禁的发布就绪度综合评估,以及持久化的磁盘运行目录 (report.md + report.json + manifest.json)。

引导(首先执行)

在开展任何审查工作之前,请按照 @.ctxr-fsm/memory/bootstrap.md 中的说明,确保已安装 ctxr-fsm、已初始化项目、已向此客户端注册 MCP 服务,并且监督进程正在运行。 引导流程具有幂等性,在预热路径上速度很快(监督进程和 MCP 服务已运行时约需 1.5–1.8 秒)。

如果缺少该软件包,请先询问用户,然后再运行安装命令。 在聊天中逐字打印拟执行的命令(即引导表格中的确切 uv add 'ctxr-fsm[all]' / pipx install 'ctxr-fsm[all]' 行),并要求用户明确同意后再继续。不要自动安装。不要串联多次安装尝试。这是原则 1(需求预检查,询问是否满足)的软件包缺失分支,应用于技能启动。

然后为每个项目注册此技能的规范和内联处理程序一次:

uv run python -m code_review.install

安装程序会输出一个简短的 JSON 信封,总结结果:

{
  "db_path": ".ctxr-fsm/fsm.db",
  "handlers_registered": 9,
  "spec_created": true,
  "spec_id": "code-reviewer",
  "spec_version": 1
}

信封中的路径相对于项目根目录,因此该制品在推送到 git 或在不同机器之间移动后仍能保留。

当规范正文未发生变化时,重新运行安装程序不会产生任何操作(spec_created 将变为 false;沿用相同版本)。内联处理程序始终会重新注册,以便新的 Python 进程能够加载它们。

运行审查

引导流程完成且规范已注册后,通过 fsm.* MCP 工具系列运行一次审查:

  1. 启动运行。 调用 fsm.start_run(spec_id="code-reviewer", args={"base": "<sha>", "head": "<sha>"})。 根据需要在 args 下添加可选字段:full(bool)、 scope-dir、scope-lang、scope-framework、scope-reviewer、 scope-severity、scope-gate、max-reviewers、format (markdown | json | auto)。记录返回的 run_id。

  2. 循环处理。 重复调用 fsm.get_brief(run_id),并根据 返回的 brief 作出响应:

    • 终态 brief。 从运行的最后状态读取 verdict + run_dir_path。 原样打印 <run_dir_path>/report.md 的内容。至此完成。

    • 工作器 brief(brief.has_worker == true)。派发一个 子 Agent,设置 prompt = brief.worker.prompt_template 和 inputs = brief.inputs。子 Agent 返回的结构化 JSON 即为工作器输出。调用 fsm.commit_outputs(run_id, outputs=<sub-agent-response>, signature=<cosignature>)。 继续循环。

    • 循环 brief(brief.has_loop == true)。派发方式与工作器 brief 相同,brief.iteration_n 表示当前迭代索引。提交本次迭代的输出;引擎 决定是继续推进,还是发起下一次迭代。

      完成所有迭代——绝不要提前结束循环。 引擎发出循环 brief 时, 它要求你在每次提交后继续调用 fsm.get_brief(run_id),直到返回其他状态的 brief。循环 是否终止由引擎决定(依据工作器的 loop_done 标志和规划器的 total_batches)。一次提交即一次迭代;如果有 N 个批次,就必须完成 N 次迭代。

      第一个循环 brief 的 iteration_n = 1。提交第 1 次迭代的输出后, 引擎会重新进入同一状态,设置 iteration_n = 2 并返回新的 brief;继续运行 循环,直到 fsm.get_brief 返回 state_id 不再是 dispatch_specialists 的 brief。编排器绝不能根据自己的计数器 退出派发循环;只有引擎知道规划器的批次何时全部处理完毕。

      此 bug 的表现:最终的 report.md 中,专家显示 status: fail 和 skip_reason: "no per-leaf output written" 或 "no output written for shard <n>",尽管编排器认为每个派发的子 Agent 都已成功。原因:编排器提交第 1 次迭代的输出后,没有继续轮询下一个 brief, 而是直接越过了 dispatch_specialists(例如直接调用 merge_specialist_outputs)。因此,合并器在 loop_iters 中只看到第一个批次的单元,为所有尚未派发的单元生成失败行, 运行最终以 NO-GO 结束,且大多数审查者都被默默标记为失败。现在,运行器会在这种情况下直接抛出 DispatchLoopExitedEarlyError(而不是 默默生成降级报告),让故障作为验证后的错误显现。

    • 内联 brief。 你绝不会看到它们。内联状态 (risk_tier_triage、activate_leaves、collect_findings、 verify_coverage、synthesize_release_readiness、 write_run_directory、emit_stdout、short_circuit_exit、 stage_a_empty)会在 ctxr-fsm 内由服务端推进。工作器提交后返回的下一个 brief,可能已经是一个或多个内联步骤之后的 brief。

  3. 停止。 当 brief 为终态时停止;或者,如果任何一次提交返回 错误封装(参见 .ctxr-fsm/memory/principles.md 中的原则 4–5),也停止。

Worker 分派——并发

dispatch_specialists 状态的 worker 是唯一负责扇出的 worker。它是一个 Loop 状态:上游的 plan_specialist_batches 内联处理器会将每个选中的叶节点划分为一个或多个确定性批次(通常每批 3–5 个单元,由 tier 决定),引擎会针对每个批次驱动一次 Loop 迭代。每次迭代的说明包含:

  • iteration_n — 从 1 开始的迭代计数器。
  • inputs.specialist_batches[] — 完整计划。
  • inputs.total_batches — 规划器预期的 Loop 长度。

每次迭代中:使用客户端的并行工具调用机制,并发分派 specialist_batches[iteration_n - 1].units[] 中的每个单元(Claude Code:在一条消息中发起多个 Task / Agent 调用;Codex:使用等效机制),收集每个子 Agent 的输出,然后将聚合载荷({batch_index, iter_outputs[], loop_done})提交到 fsm.commit_outputs,并匹配 worker 的 response_schema。

Loop 的终止由引擎决定,而不是由你决定。 在最后一次迭代中设置 loop_done = (iteration_n == total_batches)——但绝不要自行推进到 dispatch_specialists 之后。每次提交后,都要再次调用 fsm.get_brief(run_id)。只要下一份说明仍包含 state_id = "dispatch_specialists",就还有一次迭代需要运行。只有当下一份说明进入后续状态(通常是 merge_specialist_outputs)时,分派才算完成。

循环直至为空的示例(伪代码)
while True:
    brief = fsm.get_brief(run_id)
    if brief.terminal:
        print(brief.run_dir_path)
        break
    if brief.has_loop and brief.state_id == "dispatch_specialists":
        # One iteration = one planner batch. Drain every unit IN PARALLEL.
        current_batch = brief.inputs["specialist_batches"][brief.iteration_n - 1]
        iter_outputs = dispatch_units_in_parallel(current_batch["units"])
        loop_done = brief.iteration_n == brief.inputs["total_batches"]
        fsm.commit_outputs(run_id, outputs={
            "batch_index": brief.iteration_n,
            "iter_outputs": iter_outputs,
            "loop_done": loop_done,
        })
        # Do NOT break here. Continue the WHILE loop so we re-poll
        # fsm.get_brief; the engine reissues another dispatch_specialists
        # iteration (with iteration_n + 1) until total_batches is reached.
        continue
    if brief.has_worker:
        outputs = dispatch_single_agent(brief.worker)
        fsm.commit_outputs(run_id, outputs=outputs)
        continue

单次迭代内并行执行的专家数量上限取自 risk_tier_triage 的 cap 字段:trivial=3、lite=8、full=20、sensitive=30;也可以通过 args["max-reviewers"] 覆盖(限制在 [3, 50] 范围内)。迭代次数则独立计算,取决于规划器给出的 total_batches。对一个包含 100 个叶节点的评审任务,在 full tier 下使用 batch_size 5,会产生 20 次迭代,每次并行分派最多 5 个单元。

诊断:DispatchLoopExitedEarlyError

如果合并器检测到 loop_iters[] 涵盖的 (leaf_id, sub_index) 单元少于 specialist_batches[] 计划的数量,就会抛出 DispatchLoopExitedEarlyError,而不是生成一份专家静默失败的 NO-GO 报告。错误消息会包含缺失的单元 ID,操作人员可以立即查看编排器跳过了哪些迭代。修复始终在编排器一侧进行:继续运行循环,直到 fsm.get_brief 不再返回 dispatch_specialists。

各状态的工具范围

每个工作状态都会在 FSM 规范中固定一个 allowed_tools 允许列表。 该列表是为该状态分派的子 Agent 可以调用的工具的准确集合。工具 ID 使用 Claude Code 权限格式(作用域 shell 命令使用 Bash(<prefix>:*),其他工具使用裸工具名称);其他执行框架会在分派时进行转换。

状态allowed_tools
scan_projectBash(git diff:*)、Bash(git log:*)、Bash(git status:*)、Bash(git ls-files:*)、Bash(cat:*)、Read、Glob
tree_descendRead
llm_trim(无 — 仅根据简述进行推理)
tool_discoveryBash(eslint:*)、Bash(ruff:*)、Bash(mypy:*)、Bash(npm test:*)、Bash(pytest:*)、Bash(cargo:*)、Bash(go test:*)、Bash(which:*)、Read
dispatch_specialistsRead、Grep、Glob、WebFetch、Bash(git diff:*)、Bash(git log:*)

内联状态(risk_tier_triage、activate_leaves、 collect_findings、verify_coverage、synthesize_release_readiness、 write_run_directory、emit_stdout、short_circuit_exit、 stage_a_empty)以及 terminal 状态的允许列表均为空 — 它们在 ctxr-fsm 内由服务端运行,不会分派给子 Agent。

为工作状态分派子 Agent 时,将该状态的 allowed_tools 原样 传递到子 Agent 的工具权限格式中(Claude Code:--allowedTools=<list>;Codex 对应参数:--tools; Cursor:对应参数)。然后,对于子 Agent 发出的每次非 fsm.* 工具调用,都要调用 fsm.observe_tool_call,以便漂移检测器进行审计。违规会触发 off_allowlist_tool_call(权重 5.0); 累计值 > 10 时会自动暂停运行。

此技能的产出

在 <project>/.skill-code-review/<yyyy>/<mm>/<dd>/<shard>/<rest>/ 下生成 report.md(Markdown)、report.json(机器可读)和 manifest.json(技能侧运行元数据)。具体格式见 report-format.md。 判定结果:GO、CONDITIONAL 或 NO-GO。

可观测性

该技能的逐状态验证器面板会在每次工作状态提交时,向 FSM 事件流发出 verifier_passed 和 verifier_rejected 事件。要实时查看这些事件:

  1. 在 http://localhost:7475/runs/<run_id> 打开 fsm UI(该 URL 由编排器在 start_run 时输出;端口与 ctxr-fsm 监督程序配置的 UI 端口一致)。
  2. 打开此运行的 AdminSheet;漂移部分会显示逐状态验证器结果时间线。
  3. 点击任一工作状态(scan_project、tree_descend、 llm_trim、tool_discovery、dispatch_specialists),然后切换到 此状态的事件选项卡 — 该状态的每个 verifier_passed / verifier_rejected 事件都会与面板中各投票者给出的原因一并列出。

当同一工作状态达到连续拒绝上限 (目前为 3,定义于 code_review.handlers._VERIFIER_REJECTION_LIMIT)时,编排器会将运行推进到内联的 verifier_stuck 状态。 该状态会发出 degraded_run 信封,供 synthesize_release_readiness 使用,以降低判定结果(覆盖不完整的运行不会得到 GO)。fsm UI 会在受影响状态的 Sheet 上以黄色标记显示这一僵局。

另请参阅

程序化运行器(生产级编排)

code_review/runner.py::run_review(args, dispatch_worker, dispatch_specialist, ...) 在进程内驱动 FSM,并通过受控、容错的线程池调度各叶级专家——这是实现并行处理 + 韧性的核心:

  • 有界、自适应并行度(AIMD)。 _AdaptiveLimiter 将实时 worker 数量限制在 [min_workers, max_workers];收到速率限制信号时数量减半,持续成功时则增加 1(线程由 ThreadPoolExecutor 负责)。
  • 可容忍速率限制。 单元引发 RateLimitError 时 → 乘法退避 + 重试,并缩小线程池。
  • 可容忍上下文溢出。 单元引发 ContextOverflowError 时,会进一步拆分为多个子分片(文件均分),然后重新调度;无法拆分的单个文件会成为一个 failed 单元——绝不丢弃。
  • 100% 覆盖率。 每个计划中的单元都会被调度;仍然失败的单元会标记为 status: "failed"(合并阶段会确保没有遗漏文件)。
  • 与模型无关。 每个状态 / 叶级的 LLM 调用均通过注入的钩子实现 (dispatch_worker / dispatch_specialist,或环境变量 CTXR_SCR_WORKER_DISPATCH / CTXR_SCR_SPECIALIST_DISPATCH)。

本 SKILL.md 中的 LLM 编排路径与此程序化运行器共用相同的 FSM + 内联处理程序;运行器是通过代码确定性、并行且容错地驱动审查的方式。

评论

支持 Markdown