Astro Triage 流水线中的 Bug 复现阶段:reproduce 技能从 Issue 校验到 report.md 输出的完整工作流
Astro 仓库内置了一套面向 AI Agent 的 bug 分诊(triage)流水线,其中 reproduce.md 定义了流水线的第一个阶段:把一个 GitHub issue 变成可在本地运行的最小复现项目,并产出供下游 Agent 使用的 report.md。本文围绕该文档的完整工作流展开——输入校验、六类提前退出条件、五类复现项目的搭建方式、复现验证步骤、服务器生命周期管理规则,以及 report.md 的输出契约,并结合 pnpm-workspace.yaml、.gitignore 与 examples/ 模板说明其在本仓库 monorepo 中的落地机制。读完后你可以理解 Astro 团队如何让 Agent 在有限时间预算内可靠地判定“一个 issue 是否真实可复现”。
复现阶段在整体流水线中的位置
Triage 流水线由 SKILL.md 编排,共四个阶段:
- Reproduce(本文主题):读取并遵循 reproduce.md,隔离上下文中执行;
- Diagnose:读取 diagnose.md,在
packages/源码中定位根因; - Verify:读取 verify.md,判定行为是 bug 还是有意设计;
- Fix:读取 fix.md,实现最小修复、补单测、创建 changeset。
复现阶段的输出结果直接决定后续走向:issue 被跳过(host-specific、版本过旧等)→ 直接进入 Output;无法复现 → 进入 Output;成功复现 → 继续诊断阶段。
各阶段之间不共享对话上下文,唯一的上下文载体是 triageDir 目录下的 report.md。reproduce 文档对此有明确的强约束(CRITICAL):无论结果如何——即使复现失败、遇到意外错误或中途放弃——都必须写出 report.md,因为编排器和下游技能依赖该文件判断发生了什么;如果没写,整条流水线会“静默失败”。同时文档用 SCOPE 约束了本阶段的边界:只负责复现,完成工作流后立即结束,不做根因诊断、不修 bug、不派生子任务。
两个前置变量
文档定义了贯穿全流程的两个变量,可由编排器以 args 传入,也可在独立运行时从对话上下文推断:
triageDir— 复现项目所在的目录(例如triage/issue-123)。未传入时应从之前的对话中推断;issueDetails— GitHub API 返回的 issue 详情载荷,必须由用户显式提供或来自之前的工具调用结果;若缺失,可运行gh issue view ${issue_number}直接从 GitHub 拉取。
仓库侧为这套约定提供了配套支持:pnpm-workspace.yaml 的 workspace 列表中包含 'triage/*',意味着每个 triage/gh-<issue_number> 目录都会被纳入 pnpm workspace;同时 .gitignore 中有 /triage/ 条目,保证这些一次性复现项目不会污染仓库状态。
Step 1:确认 Bug 细节
首要任务是确认 issueDetails 已就位;否则直接失败——没有细节就无法分诊。拿到细节后需要仔细阅读四部分信息:
- bug 描述,以及期望行为与实际行为的对照;
- issue 中提供的复现步骤;
- 环境细节,特别是
astro info输出中的 Astro 版本; - 评论区中可能澄清问题的讨论。
Step 2:提前退出条件(Early Exit)
在动手复现之前,先判断该 issue 是否因“沙箱复现环境的限制”而应被跳过。命中任一条件就跳到 Step 5,在 report.md 中写明跳过原因(skip details)。
一个关键的动态规则:评论会使早期退出失效。提前退出只有在该 issue 的后续评论没有“推翻”它时才成立。文档给出的例子:若原报告者使用的是 Astro 3.0,初次运行会以 unsupported-version 退出;但在一次后续运行中,如果有评论者发布了基于最新 Astro 版本的类似复现,那么这就不再是有效的提前退出,而应继续走完整工作流。
文档明确支持的六类提前退出条件如下:
| 条件 | 标识 | 判定依据 |
|---|---|---|
| 非可执行报告 | not-actionable |
issue 不是 bug 报告。该工作流只分诊 bug——功能请求、建议、讨论都不在此范围内 |
| 缺少细节 | missing-details |
issue 缺少有效复现(见 Step 3 的复现形式列表),或缺少对用户期望结果的描述(例如 issue 模板中“期望结果”一节未填写)。两者都是成功复现和后续验证所必需的 |
| 不支持的 Astro 版本 | unsupported-version |
bug 针对 Astro 4.x 或更早版本。从 astro info 输出或 package.json 中查找版本号 |
| 宿主平台专属问题 | host-specific |
bug 只能在特定托管平台(Vercel、Netlify、Cloudflare、Deno Deploy 等)上复现。信号包括:issue 引用了宿主专属 adapter(@astrojs/vercel、@astrojs/netlify、@astrojs/cloudflare);或 bug 只“在生产环境/部署后”出现,在 dev 与本地 preview 构建中均无法复现 |
| 运行时专属问题 | unsupported-runtime |
bug 只出现在 Bun 或 Deno 上。沙箱只支持 Node.js |
| 维护者覆盖 | maintainer-override |
仓库维护者在评论中表示不应在此复现该 issue。判断评论者是否为维护者:检查 issueDetails 中该评论的 authorAssociation 字段,值为 MEMBER、COLLABORATOR 或 OWNER 即视为维护者 |
Step 3:搭建复现项目
每份 bug 报告都应包含某种形式的复现。复现项目放入 triageDir(例如 triage/gh-123);未提供 triageDir 时默认为 triage/gh-<issue_number>。文档按 issue 提供的复现形式分五种情况处理。
形式一:StackBlitz 项目 URL(https://stackblitz.com/edit/...)
用 stackblitz-clone 下载到 triageDir:
npx stackblitz-clone@latest <stackblitz-url> <triageDir>
形式二:StackBlitz GitHub URL(https://stackblitz.com/github/...)
这是 StackBlitz 上常见的“在 StackBlitz 中打开 GitHub 仓库”的专用 URL。收到此类链接时,解析出 GitHub 的 org 与 repo 名,然后按下面的“GitHub URL”方式处理。
形式三:GitHub 仓库 URL(https://github.com/...)
克隆到 triage 目录,并删除 .git 目录以避免与宿主仓库冲突:
git clone https://github.com/<owner>/<repo>.git <triageDir>
rm -rf <triageDir>/.git
若 issue 中指明了特定分支或子目录,在删除 .git 之前先 checkout 该分支,或只拷贝相关子目录。
形式四:Gist URL(https://gist.github.com/)
通过 GitHub API 拉取 gist 内容以理解复现:
curl -s "https://api.github.com/gists/<gist-id>"
此时往往仍需要从零搭建一个项目(走下面的手动步骤回退路径),再把 gist 中的文件应用进去。
形式五:手动步骤(无复现 URL)
若没有提供复现 URL,则按 issue 作者给出的手动步骤操作;未指明模板时,默认使用仓库的 minimal 模板:
# 1. 列出可用的示例模板
ls examples/
# 2. 删除所选模板的 node_modules,避免 `cp -r` 出问题
rm -rf examples/<template>/node_modules
# 3. 将所选模板拷贝到 triage 目录
cp -r examples/<template> <triageDir>
# 4. 在工作区根目录重新安装,补回缺失的 node_modules 依赖
pnpm install --no-frozen-lockfile
创建后用 cat <triageDir>/package.json 验证项目落在正确位置。
为什么 pnpm install --no-frozen-lockfile 能把复现项目“接进” monorepo:从 pnpm-workspace.yaml 可以看到 'triage/*' 是 workspace glob,且配置了 preferWorkspacePackages: true 与 linkWorkspacePackages: true——因此复现项目(如 examples/minimal/package.json 中声明的 astro 依赖)在 workspace 根目录安装后,会优先解析到本仓库 packages/astro 的源码版本而非注册表中的已发布包。这正是 triage 工作流能够针对当前检出中的代码验证 bug 的关键;同时 --no-frozen-lockfile 允许为复现项目新增依赖(如 @astrojs/react 等集成)时更新锁文件。
复现项目搭好后,按 issue 需要最小化地改造它:
- 更新
astro.config.mjs中必要的配置变更; - 增改依赖或 Astro 集成(
@astrojs/react等); - 增改触发 bug 的页面、组件、中间件等;
- 增改 issue 中提到的其他文件。
文档强调:保持复现尽可能小——只添加 issue 报告者记录为触发 bug 所必需的内容。
Step 4:在 Triage 项目中尝试复现
可使用一切可用工具——pnpm run dev|build|preview|test、curl、agent-browser 等。复现验证分三步:
- 触发 bug:按 issue 的复现步骤操作,确认 bug 出现;
- 验证基线:移除或反转触发代码,确认项目在不带触发代码时工作正常。这是防止误报的保护——如果去掉触发代码项目仍然是坏的,说明问题可能出在你的环境搭建,而不是报告中的 bug;
- 记录观察:记录精确的错误信息与堆栈、触发问题的具体命令,以及问题是稳定复现还是间歇性的。
服务器管理规则
dev/preview 服务器经常是复现必需的,但服务器问题不能吃掉时间预算。文档给出五条硬性规则:
- 连续两次启动失败就放弃(Bail out after 2 failed server starts)。 若
astro dev --background或手动服务器命令连续失败两次,停止重试,不要带着各种变体无限循环。用已有信息写报告。 - 重启前必须先停掉旧服务器。 启动新 dev 服务器前先执行
pnpm -C <triageDir> dev stop;若无效,回退到pkill -f "astro dev\|astro preview\|node.*entry.mjs";再失败就换一个端口(--port 4322),而不是与残留进程死磕。 - 一轮复现就够(One reproduction run is enough)。 确认或否认 bug 之后,不要为了微调配置的再测而重启服务器。追加测试属于 diagnose 阶段,不属于这里——写完发现就继续。
- 尽量用
astro build而非 dev/preview。 构建期复现可以完全绕开服务器生命周期问题。只有当 bug 明确依赖运行中的服务器(HMR、运行时 SSR、请求处理)时才用 dev/preview。 - 永远使用 Astro 的后台 dev 服务器——绝不用
&。 不要用&后台化服务器(例如pnpm dev &),这在 CI 中会挂起。dev 服务器生命周期统一用pnpm -C <triageDir> dev --background/pnpm -C <triageDir> dev stop管理。
这些规则与 SKILL.md 的全局原则一脉相承:不要在基础设施问题上卡死——服务器起不来、端口被占、CI 环境缺工具时,两次尝试后就用已有数据写报告;一份带着扎实发现的“部分报告”,永远比因耗尽时间在僵死进程上而交不出报告更有价值。
Step 5:写出 report.md
report.md 写到 triage 目录中。它的定位很明确:写给下一个 LLM 阶段看的详细内部报告,不是写给人看的——下游技能访问不到原始 issue,report.md 是它们唯一的上下文来源。因此要写得详尽,包含:
- 原始 issue 的标题、描述及 issue 正文中一切相关细节。“包含太多上下文”永远好过“太少”,没人知道哪条信息会在后续(例如在代码库中找修复)时派上用场;
- 完整的环境细节;
- 所有已尝试的步骤及其结果;
- 完整的错误信息与堆栈;
- 对代码库的观察、关于根因的猜想;
- 任何能让下一阶段工作更快的信息。
报告还必须包含日后由 comment 技能生成最终 GitHub 评论所需的全部信息:
- 环境细节(包版本、Node.js 版本、包管理器);
- 复现步骤(编号列表);
- 期望结果与实际结果的对照;
- 错误信息与堆栈;
- 该 issue 是被复现、未复现还是跳过(以及原因)。
从下游技能可以印证这份契约的分量:diagnose.md 第一步就是读 report.md,未复现/跳过时直接追加 “DIAGNOSIS SKIPPED: No reproduction”;fix.md 则在未复现时追加 “FIX SKIPPED: Not reproduced” 并返回 fixed: false。所有阶段都只读、只追加同一个文件,形成一条以 report.md 为总线的单向信息流。
仓库中的可验证证据
这份工作流不是停留在纸面的提示词,仓库中有多处相互印证的实现与验证设施:
- workspace 接线:pnpm-workspace.yaml 中的
'triage/*'glob 加上preferWorkspacePackages: true/linkWorkspacePackages: true,使triage/gh-<n>下的复现项目在 workspace 根目录执行pnpm install --no-frozen-lockfile后链接到packages/下的源码; - 目录可丢弃性:.gitignore 中的
/triage/与.compiler/条目,保证复现项目与可选克隆的编译器源码(withastro/compiler,供 diagnose.md 与 verify.md 在涉及.astro文件转换时查阅)都不进入版本历史; - 默认模板:examples/minimal/ 即“手动步骤”回退路径的默认起点,其 package.json 声明
node >= 22.12.0与astro依赖,对应 fix 阶段要求运行时代码面向 Node.js>=22.12.0的约束; - 技能评测:evals/evals.json 中第 2 条评测(“Runtime binding is undefined only after deploying to Cloudflare Pages”)直接验证了本文 Step 2 的
host-specific提前退出:断言要求triage/evals/cloudflare-binding/report.md存在且明确分类为host-specific跳过,且不得执行任何项目搭建、安装、构建、起服务器或诊断动作——正是“命中提前退出后跳过 Step 3/4、直接写 report.md”这一流程的自动化验证。
小结
reproduce.md 是 Astro triage 流水线的守门阶段,其设计要点可以归纳为四条:输入校验不通过就失败、命中六类提前退出条件就快速止损并如实记录;复现项目按 issue 提供的五种形式(StackBlitz URL、StackBlitz GitHub URL、GitHub URL、Gist、手动步骤)落到 triageDir,并通过 workspace 机制链接到本仓库源码;复现验证遵循“触发—基线—记录”三步与严格的服务器管理规则,防止在僵死进程上耗尽预算;最终无论成败,report.md 都必须承载 issue 全量上下文、环境细节、步骤结果与错误证据,成为 diagnose、verify、fix 各阶段以及最终 GitHub 评论的唯一事实来源。理解这套契约,就能理解 Astro 如何用可验证的 Agent 技能把“issue 是否可复现”变成一个有明确边界、可评测、可追溯的自动化环节。
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