Metabase Fixbot 自动化修复工作流解析:从 Linear Issue 到代码修复、验证与提 PR 的完整链路
【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase
Fixbot 是 Metabase 仓库中为"修复 Linear issue"而设计的自动化 AI 工作流:它以/fixbot斜杠命令为编排入口,借助仓库内的 Claude Code 命令、dev/bot下的 Agent 提示模板,以及mage的 bot 系列 CLI 命令,完成问题上下文采集、Agent Prompt 生成、代码修复与自验、用户验收乃至 PR 提交流程。本文以 .claude/commands/fixbot.md 为主干,结合其下游文件与mage源码实现,说明这套工作流每一步做什么、为什么这样做,以及每个命令与产物的真实形态,供需要扩展或维护仓库开发工具链的读者参考。
一、工作流定位:在哪个环境下、修什么问题
先明确 fixbot 的边界。编排命令的开头就定义了它的运行方式与约束:
"You are the orchestrator for the fixbot workflow. Fixbot fixes a Linear issue, running directly in this project against the locally running server."
也就是说,fixbot 直接在当前仓库中运行,针对一台本地已经启动的 Metabase 服务进行排障和修复,而不是在隔离的临时环境中。如果希望把整个修复过程放进一个隔离的 git worktree(独立的后端 + 前端),则改用复合命令/autobot <branch> /fixbot <args>——autobot会先创建/复用 worktree、搭好开发环境,再在会话里以/fixbot作为内部命令执行(见 .claude/commands/autobot.md)。
在 Metabase 的机器人家族里,fixbot 与 qabot(QA 评审 PR)、reprobot(复现 bug 并产出失败测试)、uxbot(UX 评审)并列,分别对应修复、验收、复现与交互体验评估;dev/bot/下与之配套的 Agent 模板包括 dev/bot/fixbot-agent.md 等,公共操作手册则放在 dev/bot/common/(environment-discovery、test-strategy、reproduction-strategies、report-generation 等),fixbot 是这条自动化链路的"实施者"。
1. 直接运行与 worktree 运行的选择
| 运行方式 | 触发命令 | 运行环境 | 适用场景 |
|---|---|---|---|
| 直接运行 | /fixbot [args] | 当前仓库 + 本地已运行服务 | 快速修复,允许与开发中的工作区共享 |
| 隔离运行 | /autobot <branch> /fixbot <args> | 独立 worktree + 独立后端/前端 | 需要干净分支、互不干扰的修复 |
autobot 文档还支持 PR 预览环境(形如https://pr<NUMBER>.coredev.metabase.com)的远程模式,此时本机不启动服务,bot 直接对接已部署的预览环境。
二、Step 1:生成每次运行的隔离目录(Per-run Directory)
工作流的第一步是为本次运行分配独立的产物目录,这是整套设计的地基:
- 生成时间戳,格式固定为
YYYYMMDD-HHMMSS(如20260907-070155); - 若无法确定当前墙钟时间,运行
./bin/mage -bot-timestamp获取——明确禁止直接用date命令; - 定义两个变量并据此建目录:
TIMESTAMP=<YYYYMMDD-HHMMSS> OUTPUT_DIR=.bot/fixbot/<TIMESTAMP>紧接着执行mkdir -p <OUTPUT_DIR>,保证后续所有cp/Write写入都能成功。设计要点有三条,直接体现了多任务并发的可靠性考量:
- 本次运行产生的所有文件(包括 discover 阶段工件)都只能落在
<OUTPUT_DIR>/下,不允许出现跨运行共享的路径; - 因为目录名带秒级时间戳,同一仓库内多次
/fixbot调用不会互相冲突; - 严禁从上次运行的目录复制旧工件(例如
linear-context.txt),上下文必须让 discover 重新生成,避免把过期内容带进本次修复。
这样的"一运行一目录 + 全局唯一产物路径"约定,配合 mage/src/mage/bot/prompt.clj 中写入文件前自动mkdirs父目录的实现,让每个阶段的文件操作都无须担心路径竞争。
三、Step 2:上下文收集——discover 阶段与 config.env
当<OUTPUT_DIR>/config.env不存在时,先执行 discover:
/fixbot-discover $ARGUMENTS --output-dir <OUTPUT_DIR>Discover 把工件直接写入<OUTPUT_DIR>/,从不写共享位置。随后编排者读取两份关键输入:
| 文件 | 从中提取的内容 |
|---|---|
<OUTPUT_DIR>/config.env | ISSUE_ID、BRANCH_NAME、APP_DB |
<OUTPUT_DIR>/linear-context.txt | 完整 Linear issue 内容(注入 Agent 模板的LINEAR_CONTEXT) |
3.1 fixbot-discover 的职责拆解
.claude/commands/fixbot-discover.md 把上下文发现拆成六个子步骤:
① 解析 Issue ID。位置参数支持三种输入格式,需归一化为 Linear issue ID:
- Linear issue ID(如
MB-12345、UXW-3155)——直接使用; - GitHub issue 编号(如
12345)——先解析为 Linear; - GitHub issue URL——先抽取编号再解析为 Linear。
GitHub 输入的解析路径是:用gh issue view <NUMBER> --repo metabase/metabase --json body,comments,title拉取 issue,在正文与评论里搜索 Linear 链接(https://linear.app/metabase/issue/[A-Z]+-[0-9]+模式)并抽取 ID;找不到 Linear 链接时,改用./bin/mage -bot-fetch-issue以 GitHub issue 标题派生搜索词直接搜索 Linear;仍无匹配则明确告知用户并停止。ID 最终必须通过[A-Z]+-[0-9]+校验。
② 解析运行目录。从$ARGUMENTS中提取--output-dir <PATH>(缺失即停止并提示),TIMESTAMP取路径尾部的时间戳段。
③ 从 Linear 拉取 issue。执行./bin/mage -bot-fetch-issue <ISSUE_ID>,读取输出中的 issue 详情与分支名,并把完整输出写入<OUTPUT_DIR>/linear-context.txt。
该命令的底层实现见 mage/src/mage/bot/linear.clj:通过 GraphQL(endpoint 为https://api.linear.app/graphql)按 identifier 查询,返回identifier / title / description / url / branchName / state / comments等字段;API Key 通过 mage/src/mage/bot/env.clj 的共享解析函数获取。值得注意的是 linear.clj 内部把该命令记为-fixbot-fetch-issue(usage 提示即./bin/mage -fixbot-fetch-issue MB-12345),说明这套 bot CLI 在迭代中调整过命名,新增命令时需以当前mage任务表为准。
④ 确定分支名。优先取 Linear issue 输出中声明的分支;若未指定,则用 issue ID 小写化作为分支名(例如MB-12345→mb-12345)。
⑤ 推断应用数据库。依据 issue 描述/评论内容,从三个候选中选择一个:
- 提到MySQL问题、MySQL 专有 SQL 语法或错误信息 →
mysql; - 明确提到MariaDB→
mariadb; - 其他一律 →
postgres(默认)。
这一点之所以重要,是因为 Metabase 支持 Postgres/MySQL/MariaDB 等不同应用数据库,某些 bug 只在特定方言下复现,bot 需要按正确数据库启动测试环境。
⑥ 写结果。用结构化格式把五元组写入<OUTPUT_DIR>/config.env:
APP_DB=<postgres|mysql|mariadb> BRANCH_NAME=<branch-name> ISSUE_ID=<ISSUE_ID> TIMESTAMP=<TIMESTAMP> OUTPUT_DIR=<OUTPUT_DIR>autobot 的编排逻辑里还有一层兜底:读完 config.env 后若APP_DB缺失,默认按postgres处理。这套"配置文件即契约"的约定,让 discover 阶段与后续的 Agent 阶段通过文件解耦,任何一方都能独立测试。
四、Step 3:用 mage 生成 Agent Prompt
上下文齐备后,编排者用mage的模板引擎从 Agent 提示模板渲染出本次运行专属的 prompt:
./bin/mage -bot-generate-prompt \ --template dev/bot/fixbot-agent.md \ --output <OUTPUT_DIR>/prompt.md \ --set "ISSUE_ID=<ISSUE_ID>" \ --set "BRANCH_NAME=<branch-name>" \ --set "APP_DB=<postgres|mysql|mariadb>" \ --set "OUTPUT_DIR=<OUTPUT_DIR>" \ --set-from-file "LINEAR_CONTEXT=<OUTPUT_DIR>/linear-context.txt"参数语义:
| 参数 | 作用 | 说明 |
|---|---|---|
--template | 指定 Agent 提示模板 | 指向 dev/bot/fixbot-agent.md,必填且要求文件存在 |
--output | 渲染产物路径 | 写入<OUTPUT_DIR>/prompt.md,必填 |
--set KEY=VALUE | 内联变量 | 例如 ISSUE_ID、BRANCH_NAME、APP_DB、OUTPUT_DIR |
--set-from-file KEY=path | 从文件读入多行值 | LINEAR_CONTEXT内容通常很长且含换行,直接内联需要繁琐的 shell 转义,故用此方式按路径读取 |
4.1 模板引擎源码级原理
mage/src/mage/bot/prompt.clj 的generate-prompt!展示了渲染的实际行为:
- 必填参数校验:
--template/--output缺失即报错退出;模板文件不存在同样退出; - 先执行
resolve-file-includes,把{{FILE:path}}占位符替换为仓库根目录下对应文件的内容——只做一遍,被包含的内容不会被再次扫描,路径相对仓库根目录,文件缺失时替换为<!-- FILE NOT FOUND: path -->并给出警告; - 再用
--set与--set-from-file合并出的替换表,把{{KEY}}占位符逐一替换(源码中是逐 key 执行str/replace,因此替换后的值不会参与其他 key 的展开); parse-set-args对缺少=的--set项给出黄色警告并忽略;parse-set-from-file-args对不存在的文件也仅警告并将值置为空串;- 写入前
.mkdirs自动创建父目录,然后spit输出并打印Wrote prompt:。
这样,fixbot-agent.md 模板里的两类占位符——{{ISSUE_ID}}、{{BRANCH_NAME}}、{{APP_DB}}、{{LINEAR_CONTEXT}}这类变量占位符,以及{{FILE:dev/bot/common/environment-discovery.md}}、{{FILE:dev/bot/common/test-strategy.md}}这类文件包含占位符——会在渲染时被统一替换,最终产出一个自包含、可直接作为 Agent 系统提示的 prompt.md。这种"模板 + 文件包含 + 运行时变量"的结构,让 agent 手册可以复用公共章节,避免在多份模板间复制粘贴漂移。
五、Step 4:执行阶段——Agent 的任务模型与自主边界
编排者随后读取渲染好的<OUTPUT_DIR>/prompt.md,按其中的 Phase 1–4 顺序执行,且要求所有阶段在同一个 turn 内完成,除非触发 STOP 条件——不能在阶段之间停下来等用户。真正定义 Agent 行为的是提示模板 dev/bot/fixbot-agent.md,其任务模型包含约束、阶段与安全边界三层。
5.1 两条硬性约束
20 分钟时间上限。Agent 若在 20 分钟内未能完成"理解—修复—自检"(Phases 1–3),必须立刻停下,把已有的诊断、部分修复和阻塞点呈现给用户并请求指引——"能说明自己卡在哪里的 fixbot,远比一个默默绕圈烧时间的 fixbot 有价值"。
Know Your Limits。fixbot 只处理能自主完成的简单明确 bug 与功能请求,遇到以下情况必须 STOP 并说明原因:
- 修复涉及复杂架构决策、存在工程师难以一致认同的取舍;
- 改动以难以察觉的方式影响既有功能(如其他功能依赖的行为);
- 功能请求本身该不该做需要产品讨论;
- issue 存在歧义,不同解读会导向差异很大的方案;
- 修复横跨多个子系统、爆炸半径过大;
- 需要靠猜而不是靠把握来确定预期行为。
模板还给出了一个沟通技巧:任何需要用户输入、提问、停下决策、或必须让用户注意的节点,都要用醒目的横幅框住(示例为用╔═╗绘制的 "FIXBOT NEEDS YOUR INPUT" 风格 banner),避免用户错过关键信息。
5.2 六个执行阶段
| 阶段 | 目标 | 关键动作 |
|---|---|---|
| Phase 1 Understand | 理解问题 | 通读已注入的 LINEAR_CONTEXT(不再重复 fetch);充分搜索代码库理解 bug 周边架构;涉及 UI 时用 Playwright MCP 复现;产品行为歧义才问用户 |
| Phase 2 Fix | 修复 | 严格 red/green TDD;后端先写失败 Clojure 测试(./bin/test-agent),前端先写 Jest 单测或 Cypress E2E;补上 issue 编号命名规范 |
| Phase 3 Self-Review | 自检 | 改过的 Clojure 文件跑/clojure-review、TS/JS 文件跑/typescript-review;逐个解决发现的问题;有重大改动则复审,直到干净 |
| Phase 4 Verify | 验证 | UI 修复先由 Agent 用 Playwright MCP 自查;再把精确的验收步骤交给用户:URL 一律用http://localhost:$MB_JETTY_PORT/...(后端端口而非前端 dev server),并提醒登录凭据来自./bin/mage -bot-server-info |
| Phase 5 Open PR | 提交 | 用户确认后提交:绝不提交.claude/、.bot/fixbot/、mage/下的改动;逐个git add path/to/file.clj(禁用git add ./-A);commit 与 PR 中严禁出现 Linear URL 或 issue ID(内部信息不外泄);PR body 用固定模板(Description / How to verify / Checklist)且不加 label |
| Phase 6 Monitor PR | 跟进 | 提 PR 后用/cibot监控 CI 结果并处理失败,直到通过 |
值得注意的是模板环境章节特别说明:开发环境始终是 Enterprise Edition(EE),即使 issue 说的是 OSS 版本也要在 EE 下开发测试;若修复确实必须在 OSS 版验证(例如 OSS-only 行为差异),要停下告知用户,而不是强行尝试 OSS-only 修复。同时关于"用户画像"的设定也约束了交互方式:用户不是开发者,不要向 TA 要实现建议或技术决策,但 TA 是资深的 Metabase 产品用户,可用于澄清预期行为与做 UI 验收。
5.3 复现机器人(reprobot)协同
模板还提供了与 reprobot 的协作路径:若 issue 评论里包含 reprobot 的排查结论(Linear 评论中的 patch 或测试代码),可取来作为 TDD "red" 步骤的起点;但模板同时告诫——reprobot 的分析与根因假设不总是正确,必须对照真实代码自行验证。这与dev/bot/中 reprobot/qabot/fixbot 分角色协同的设计互为印证。
六、支撑 Agent 运行的公共手册与 mage 基建
被{{FILE:...}}注入到提示模板中的公共手册,承载了 agent 实际操作的底层能力:
6.1 environment-discovery:环境发现与免硬编码
dev/bot/common/environment-discovery.md 规定了一套"先发现、后使用"的原则,核心是./bin/mage -bot-server-info——它会一次性输出 Jetty 后端端口、前端 dev server 端口、nREPL/socket REPL 端口、数据库端口、配置文件路径与内容、环境变量与 edition/token/连接 URI。严禁硬编码端口、凭据与 API key,一律从-bot-server-info动态获取。配套的 mage 实现包括:
-bot-repl-eval '<clojure-form>'——统一 REPL 入口,自动在 nREPL 与 socket REPL 间回退(见 mage/src/mage/bot/repl_eval.clj);-bot-api-call /api/<path> [--method ...] [--api-key ...] [--body ...]——API 调用封装(见 mage/src/mage/bot/api_call.clj);-bot-preflight-health——轮询健康检查最长 5 分钟的后端就绪探测(见 mage/src/mage/bot/preflight.clj);- 环境变量解析优先级:
mise.local.toml > .env > .lein-env > 系统环境变量(见 mage/src/mage/bot/env.clj)。
该手册同时强调数据库操作应通过 REPL + Clojure JDBC(Toucan2)进行(如./bin/mage -bot-repl-eval '(do (require (quote [toucan2.core :as t2])) (t2/select :model/Card :id 1))'),而不是在 shell 里直接跑psql/mysql;日志访问可通过metabase.logger.core的(logger/messages)(最近 250 条)与set-ns-log-level!动态调整级别;此外还有 Playwright MCP 工具装载、fail-fast 策略、远程 PR 环境模式(Tailscale 依赖、通过 API 而非本地库访问数据、破坏性测试先快照再回滚)等约束。
6.2 test-strategy:测试类型选型与命令
dev/bot/common/test-strategy.md 提供测试选型矩阵:后端逻辑/查询处理器/API 用 Clojure 单测(test/metabase/...);前端 UI 行为用 Jest 组件测试(co-located*.unit.spec.tsx);端到端用户流程用 Cypress(e2e/test/scenarios/...);混合改动则两者都写。文件命名有固定规律:src/metabase/foo/bar.clj→test/metabase/foo/bar_test.clj。运行命令示例:
./bin/test-agent :only '[metabase.foo-test/issue-12345-test]' bun run test-unit-keep-cljs path/to/file.unit.spec.ts npx cypress run --spec e2e/test/scenarios/category/file.cy.spec.ts测试命名还要求带上 issue 号(如(deftest issue-12345-test ...)),让自动化修复的测试可以追溯到原始工单。
6.3 状态与结果汇报
为了让用户在长任务中保持知情,agent 要维护两个文件:.bot/autobot/llm-status.txt(1–3 行的状态栏,如 "Reproducing issue"/"Writing tests"/"Blocked: ..."),以及<OUTPUT_DIR>/result.md——后者由/autobot-result <branch> <bot>读取展示,格式为"已完成 / 当前状态"两段式摘要加一份不断追加的绝对路径产物清单(diff 分析、截图、API 响应、日志抓取等)。
七、完整命令速查
下面是整套 fixbot 工作流涉及的触发命令与 CLI,便于对照使用:
| 位置 | 命令 | 用途 |
|---|---|---|
| 编排入口 | /fixbot <issue> | 直接在当前仓库修复 Linear issue |
| 隔离运行 | /autobot <branch> [from <base>] /fixbot <issue> | worktree 内修复 |
| 上下文发现 | /fixbot-discover <issue> --output-dir <dir> | 解析 issue、推断 APP_DB、写 config.env |
| 时间戳 | ./bin/mage -bot-timestamp | 生成YYYYMMDD-HHMMSS(禁止用date) |
| 拉取 issue | ./bin/mage -bot-fetch-issue <ISSUE_ID> | Linear GraphQL 拉取 issue 详情 |
| 渲染 prompt | ./bin/mage -bot-generate-prompt --template dev/bot/fixbot-agent.md --output ... --set ... --set-from-file ... | 生成自包含 Agent 提示 |
| 环境发现 | ./bin/mage -bot-server-info | 端口/凭据/API key/环境变量一站式输出 |
| REPL | ./bin/mage -bot-repl-eval '<form>' | nREPL/socket 自动回退求值 |
| API | ./bin/mage -bot-api-call /api/... | 免手工鉴权的 HTTP 调用 |
| 健康检查 | ./bin/mage -bot-preflight-health | 后端就绪轮询(最长 5 分钟) |
| 测试 | ./bin/test-agent/ Jest / Cypress | 后端/前端/E2E 测试 |
八、小结:这套编排设计的可取之处
从仓库实现回看 fixbot 工作流,可以提炼出四个值得借鉴的设计点:
- 文件即接口、目录即隔离:discover 与执行阶段通过
config.env和 per-runOUTPUT_DIR解耦,秒级时间戳保证并发安全,"禁止复制旧工件"杜绝了上下文过期问题; - 模板 + 文件包含 + 变量:
mage -bot-generate-prompt的{{FILE:path}}机制让 agent 公共手册可以在多份 bot 模板间复用,单一事实来源,避免各 bot 行为漂移; - 明确的时间预算与决策边界:20 分钟硬上限、"Know Your Limits" 的 STOP 清单,把自主执行限定在低风险修复内,把架构取舍与产品歧义交还人类;
- 安全与卫生红线:不提交
.claude/、.bot/、mage/等生成/拷贝目录,逐文件 stage,PR 与 commit 零 Linear 内部信息,敏感信息不落公开历史。
对希望基于 .claude/commands/fixbot.md 理解或扩展这套"机器人修复 issue"基础设施的读者,建议按 .claude/commands/fixbot-discover.md → dev/bot/fixbot-agent.md → dev/bot/common/environment-discovery.md 的顺序阅读,并结合 mage/src/mage/bot/prompt.clj 与 mage/src/mage/bot/linear.clj 等实现逐行对照,即可获得从"编排命令"到"底层 CLI"的完整视角。
【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考