gstack /canary 深度解析:基于 browse 守护进程与基线对比的部署后可视化金丝雀监控
【免费下载链接】gstackUse Garry Tan's exact Claude Code setup: 23 opinionated tools that serve as CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA项目地址: https://gitcode.com/GitHub_Trending/gs/gstack
/canary是 gstack(Garry's Stack)技能库中的Post-Deploy Visual Monitor(部署后可视化金丝雀监控)技能。它以"发布可靠性工程师(Release Reliability Engineer)"的视角,在每次发布后的关键 10 分钟窗口内,驱动 browse 无头浏览器守护进程对线上应用反复进行页面加载、截图、控制台错误与性能采样,并与部署前捕获的基线对比,把"已经发布"与"已验证可用"之间的空档补上。阅读本文后,你将掌握/canary的参数语义、基线建立流程、告警分级与瞬态容错规则、健康报告的产物结构,以及它在源码与测试中的底层实现依据。
该技能的权威定义位于 canary/SKILL.md,由 canary/SKILL.md.tmpl 通过bun run gen:skill-docs(见 package.json 的gen:skill-docs脚本)自动生成。技能核心围绕 browse 守护进程展开,browse 的完整命令语义记录在 browse/SKILL.md 与 browse/sections/command-list.md 中。
/canary解决的问题:CI 通过不等于线上可用
技能开头给出了一个典型的失败叙事:一个部署在 CI 中全绿,却在生产环境崩坏,原因可能是缺失的环境变量、CDN 缓存了过期静态资源、或是数据库迁移在真实数据量下比预期慢得多。这类问题的共性在于:它们在 CI 环境里根本不会被观察到,只有真实流量、真实环境变量、真实 CDN 链路才会触发。
/canary的定位就是"shipped 到 verified 之间的安全网",把发现问题的时间窗口压缩到前 10 分钟,而不是 10 小时。它在 docs/skills.md 的技能目录中被描述为SRE角色:"Post-deploy monitoring loop. Watches for console errors, performance regressions, and page failures using the browse daemon.",并在技能正文中将其明确为"post-deploy monitoring mode"。
与技能调度相关的两个环境要素也值得说明:
- 技能以斜杠命令方式唤起,用户在输入
/canary <url>时即触发本技能,frontmatter 中声明了模型需要拥有 Bash、Read、Write、Glob、AskUserQuestion 工具,版本为 1.0.0,premable 层级为 2。 - frontmatter 里的
triggers(如monitor after deploy、canary check、watch for errors post-deploy)让技能在用户以自然语言而非斜杠命令提出"监控部署""金丝雀检查"等请求时也能被正确路由。
底层引擎:browse 守护进程与技能用到的命令
/canary本身不实现浏览器能力,它全部委托给 browse 守护进程。browse 是 gstack 的常驻无头 Chromium,第一次调用自动启动(约 3 秒),此后单条命令约 100ms,且 cookie、标签页、登录会话等状态在多次调用间保持(详见 browse/SKILL.md)。
在每次调用任何 browse 命令之前,技能执行 SETUP 检查来解析可执行文件路径$B:优先使用仓库内的$_ROOT/.claude/skills/gstack/browse/dist/browse,否则回退到$HOME/.claude/skills/gstack/browse/dist/browse;两者都不可执行则输出NEEDS_SETUP,此时需要先向用户确认执行一次性构建(约 10 秒),再运行cd <SKILL_DIR> && ./setup。若系统缺少bun,脚本会用固定版本 1.3.10 并校验 SHA-256 校验和后安装。
/canary监控循环中反复出现的$B命令及其在命令体系中的实现位置如下:
| 命令 | 用途 | 依据 |
|---|---|---|
goto <url> | 导航到页面,超时或报错意味着页面加载失败 | 导航类命令见 browse/sections/command-list.md |
snapshot -i -a -o <png> | 输出可访问性树(-i仅交互元素)并同时生成带标注框的截图(-a -o) | snapshot 标志语义见 browse/sections/command-list.md |
console --errors | 仅过滤输出 console 的错误与警告,是"新控制台错误"告警的数据源 | 在 browse/src/commands.ts 中注册 |
perf | 输出页面加载耗时,是性能回归告警的数据源 | 同上 |
links | 输出全部链接为 "text → href",用于页面自动发现 | 同上 |
text | 输出清洗后的页面文本,作为内容快照 | 同上 |
browse的许多读取类输出(text、links、console 等)会被包裹在BEGIN/END UNTRUSTED EXTERNAL CONTENT标记中,以防御提示注入。snapshot的-D(diff)、-c(compact)、-s <sel>(作用域)、-C(cursor-interactive)等标志可以自由组合,-o仅在同时使用-a时生效,例如$B snapshot -i -a -C -o /tmp/annotated.png。
命令形态与参数
/canary支持四种参数形态,覆盖"发布前建档、发布后持续监控、单次体检"三种场景:
/canary <url>:对某 URL 做发布后10 分钟(默认时长)的持续监控;/canary <url> --duration 5m:自定义监控时长,允许范围为1 分钟到 30 分钟;/canary <url> --baseline:捕获基线截图,必须在部署之前运行;/canary <url> --pages /,/dashboard,/settings:显式指定要监控的页面清单,覆盖默认的自动发现;/canary <url> --quick:单次通过式健康检查,不进入持续监控循环。
未指定--pages时,页面列表从应用导航自动发现(见 Phase 3)。默认监控时长为 10 分钟,每次采样间隔为 60 秒。整个技能遵守Read-only原则:只观察与报告,除非用户明确要求介入修复,否则不修改代码。
七阶段工作流
Phase 1:Setup,建立产物目录
技能启动后先在项目内建立报告目录结构:
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null || echo "SLUG=unknown")" mkdir -p .gstack/canary-reports mkdir -p .gstack/canary-reports/baselines mkdir -p .gstack/canary-reports/screenshotsgstack-slug用于生成当前项目的稳定 slug,失败时回退为unknown。之后解析用户参数:默认时长 10 分钟;默认页面由应用导航自动发现。
在动手监控之前,还需要完成 Step 0 的平台与基准分支探测:通过git remote get-url origin判断托管平台(含github.com为 GitHub、含gitlab为 GitLab),或通过gh auth status/glab auth status兜底,进而用gh pr view或 Git 原生命令(git symbolic-ref、git rev-parse --verify origin/main等)确定 PR 目标分支或仓库默认分支,作为后续所有比较与日志记录的上下文。
Phase 2:基线捕获(--baseline模式)
基线是金丝雀的灵魂。若传入--baseline,则在部署前对每个页面(来自--pages或首页)采集四类证据:
$B goto <page-url> $B snapshot -i -a -o ".gstack/canary-reports/baselines/<page-name>.png" $B console --errors $B perf $B text每个页面需收集:截图路径、console 错误数、来自perf的页面加载时间、文本内容快照。随后写入基线清单.gstack/canary-reports/baseline.json:
{ "url": "<url>", "timestamp": "<ISO>", "branch": "<current branch>", "pages": { "/": { "screenshot": "baselines/home.png", "console_errors": 0, "load_time_ms": 450 } } }完成基线后技能立即STOP并明确告知用户:"Baseline captured. Deploy your changes, then run/canary <url>to monitor.",把部署动作交还给用户,保证基线永远代表"发布前的已知良好状态"。
Phase 3:页面自动发现
未显式给出--pages时,通过以下命令发现导航入口:
$B goto <url> $B links $B snapshot -i从links输出中提取前 5 个站内导航链接,首页总是被包含。随后通过 AskUserQuestion 呈现页面清单,推荐选择 A(主导航目标);用户也可以追加更多页面(B),或只监控首页做快速检查(C)。从 browse/sections/command-list.md 的实现看,links输出为 "text → href" 形态,天然适合提取导航目标。
Phase 4:部署前快照(无基线时的回退参照)
若不存在baseline.json,技能会在部署前先做一次快速参考快照,作为后续回归检测的参照系:
$B goto <page-url> $B snapshot -i -a -o ".gstack/canary-reports/screenshots/pre-<page-name>.png" $B console --errors $B perf需要说明的是,此回退只是"参照点",强度弱于真正的基线。技能因此特意鼓励在部署前使用--baseline:没有基线时,金丝雀退化为健康检查(health check)。
Phase 5:持续监控循环
在指定时长内每 60 秒对每个页面执行一次检查:
$B goto <page-url> $B snapshot -i -a -o ".gstack/canary-reports/screenshots/<page-name>-<check-number>.png" $B console --errors $B perf每次检查后与基线(或部署前快照)对比,按四档告警分级:
- 页面加载失败:
goto返回错误或超时,触发 CRITICAL; - 新控制台错误:出现基线中不存在的错误,触发 HIGH;
- 性能回归:加载时间超过基线的 2 倍,触发 MEDIUM;
- 坏链:出现基线中不存在的新 404,触发 LOW。
循环内置两条经验法则,防止误报与报警疲劳:
- 针对变化告警,而非绝对值(Alert on changes, not absolutes):基线里就有 3 个 console 错误的页面,只要仍然只有 3 个就算正常;多出 1 个新错误才触发告警。性能阈值同样是相对的:2 倍于基线是回归,1.5 倍可能只是正常波动。
- 不要狼来了(Don't cry wolf):只有连续 2 次及以上检查都持续出现的模式才构成告警,单次网络抖动不告警。
一旦出现 CRITICAL 或 HIGH,立即通过 AskUserQuestion 通知用户,告警卡要求包含时间、页面、类型、具体发现、截图证据路径以及基线与当前值:
CANARY ALERT ════════════ Time: [timestamp, e.g., check #3 at 180s] Page: [page URL] Type: [CRITICAL / HIGH / MEDIUM] Finding: [what changed — be specific] Evidence: [screenshot path] Baseline: [baseline value] Current: [current value]用户据此在四个动作中决策:立即调查并停止监控(A)、继续监控等待下一次采样以确认是否为瞬态(B)、立刻回滚部署(C)、判为误报继续监控(D)。
Phase 6:健康报告
监控结束(或用户提前终止)后产出总结报告:
CANARY REPORT — [url] ═════════════════════ Duration: [X minutes] Pages: [N pages monitored] Checks: [N total checks performed] Status: [HEALTHY / DEGRADED / BROKEN] Per-Page Results: ───────────────────────────────────────────────────── Page Status Errors Avg Load / HEALTHY 0 450ms /dashboard DEGRADED 2 new 1200ms (was 400ms) /settings HEALTHY 0 380ms Alerts Fired: [N] (X critical, Y high, Z medium) Screenshots: .gstack/canary-reports/screenshots/ VERDICT: [DEPLOY IS HEALTHY / DEPLOY HAS ISSUES — details above]报告落盘为.gstack/canary-reports/{date}-canary.md与同名前缀的.json两份。同时把结果写入 review dashboard 的 JSONL 日志:
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" mkdir -p ~/.gstack/projects/$SLUG日志条目为{"skill":"canary","timestamp":"<ISO>","status":"<HEALTHY/DEGRADED/BROKEN>","url":"<url>","duration_min":<N>,"alerts":<N>},按项目 slug 分目录持久化在~/.gstack/projects/下,便于跨发布追踪趋势。
Phase 7:基线滚动更新
部署健康时,技能询问用户是否用本次截图更新基线:
- A) 用当前截图更新基线(推荐:部署健康,新基线反映当前生产状态);
- B) 保留旧基线。
选择 A 后,最新截图被复制进 baselines 目录并更新baseline.json。这一机制让基线随健康的发布滚动前进,避免基线老化到与生产严重脱节。
关键规则速查
| 规则 | 含义 |
|---|---|
| 速度优先 | 调用后 30 秒内开始监控,不要过度分析 |
| 对变化告警 | 与基线对比,而非与行业标准对比 |
| 截图即证据 | 每条告警必须附带截图路径,无例外 |
| 瞬态容忍 | 仅对连续 2+ 次检查持续出现的模式告警 |
| 基线是王道 | 无基线的金丝雀只是健康检查 |
| 阈值为相对值 | 2 倍基线为回归;1.5 倍可能是正常波动 |
| 只读原则 | 只观察和报告,不修改代码 |
与发布链路的配合:/land-and-deploy之后的最后一道闸
/canary在技能体系中的上游是 docs/skills.md 描述的发布链路:/ship创建 PR,/land-and-deploy负责合并、等 CI、执行部署并验证生产健康,/canary则在部署完成后立即接管。docs 中给出的典型会话演进是:运行/setup-deploy一次性检测部署平台(Fly.io、Render、Vercel、Netlify、Heroku、GitHub Actions 或自定义)并写入配置,之后/land-and-deploy一条命令从"已批准"走到"已在生产验证",部署完成后用/canary继续盯防:
You: /canary https://myapp.com Claude: Monitoring 8 pages every 2 minutes... Cycle 1: ✓ All pages healthy. p95: 340ms. 0 console errors. Cycle 2: ✓ All pages healthy. p95: 380ms. 0 console errors. Cycle 3: ⚠ /dashboard — new console error: "TypeError: Cannot read property 'map' of undefined" at dashboard.js:142 Screenshot saved. Alert: 1 new console error after 3 monitoring cycles.该文档同时建议在风险较高的发布后周期性重复运行/canary,而不仅是部署完成后的一次性检查。
技能工程化侧面:模板生成与可校验的产物契约
从仓库结构可以观察到两个工程化细节,它们解释了为什么/canary的流程可以被反复执行而不漂移:
其一,SKILL.md 由模板生成。canary/SKILL.md 文件头注释明确标注" AUTO-GENERATED from SKILL.md.tmpl do not edit directly ",需要重新生成时执行bun run gen:skill-docs(即 scripts/gen-skill-docs.ts)。对照 canary/SKILL.md.tmpl 可见,发布流程主体(Arguments、Phase 1-7、Important Rules)保存在模板中,而 preamble、browse 环境探测({{BROWSE_SETUP}})、基准分支探测({{BASE_BRANCH_DETECT}})等共享段落以占位符形式注入,保证所有技能共享同一套运行时规范且互不漂移。
其二,工作流的产物契约被端到端测试锁定。test/skill-e2e-deploy.test.ts 中定义了Canary skill E2E(canary-workflow标签):测试在一个临时 git 仓库中复制canary技能目录,然后以模拟提示驱动模型,要求其在没有 browse 守护进程、没有真实 URL的前提下演示对工作流的理解,即创建.gstack/canary-reports/目录结构、按 Phase 2 的 schema(url、timestamp、branch、pages 下的 screenshot / console_errors / load_time_ms)写出模拟baseline.json、按 Phase 6 的 Health Report 格式(CANARY REPORT 头、duration、pages、status、逐页结果表、verdict)写出模拟报告。测试断言目录存在且产物文件数大于 0。这意味着baseline.json与健康报告的字段结构是可被测试校验的契约,任何对 Phase 2 / Phase 6 输出格式的改动都需要同步更新测试,从而防止技能文档与真实行为脱节。
写在最后:金丝雀的设计哲学
回看整个/canary设计,核心并不在于"截图"或"看日志"这些单个动作,而在于三组取舍:
- 对比基线而非绝对标准:生产页面本来就可能有历史遗留的 console 错误或偏慢的加载,绝对阈值只会产生噪音;只有"相对基线的变化"才与"这次部署引入了什么"直接相关。
- 用截图锁定证据:告警消息不携带抽象描述,而是携带可复查的截图路径,任何告警都能被人工回溯确认。
- 瞬态与持续的区分:网络抖动在发布后 10 分钟窗口内几乎必然出现,只有跨越两次采样仍然存在的异常才值得打断用户。
如果你的部署流程目前只依赖 CI 绿灯,/canary提供了一种低成本补齐生产验证的手段:部署前一条--baseline建档,发布后一条/canary <url>盯防,即可把"发布后前 10 分钟"从无人区变成可观测、可告警、可回滚决策的受控窗口。技能完整定义见 canary/SKILL.md,browse 命令全量参考见 browse/sections/command-list.md,端到端契约测试见 test/skill-e2e-deploy.test.ts。
【免费下载链接】gstackUse Garry Tan's exact Claude Code setup: 23 opinionated tools that serve as CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA项目地址: https://gitcode.com/GitHub_Trending/gs/gstack
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考