gstack /ios-fix:基于真机状态快照的 iOS 自主 Bug 修复闭环工作流
【免费下载链接】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
gstack 中的/ios-fix技能把"发现 bug → 复现 bug → 定位根因 → 修复 → 真机验证 → 固化回归测试"串成一条零人工干预的自主修复流水线,并立下一条铁律:没有可复现的状态快照,就不许动任何一行 Swift 源码。读完本文,你将掌握该技能的五阶段修复协议、真机控制链(daemon + StateServer)的端点与权限分层、快照字段的默认拒绝机制,以及重建后409 schema_mismatch等典型故障的处理方式。
一、技能定位:/ios-qa 负责找 bug,/ios-fix 负责修 bug
/ios-fix定义在 ios-fix/SKILL.md 中,是一份 Claude Code 技能文件(skill 文件即"可执行指令",Agent 逐条照做而非仅作参考资料)。其 frontmatter 声明了技能的元信息:
name: ios-fix preamble-tier: 3 version: 1.0.0 description: Autonomous iOS bug fixer. (gstack) allowed-tools: - Bash - Read - Write - Edit - Grep - Glob - AskUserQuestion triggers: - fix this ios bug - patch the iphone app - auto-fix the ios issue几个关键字段的含义:
triggers:三种自然语言触发短语("fix this ios bug"、"patch the iphone app"、"auto-fix the ios issue"),用户说出这些话或/ios-qa报告了 bug 后想要自动修复时,技能会被调用;语音触发别名包括 "fix the iOS bug"、"patch the iPhone app" 等。allowed-tools:技能运行期间被允许使用的宿主工具集——Bash、Read、Write、Edit、Grep、Glob,外加用于向用户提问的 AskUserQuestion。preamble-tier: 3:该技能携带第三档"前导程序"(Preamble),即所有 gstack 技能共享的一段运行时引导逻辑(详见第五节)。
上游依赖是/ios-qa(ios-qa/SKILL.md):/ios-qa负责驱动真机做 QA 并产出 bug 发现(bug 描述、截图、疑似的 accessibility-tree 节点),/ios-fix负责消费这份发现并闭环修复。官方文档对两者的分工概括为:
"Iron Law: no fix without a reproducing snapshot. The agent captures pre-bug state via
GET /state/snapshot, writes the fix, rebuilds, redeploys, restores the snapshot, and verifies the bug is gone. The snapshot becomes a regression test fixture so the bug can't recur silently." —— docs/skills.md
二、铁律:没有复现快照,就不许修
技能正文开头(ios-fix/SKILL.md)立下整条流水线的锚:
NO FIX WITHOUT A REPRODUCING SNAPSHOT.在编辑任何 Swift 源码之前,Agent 必须先抓取一个能复现该 bug 的
GET /state/snapshot。该快照会沉淀为回归测试夹具(test/fixtures/ios-fix/)。 没有复现快照就落地的修复,等于三个月后还得再修一次。
这条铁律的技术支撑是 gstack iOS 能力里的"状态快照"机制:iOS 应用内嵌一个StateServer(DebugBridgeSPM 库,仅#if DEBUG编译,监听::1/127.0.0.19999 端口),其中被// @Snapshotable标记的字段可以被完整导出为 JSON,并可通过POST /state/restore全量恢复。快照因此既是复现手段(restore 到 bug 状态),也是验证手段(恢复后截图对比),还是回归夹具(提交进仓库)。
三、五阶段修复协议
Phase 1:复现 bug
- 读取
/ios-qa的 bug 发现(bug 描述、截图、疑似出错的 accessibility-tree 节点); - 通过
POST /tap、/swipe、/type或POST /state/<key>(仅限可快照字段)把设备带入 bug 状态; - 抓取
GET /state/snapshot→ 写入test/fixtures/ios-fix/<bug-slug>-pre.json; - 抓取
GET /screenshot→ 写入test/fixtures/ios-fix/<bug-slug>-pre.png; - 用一行文字固化"哪里错了 + 期望行为"。
注意第 2 步的分层约束:直接操作 UI(tap/swipe/type)走interact权限层,而写状态字段POST /state/<key>走更高的mutate层,恢复整个快照POST /state/restore则是最高层restore。权限分层在 daemon 源码 ios-qa/daemon/src/types.ts 中逐条映射:
export const TAILNET_ENDPOINT_TIERS: Record<string, Capability> = { 'GET /screenshot': 'observe', 'GET /state/snapshot': 'observe', 'POST /tap': 'interact', 'POST /swipe': 'interact', 'POST /type': 'interact', 'POST /state/*': 'mutate', // 通配:/state/ 下的写操作 'POST /state/restore': 'restore', // 最高层 };四个权限层是嵌套的:observe ⊂ interact ⊂ mutate ⊂ restore,原则是最小够用。这意味着复现阶段"把设备带到 bug 状态"这个动作本身就受到权限审计(见第五节审计日志),而不是随意的黑盒操作。
Phase 2:定位根因
沿用/investigate的铁律:没有根因就不修。Agent 读取 Swift 源码,从出错的屏幕反向追踪到 view model、数据流、状态变更点,并找出能修复行为的最小改动。
如果存在多个可信的根因假设,协议要求使用 AskUserQuestion 让用户选择要修哪一个——这是整条"零人工干预"流水线中唯一被明确允许的人工介入点。gstack 对 AskUserQuestion 有严格格式规范(D 决策简报、ELI10 白话解释、每选项完整性评分、(recommended)标签),其拆分链完整规则见 docs/askuserquestion-split.md,CJK 文本直接输出规范见 docs/askuserquestion-cjk.md。
Phase 3:应用修复
- 编辑 Swift 源码,diff 保持最小化;
- 重建:
xcodebuild -scheme <SchemeName> -destination 'platform=iOS,id=<UDID>' build install; - daemon 感知到重建,重新连接 StateServer 隧道;
- 重新部署——执行与首次启动相同的 boot-token 轮换流程(app 内的一次性启动 token 在 daemon 侧被消费后轮换为内存态凭证,约 5 秒内完成,防止日志抓取者拿到长期凭证)。
这里与 docs/howto-ios-testing-with-gstack.md 描述的部署流程一致:xcodebuild构建后通过xcrun devicectl device install app/device process launch --terminate-existing装机并拉起,daemon 通过 CoreDevice IPv6 ULA 隧道中转流量,iOS 侧 StateServer 始终只绑 loopback,身份校验全部发生在 Mac 侧。
Phase 4:验证
- 用修复前的快照调
POST /state/restore→ 在真机上精确重现 bug 前的状态; - 重新截图,与
test/fixtures/ios-fix/<bug-slug>-pre.png对比; - bug 仍可见 → 修复失败:回滚改动重试,最多 3 轮后升级给用户;
- bug 消失→ 抓一张
<bug-slug>-post.png存档,供回归测试引用。
"restore 后验证"正是快照机制的核心价值:修复效果不依赖人工把手机点回原状态,而是用同一份 JSON 确定性还原。
Phase 5:固化回归测试
在test/fixtures/ios-fix/<bug-slug>.test.ts写一个测试,要求三步:
- 加载修复前快照;
- 通过
POST /state/restore还原到 bug 状态; - 在真机上断言修复后的行为——该测试受环境变量
GSTACK_HAS_IOS_DEVICE=1门控,归属"periodic"(周期性)测试层,即只在挂了真机的环境里跑,CI 无设备时自动跳过。
快照夹具 + 测试文件与修复代码一同提交。至此闭环完成:下次任何人(包括未来的 Agent)跑回归测试,就能在真机上确定性复现"修复前状态",bug 无法"静默复发"。
四、快照机制的底层细节
理解五阶段协议,必须理解@Snapshotable快照的设计约束(完整说明见 docs/howto-ios-testing-with-gstack.md):
@Observable final class AppState { // @Snapshotable var username: String = "" var authToken: String = "" // never exported }- 默认拒绝(default-deny):只有字段上方带独立标记注释
// @Snapshotable的实例var才会被导出;token、PII、鉴权状态默认不会出现在快照里,避免敏感数据被写进提交到仓库的回归夹具; - 类型白名单:标记字段必须是 JSON 原生标量(
String、Bool、各宽度整数、Float、Double、CGFloat)、数组、String 键字典及其 Optional 组合;非法声明(自定义值、IUO、嵌套 observable、重复 key)会让生成器报错停止,而不是产出有损 Swift; - 两阶段恢复:
POST /state/restore先让每个模型校验完整输入,全部通过后才在 MainActor 上执行赋值,避免恢复一半的坏状态; - 构建防泄漏:
Package.swift用.when(configuration: .debug)从结构上禁止 Release 构建链接任何DebugBridge*target;正式发布前用/ios-clean(ios-clean/SKILL.md)移除依赖并剥掉#if DEBUG接线。
版本防错(409 schema_mismatch):快照信封中携带_accessor_hash——访问器代码的哈希。若某份快照是在旧版 app 构建上抓的,新构建恢复它会大声地以 409 schema_mismatch 拒绝,而不是静默写坏状态(见 CHANGELOG.md 中 iOS 能力的发布记录)。这正是"重建后快照失效"这一高频故障的防线,也是下一节故障表的来源。
五、技能运行时契约:共享前导与收尾
/ios-fix与其他 gstack 技能一样,执行前有一段共享 Preamble(由 SKILL.md.tmpl 中的{{PREAMBLE}}占位符在生成时注入,SKILL.md头部注明 "AUTO-GENERATED from SKILL.md.tmpl — do not edit directly",重新生成命令为bun run gen:skill-docs)。前导程序按序完成:
- 更新检查与会话登记:运行
gstack-update-check,在~/.gstack/sessions/落一个以$PPID命名的会话文件并清理 120 分钟前的陈旧文件; - 环境探测:读取
proactive、skill_prefix、telemetry、explain_level、question_tuning、update_check、checkpoint_mode/checkpoint_push等配置,检测当前分支、会话类型(spawned/headless/interactive,非法值回退interactive)、Conductor 宿主(此时决策以 prose 渲染而非调用工具)、plan-mode 状态(GSTACK_PLAN_MODE=active/inactive,默认 inactive 是安全回退); - 遥测与学习注入:telemetry 非 off 时向
~/.gstack/analytics/skill-usage.jsonl追加一条{"skill":"ios-fix",...}记录;加载本项目的learnings.jsonl(超过 5 条时自动检索 top 3 相关学习);向 timeline 记录started事件; - 一次性引导(各带标记文件,只问一次):首次运行按项目类型给一句话建议(
greenfield/code_ios/branch_ahead等 token 映射);"Boil the Ocean" 完整性原则介绍;telemetry 三档选择(community/anonymous/off);CLAUDE.md 技能路由规则注入;vendored gstack 迁移提醒;spawned 会话则全部静默、自动选择推荐项。
工作流收尾时的契约包括:
- Artifacts Sync 尾部:运行
gstack-brain-sync --discover-new与--once,把本地产物(计划、报告等)按配置同步到 GBrain/artifacts 仓库; - 持续检查点(
checkpoint_mode=continuous时):完成一个逻辑单元即以WIP:前缀自动提交,提交信息内嵌[gstack-context]块记录 Decisions/Remaining/Tried,/ship时再压平成干净提交; - 完成状态协议:以
DONE/DONE_WITH_CONCERNS/BLOCKED/NEEDS_CONTEXT之一收尾;三次尝试失败、安全敏感不确定变更时按STATUS / REASON / ATTEMPTED / RECOMMENDATION格式升级; - 经验沉淀:结束前强制回顾可持久学习的坑(项目怪癖、命令修正、可节省 5 分钟以上的模式),用
gstack-learnings-log记录;确无可记时显式声明 "No durable learnings this session"; - 遥测收尾:
PLAN MODE EXCEPTION — ALWAYS RUN,写本地 timelinecompleted事件与本地 analytics;远端遥测仅当用户 opt-in 且二进制存在时执行。
六、故障模式与处理动作
原文档(ios-fix/SKILL.md)给出的故障模式表完整保留如下:
| 症状 | 处理动作 |
|---|---|
| 3 轮迭代后 bug 仍在 | STOP,把当前最优假设报告给用户 |
重建后/state/restore返回409 schema_mismatch | 重新生成访问器(swift run gen-accessors),重新抓快照 |
| 修复中途设备断连 | daemon 自动重连;从 Phase 4 恢复执行 |
| 构建失败 | 回滚 Swift 改动;先排查编译错误再重新应用修复 |
补充两个来自/ios-qa故障表的相邻情况,/ios-fix流程同样会遇到:409 schema_mismatch的根因是"快照抓自旧版 app 构建",标准动作是丢弃旧快照重新抓取;413 body_too_large表示快照超过 1MB 上限,需要调大 daemon 的--max-body或裁剪快照字段(见 ios-qa/SKILL.md 故障表)。gen-accessors本身有 Swift 工具插件与 TS 回退双实现(ios-qa/scripts/gen-accessors.ts),缓存键为sha256(source || swift_version || tool_git_rev || platform_triple)——所以 Swift 版本变化、生成器自身 git rev 变化、源码变化都会使缓存失效,这解释了为什么"重建后快照 hash 不匹配"是跨版本升级时的预期行为而非异常。
七、运行前提与适用边界
要跑通/ios-fix全闭环,仓库文档给出的硬件/软件前提是(docs/howto-ios-testing-with-gstack.md):
- macOS + Xcode 16.0+(
xcrun devicectl --version可用,CoreDevice 隧道依赖 Xcode 16); - iOS 16+ 真机,已解锁、已配对、开启 Developer Mode;
- Apple 开发者团队(免费个人团队即可);
- gstack 已安装(
./setup完成,gstack-ios-qa-regen与gstack-ios-qa-daemon在 PATH 上),Bun 运行时在 PATH 上; - 回归测试仅在
GSTACK_HAS_IOS_DEVICE=1的环境执行,无设备环境只验证非真机部分; - iOS 17 及以下的设备存在 SwiftUI Button 点击不生效的平台限制(
_UIHitTestContext缺失),/tap会返回ok:true但手势不触发,需要 iOS 18+ 或改用 UIKit 控件。
从仓库结构看,该技能的模板与生成物一一对应(ios-fix/SKILL.md.tmpl →gen:skill-docs→ ios-fix/SKILL.md),修改工作流时应当改模板而非生成物;其五阶段协议、铁律与故障表是"可复制的操作规程",而 daemon 端点表、权限分层、快照哈希校验则给出了这套规程能在真机上确定性执行的底层保证。
【免费下载链接】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),仅供参考