feat: 采购失败保存现场控件树供管理员下载(诊断闭环) #364

Open
opened 2026-10-08 09:08:14 +08:00 by ila · 10 comments
Owner

2026-10-08 拆单(用户确认):原「A:核单等待调整」已拆至 #365 单独实施;本单只实施「B:采购失败现场诊断」(Android 截取与本机存储、上传队列、服务端存储与清理、管理端下载),B 不再按端拆分。下文 A 节保留为历史设计,以 #365 为准。

基本信息

  • 类型:功能增强(采购失败现场诊断完整闭环;核单等待调整已拆至 #365)。
  • 状态:代码已实现并合并 main,待真机验收,尚未线上发布。 2026-10-08 已获v1设计、本地数据库初始化/迁移/权限及三单合并授权;本次不包含线上迁移、重启发布、手机安装或真实下单。此前各阶段“只更新工单/未授权”描述仅作历史记录,以本节和末尾最新交付为准。
  • 本文整合评论 8953、8962 及 Codex 对 SynapBus #83/#84 的代码核验建议;正文为当前方案,评论保留历史,冲突表述以本正文为准。
  • 来源:2026-10-07~08 用户与 Claude Code 讨论 CG-1910 order_result_unknown。用户原话摘要:「延长待付款界面的等待时间。如果失败,要不要把最后现场的无障碍控件树数据保存到 sqlite 或提交给 admin,用来后续 debug 修复」;「内部系统,不需要门禁……要把原始控件树数据保存下载,才能完整 debug」。
  • 规则依据:AGENTS.md 已于 2026-10-08 合并 4ae33d8,新增「失败现场控件树」条款(用户 2026-10-07 确认):只在失败/结果待核对时保存;存本机 SQLite 和服务端;仅管理员下载;不进代码、普通日志、工单、Wiki、任务快照、SynapBus、采购员/Android 普通接口;密码框内容不存;整屏截图、付款、OCR/VLM 限制不变;两端自动清理,默认保留 30 天。

已核验事实与证据边界

  • 代码核验基线:origin/main 4ae33d8,2026-10-08。主要路径:PurchaseLiveAutomation.readOrderResult()、AgentForegroundService、AgentDiagnosticStore、GoAutoAccessibilityService 与 accessibility_service_config.xml。
  • 原工单现场调查记录:一次采购核单在观察到支付页面并执行一次返回后,约 15 秒以 PURCHASE_ORDER_CONTEXT_NOT_FOUND 结束。本轮沿用原调查记录,未重新查询线上数据。
  • 现有核单循环未见待付款标记时最多 60 次采样,间隔 200ms,另有截取和动作耗时;进入待付款分支后有独立的 30 次采样及最多 4 次滑动预算。不能仅凭总耗时约 15 秒断言从未进入待付款分支,实际路径须由诊断证据验证。
  • 尚不能区分返回后停在挽留弹窗/商品页、页面加载慢、页面文字未暴露给无障碍服务等原因;延长观察不代表已证明或修复根因。
  • 原调查记录中的历史待核对采购另行人工处理,不在本单补录或重试。
  • 现有本机 goauto_diagnostics.db / AgentDiagnosticStore schema v3 保存白名单结构化诊断,保留 7 天、最多 50 条;不是原始树存储,也未发现相应服务端树上传接口。
  • #361 的“安装后未见新增诊断记录”仍属疑点;可能是诊断回调未触发、参数校验或写入链路问题,不能直接认定 SQLite 失效。现有诊断并非覆盖全部核单失败路径。
  • XML 配置已启用 canRetrieveWindowContent、flagRetrieveInteractiveWindows 等;服务代码也设置 FLAG_RETRIEVE_INTERACTIVE_WINDOWS,本方案不新增相关权限或服务配置。

目标与非目标

目标:

  1. 尽可能完整保存采购失败或结果待核对时系统实际暴露的窗口与节点,供管理员下载分析。
  2. 覆盖采购任务 spec_probe 与 purchase 两阶段的真实失败,明确未截取原因及重启恢复现场边界;核单等待调整由 #365 独立实施。
  3. 诊断采集、存储或上传失败不改变采购结果,不阻塞结果提交与设备释放。

非目标:

  • 不付款,不保存整屏截图,不使用 OCR/VLM,不自动处理挽留弹窗。
  • 不新增点击目标、返回次数或滑动次数;不重复提交订单,不改服务端采购/核单状态机。
  • 不修改正常采集和采购共用的 capture() 及解析输入,不混入规格匹配修复。
  • 首版仅采购任务 spec_probe/purchase 两阶段的失败和结果待核对;普通采集任务失败不纳入,不保存连续多帧。仅由服务端判定且手机未观察到的失败(例如 AI 匹配无结果、离线租约超时)不要求手机快照,不能事后补抓当前页面冒充失败现场。
  • 不处理历史待核对任务,不声称快照能像素级还原页面或取得系统没有暴露的节点。

实施单元划分

按两个可独立验收的单元落单:

  • A:核单等待调整,只影响 Android 核单观察预算。
  • B:采购失败现场诊断完整功能,一次交付 Android 保存与上传、Server 存储与清理、Web 管理员下载。

拆单已完成:A 为 #365,B 为 #364;B 不按 Android / Server / Web 继续拆分。本单只验收 B,以下 A 的设计及验收条目保留历史,以 #365 为准,不计入本单实施范围。

A. 核单等待设计(历史,已拆至 #365)

  • 仅在实际识别支付页并成功执行既有的一次安全返回后,为尚未取得订单上下文的观察阶段设置最多约 30 秒预算;从该次返回成功起计时,使用单调时钟,不因重复采样重置。
  • 不能仅使用 paymentPageObserved 判定该起点:当前该标记也会在待付款页面出现时置真,不等价于已执行安全返回。
  • 无该返回事件的路径保持原预算;正常读到完整订单证据立即返回。
  • 已进入待付款页面后的独立预算和最多 4 次滑动不变;不扩大其他分支动作额度。新增等待部分只读。
  • 禁止简单把全局 ORDER_RESULT_MAX_SAMPLES 从 60 改为 120:现有循环含按采样序号触发的滑动,直接翻倍可能增加动作。
  • 持续支付页、持续空窗口、微信恢复失败、未授权应用等既有独立失败条件不变;30 秒不是保证所有失败都等待满 30 秒。
  • 测试采用可控时钟与动作计数,核验等待上限、证据出现立即退出、预算不重置及动作不增加。延长时间只能缓解加载慢,不能保证识别问题消失。

B. 失败现场诊断设计

B1. 时机、窗口与属性

  • 覆盖采购任务 spec_probe 与 purchase 两个阶段,manifest 必须记录真实 phase。每个有效 attempt UUID 最多一份快照,可包含多个窗口;针对 failed、order_result_unknown 以及本节约束内的执行阶段异常现场进行诊断,不捕获成功任务。
  • 正常执行出口以 PurchaseSpecProbePolicy.demote(rawOutcome) 之后的最终结果为准,避免漏掉“探测返回成功但空规格被降为失败”的情况。spec_probe_completed、order_created、rehearsal_completed 成功结果均不生成快照。
  • 在本次失败后的结果悬浮窗显示、页面跳转/切回 Agent、下一任务开始之前完成有界截取;随后持久化与上传解耦。上传重试只复用原快照,不能重新抓取后续页面。
  • 当前 dismissPurchaseResultBubble() 位于任务开始处,是清除上一次提示;不要求本次失败快照在该操作前截取,也不为诊断改变这一既有清理顺序。
  • 新增独立失败现场导出器,不修改正常 capture() 或解析输入。
  • 读取 getWindows() 返回的默认显示屏可交互窗口,记录窗口 ID、类型、层级、标题、活动/焦点、边界、截取时间及可取得的包名/Activity。
  • 模态窗口可能使底层窗口不可获取;root 为 null、窗口消失、读取失败、内容不可访问时显式记录,不静默遗漏或声称完整。无法抓取时记录诊断状态,但不改变任务结果。
  • 全程只读,不为补全数据点击、滑动、返回或关闭弹窗,不执行节点 action。
  • 采用有版本号的有限公开属性清单:path、class、text、contentDescription、resource ID、屏幕边界、visible、selected、checked、clickable、enabled、scrollable、editable、password、focusable/focused、long-clickable/checkable、hint、stateDescription、tooltip、action ID/label、inputType、maxTextLength、collection/range、子节点数量、drawingOrder 等;按 API 可用性读取。
  • 区分值为空、系统不支持、读取失败;不反射导出任意 extras/Parcelable。保留原始顺序和系统暴露的不可见节点,不去重、不归一化。
  • 容量、节点、耗时达到上限时输出合法的部分快照,标记 partial 及原因;具体节点/耗时上限在实现前用代表性样本确定,不能无限遍历。

B1a. 统一诊断入口与异常边界

  • 复用统一的诊断决策/保存入口,由执行结果出口、执行异常出口及恢复分支按其真实上下文调用;不按错误码逐个补丁,也不在 catch/finally 中无条件抓取。
  • 覆盖执行器明确失败(包括转换为结果的 PurchaseLiveException)、执行阶段超时/意外异常、结果待核对,以及有有效 attempt 的规则解析失败等前置失败。
  • 诊断判断须使用实际已领取/开始的任务及 attempt,而非可能尚为 pending 的 initial 对象。没有有效 attempt UUID 时,只记录本机最小未截取原因,不伪造 UUID、不复用旧 attempt,不上传错误归属的数据。
  • 前置失败未进入设备执行或无障碍未就绪时,允许仅保存“未进入设备执行/无障碍不可用”等未截取原因;不得把尚未执行任务时的 Agent 或其他页面冒充 PDD 失败现场。已取得有效任务上下文但窗口 root 为 null 时,沿用窗口缺失诊断。规则哈希无效等情况须如实标记,不为满足诊断字段校验伪造有效规则事实。
  • 区分执行失败与传输/后处理异常:executePurchaseTask() 当前 try/catch 同时包含领取、执行、本机结果入队、悬浮窗和上传。订单成功且结果已落本机库后,即使 flushPurchaseOutbox() 网络失败、显示提示失败,也不得新增采购失败快照或改掉成功结果。原执行已失败但上传失败时复用已存快照,不重复截取。
  • 对执行阶段异常中断,可在仍有本次有效上下文和现场时记录异常诊断;不为了统一诊断入口新增任务终态转换、结果上报、重试或订单动作。尤其跨过下单不可逆边界后,不得将待核对/中断改成普通可重试失败,保留已有本地标记、恢复和结果上报机制。
  • 进程被杀或崩溃到无法执行诊断时,不能保证现场抓取;不可把“所有失败覆盖”解释为事后必能补拍。诊断超时、SQLite/压缩/上传错误只影响诊断状态,不覆盖原采购错误码、成功结果或不可逆标记。
  • 防重复以持久化的 attempt UUID 归属和幂等约束为准,避免结果出口、异常处理和恢复分支重复保存;不能仅靠函数内布尔变量宣称跨重启去重完成。

B1b. 重启与恢复现场

  • recoverInterruptedPurchases() 处理的是重启后状态,不等于原失败时页面。已有快照直接复用/补传,不能覆盖;原现场没有保存时记录“进程中断,原现场未保存”。
  • 下单前中断被恢复为 AGENT_RESTARTED_DURING_EXECUTION 时,不抓重启后的页面充当旧任务原现场。
  • 对既有恢复核单本身产生的失败/待核对,若该 attempt 尚无快照且当前确有对应恢复执行上下文,可以保存一份明确标识为恢复核单现场的快照;记录当前截取时间、恢复来源与 phase,不能标成原执行时现场。仍每 attempt 最多一份。
  • 恢复过程只有网络/API错误、尚未开始核单,或缺少可靠任务归属时不补抓页面,仅记录未截取原因;恢复成功不新增快照。
  • 只观察现有恢复行为,不因保存诊断主动重新进入商品、滑动、点击、恢复下单或改变业务状态机。

B2. 密码保护与导出格式

  • password=true 节点除 text 外,contentDescription、hint、stateDescription、tooltip、action label 等可能承载内容的文字字段均不保存,保留结构和状态。
  • 普通订单/地址诊断信息仅按已确认的失败现场私有存储规则处理,不增加额外审批或复杂门禁。
  • 格式统一 ZIP,包含 manifest.json 及每窗口一个 window-<id>.xml。ZIP 已压缩,不再套一层 gzip。
  • manifest 包含任务/设备/attempt UUID、规则快照哈希及有效性、真实 phase、错误码、Agent 版本、真实截取时间、现场来源(当前执行或恢复核单)、窗口列表、属性清单版本、partial 状态与原因。未能截取时有独立诊断状态/原因,不伪造 XML 或把空文件算成成功快照。具体字段名在 API 契约设计中统一。
  • 每个 XML 只有一个 hierarchy 根,标准属性沿用 UIAutomator dump 命名,扩展属性使用合法名称或已声明命名空间。不能把多个根直接拼接成一个 XML。
  • 转义、换行、emoji 及压缩解压后需完整回读;XML 无法直接表示的字符须采用可回读编码并标明,不能静默丢弃。
  • 不仅测试标准解析器,还要验证现有 XML 分析脚本实际能读取各窗口文件。

B3. 本机存储与 #361 依赖

  • 复用现有诊断数据库,通过同一数据库版本升级管理新增独立快照表及持久化待上传状态;不把原始树塞进现有 agent_diagnostic 白名单结构化记录。
  • 原结构化记录继续原有 7 天/最多 50 条策略;新快照独立按默认 30 天和容量上限清理,不能误套旧记录上限。
  • 关联 taskId、deviceId、服务端 taskAttemptId(UUID)、规则快照哈希、阶段和错误码;尝试序号可用于显示,但不能代替 UUID 作为归属/幂等键。
  • 先验证 #361 的“回调触发 → 参数校验 → SQLite 写入 → 回读”最小链路,区分没有事件和写入失败。新导出器开发不必等待所有历史疑点查清;若证实共用数据库迁移/写入有故障,则先解决该真实阻塞再验收 B。
  • 覆盖旧 schema 升级与新装建库;诊断失败不覆盖原始采购错误。

B4. 上传、服务端与容量

  • 使用独立 Agent 上传接口 + 本地持久化待上传队列,不将 ZIP 附加到采购结果正文;结果上报优先,诊断网络失败不阻塞结果提交和设备释放。
  • Device Token 鉴权,服务端验证设备、任务与 attempt UUID 的实际归属;上传须支持原 attempt 已结束后的异步补传,不能要求任务仍在 running 才接受。
  • 以设备/任务/attempt UUID 关联固定快照,重复上传幂等;重试上传原文件,不重复抓取现场。队列在进程重启后可恢复。
  • 接口路径与响应、幂等约束在实施前补齐到共享 API 契约,以 attempt UUID 而非仅 attemptNumber 寻址。
  • 服务端独立表保存 ZIP 与元数据,原始内容不进入普通日志、任务历史通用接口、工单、Wiki 或消息。
  • 容量测试起点,非实测结论:单份解压后总量 8 MiB、ZIP 2 MiB、本机快照总量 64 MiB;需使用代表性真机数据确认并记录最终数值。双方同时校验压缩与解压大小。
  • 超限时保留可解析、带原因的 partial 快照,不截断 ZIP/XML 字节冒充有效文件;本机总量淘汰及过期清理同步处理待上传状态,避免无限重试。
  • 两端默认保留 30 天并自动清理;清理不能只依赖新增快照发生,需覆盖无新任务时的过期处理。

B5. Admin 下载与设计证据

  • 采购任务详情的 attempt 历史中,仅管理员可看到并使用「下载控件树」,下载 ZIP。
  • 无快照时不显示可用下载按钮;加载/下载失败有明确提示,partial 状态可识别;普通采购员无入口且服务端拒绝访问,不能只靠前端隐藏。
  • 小范围页面改动,实施前补一份标注截图或低保真设计,记录可审阅链接/版本及用户确认;不新增独立管理页面。
  • 本文是流程与数据设计,不冒充已通过的 UI 原型。

子项目影响、依赖与并行边界

  • 本单 B:Android 导出器、统一诊断入口、SQLite 升级、队列,Server 迁移/接口/清理,Web 下载入口;A 的核单等待及测试属于 #365,不在本单实施。
  • A 与 B 不互为算法依赖;B 的数据库最小链路验证为交付前置,不以旧诊断疑点笼统阻断全部设计工作。
  • 两单可分别验证,与不涉及同一核单/诊断代码的任务可并行;同一文件有重叠时串行集成,避免覆盖。
  • 本地数据库初始化、迁移及权限实施已获授权并验证;main合并已获授权。线上迁移、发布、安装和真机操作仍须另行授权。
  • Android 迁移与服务端 schema/API 先明确兼容契约;未具备服务端接收能力时不得将“手机已保存”称为端到端交付完成。

验收标准

A:等待调整(历史验收项,移交 #365,不计入本单)

  • 仅支付页安全返回成功后的无订单上下文阶段使用约 30 秒固定观察预算;不循环重置。
  • 订单证据出现即结束;未经历该返回的正常路径和既有独立失败条件不变。
  • 待付款额外预算及滑动次数不变,所有路径不新增点击、返回或滑动;合成测试核对动作计数。
  • 记录自动化验证和未覆盖真机项,不以延长等待宣称根因已修复。

B:完整诊断功能

  • spec_probe 与 purchase 两阶段失败/待核对均覆盖;manifest phase 正确。探测原结果为成功但经 demote 判空失败时也触发。
  • 执行器返回失败、明确执行异常、意外执行异常、执行超时、结果待核对分别验证;每个有可用现场的有效 attempt 最多一份,重复出口/重试/恢复不重复。
  • spec_probe_completed、order_created、rehearsal_completed 及普通采集均不生成快照。
  • 成功后上传失败不截取:订单成功且本机结果已入队后模拟网络异常,快照数仍为0,原成功结果及待上传结果保留;已失败任务上传异常时不重复抓取。
  • 重启不伪造原现场:模拟下单前任务中断并在不同页面恢复,记录原现场缺失、不保存当前页面充当旧现场;已有快照内容及截取时间保持不变。
  • 恢复核单失败且无旧快照时,若保存当前恢复现场则明确恢复来源、真实截取时间及phase;重复恢复最多一份。恢复成功、仅API失败且未开始核单时不新增快照。
  • 规则解析失败、无障碍不可用、无有效attempt、无效规则哈希、root为空等前置/诊断异常有真实未截取或缺失原因,不伪造任务关联、有效哈希或快照;服务端单独判定的失败明确不在手机现场覆盖范围。
  • 任务开始清除旧悬浮窗顺序不变;本次失败截取在新结果悬浮窗/切回Agent/下一任务前有界结束,超时后不继续抓取下一任务现场。
  • 注入诊断抓取、存储、压缩和上传失败,原错误码、成功结果和下单不可逆标记不变,不新增业务状态转换、采购重试或订单动作。
  • 双窗口、窗口消失/null root、系统版本缺字段、不可见节点、读取失败及超限 partial 均有测试。
  • 密码多字段无泄漏,普通日志及普通业务接口不返回树。
  • ZIP/XML 特殊字符与压缩解压回读正确,现有分析脚本读取成功。
  • #361 最小触发/写入/回读核验有证据,旧库升级、新装、新表与旧表保留策略隔离均验证。
  • taskId/deviceId/attempt UUID/规则哈希关联正确;重复上传幂等,越权拒绝,终态 attempt 可补传。
  • 断网/失败/重启后待上传任务可恢复;诊断故障不影响结果上报和设备释放。
  • 容量上下限及部分快照行为有验证,最终数值记录真机依据,不把测试起点当成实测。
  • 两端 30 天与本机容量清理有测试,无新任务时也能清理;清理后不无限重试。
  • 管理员可从对应 attempt 下载,采购员无权下载;轻量界面设计经确认。
  • 经另行授权完成一次真实失败现场“手机保存 → 上传 → Admin 下载 → 解析”验证;不得为了造现场执行付款,也不得未经授权创建订单。

风险及实施前待补齐项

  • 现场树可能包含个人信息,只存设备私有 SQLite 和服务端数据库,管理员下载,不进入普通日志或公共资料;密码内容排除。
  • 抓取需在现场尚未变化时完成,但必须有界,避免诊断本身卡住任务退出。
  • 30 秒只读等待不是已确认根因修复;现有独立超时可能更早结束,应在验收中明确。
  • 拆单、API/表结构、v1设计、实施授权和代码闭环已完成;限额仍是合成验证的开发保护值,代表性真机样本、实际回调/Android SQLite闭环及线上端到端验收仍待完成。

文档影响

早期设计整合仅更新工单;本次实施已更新长期Wiki,revision及镜像验证见交付记录。未导出任务快照。

实施后需更新长期 Wiki:Business-Rules-and-Glossary(两阶段失败现场存储例外及恢复边界;核单预算由 #365 维护)、Architecture-and-Code-Map(导出器/存储/队列)、Agent-API-Contract(上传与管理员下载、attempt UUID 与幂等边界)、Troubleshooting(下载、partial 含义及诊断排查)。准确区分普通接口禁返原始树与专用诊断接口;先改线上 Wiki 并读取 revision,再一轮 sync + sync --check。

方案整合记录

2026-10-08,Codex 按用户“把这些决策和 8953 整合进正文,发消息给 Claude”更新。本次解决原草案全部六项待建议问题;容量数字仍为明确标记的测试起点,工单拆分尚未实际执行。评论 8952/8953 保留审阅历史,以本正文为当前设计依据;不表示实施、迁移、合并、部署或验收完成。

2026-10-08 评论 8962 与失败出口审核整合

用户授权采纳并整合 Codex 对消息 #83/#84 的审核,补充“成功后上传失败不截取”“重启不伪造原现场”等回归验收。代码核验基线 7622795d97d4da3dd638619af99628b0352a42dd:AgentForegroundService.executePurchaseTask() 576–694、recoverInterruptedPurchases() 736–775、PurchaseOutboxUploader 和 PurchaseRehearsalExecutor。本次明确 demote 后的最终失败、统一诊断入口的条件、成功结果上传异常排除、无有效 attempt/无障碍的未截取原因、原现场与恢复现场区别及不可逆状态保护,并修正已拆单后残留的待拆分描述。历史 A 节仅作引用,不纳入本单实施。只更新方案与待执行测试清单,未编写/运行测试、未修改代码、未实施迁移或发布。

2026-10-08 v1 设计确认

  • 用户确认原话:“原型通过”。确认人:用户;确认日期:2026-10-08。
  • 确认版本与证据:轻量设计v1 / 评论8989,该评论保留审核时原稿,不原位改写。
  • 确认范围:采购详情执行记录末尾管理员专属“现场诊断”列;完整/部分ZIP下载、空/未截取、加载、失败、下载中及采购员无入口边界。不新增独立管理页面。
  • 此次仅确认设计,不作为Android SQLite升级、Server诊断表追加迁移、专用接口权限实施及本地迁移验证的专项授权;上述事项仍待用户明确确认。线上迁移、合并main、发布、装机、真机截取和订单操作均未授权。
  • 先核定共享API/表结构及幂等契约,再按获授权范围实施;容量等测试起点不冒充真机实测。未修改生产代码或执行迁移,不宣称功能交付。

2026-10-08 实施授权与工作计划

用户明确授权“#364 实施 Android 本地诊断库升级、服务端追加迁移、专用接口权限及本地迁移验证”。沿用评论8989已确认v1,仅本地实施/验证,不含线上迁移、合并main、发布、安装、真实现场抓取或下单。工作区 D:/OPC/goauto-worktrees/issue-364;分支 feat/364-purchase-failure-snapshot,基线最新origin/main 7622795;#365/#366不混入此分支,后续集成另行验证。

实施检查点(代码/合成验证完成,真机差距见交付记录)

  • 核定DeviceToken上传、管理员元数据/ZIP下载及attempt UUID幂等契约;先写入线上Agent-API-Contract为明确标注的目标契约,不冒充已实现。
  • Server:追加独立诊断表与迁移、严格归属/管理员校验、ZIP/XML和大小校验、30天清理;先失败测试,再最小实现。
  • Android:补齐#361无事件/校验/SQLite写入失败区分及共享SQL回读测试;实际无障碍→Android SQLite真机链路待验;独立有界导出器、密码多字段保护、合法partial、SQLite独立快照/队列/清理、统一失败及恢复入口;先测试再实现,不改正常capture/采购状态机。
  • Web:复用采购详情attempt表新增管理员现场诊断列,覆盖v1各状态;不泄漏正文到普通接口。
  • 运行跨端合成上传/下载/解析、越权/终态补传/幂等/恢复及故障隔离回归,本地MySQL追加迁移验证;代码规格和质量审核后修正阻断项。
  • 更新实际长期Wiki并在线回读,再统一sync/check,提交推送及工单证据回写;真机样本限额/现场闭环保留未验证,不把合成验证宣称现场达标。

具体容量测试起点保持正文,正式限额依据仍须代表性现场样本;本轮不因缺少真机授权伪造验证或扩大采集权限。

2026-10-08 实施交付及三单合并

代码与范围

  • 实现 4581ee5(35个任务相关文件);文档 0c24c38;main合并提交 e450ac28989de6827328b411e4bdc050dded9ab3,已推送远端。合并前origin/main 5f45932;隔离集成 8c4940d 保留 #365/#366 集成分支 d2083e7 全部提交,无生产代码冲突。#366仅②/hint保护,不包含仍缺证据的①。
  • Android独立有界导出器、不改正常capture或解析输入;实际attempt/phase/token-device身份绑定;成功结果后的后处理异常不截取,恢复现场明确标记且已有ZIP不覆盖。schema v4独立快照/队列/context,30天/64MiB逻辑容量,256KiB分块读BLOB;结果Outbox优先,独立线程上传冻结字节,不占设备互斥,旧响应按fingerprint隔离。
  • Server追加私有表迁移 1791400000000_purchase_failure_snapshot.go,LONGBLOB/LONGTEXT、attempt唯一;专用DeviceToken上传、严格管理员摘要与ZIP下载;所有权/终态/幂等/恢复升级、ZIP/XML及容量验证、日志正文排除和30天定期清理。Web复用已确认v1,在执行记录末尾加200px管理员专属列。
  • 密码节点及密码标识缺失/不可读时,所有内容文字和编码备份/action label均排除。1500ms/5000节点/2MiBZIP/8MiB展开/128窗口/深度保护为开发限额,不冒充代表性现场实测。

自动化及审核

  • TDD已记录迁移v4、SQL核心、BLOB分块、空ZIP拒绝等RED→GREEN。#364单独完整基线Debug/Release各524项通过,最后三行身份/上传忙机保护调整后31项定向+assemble通过。
  • 最终三单集成:gradlew.bat :app:test :app:assembleDebug 退出0;Debug/Release各564项,均0失败/错误/跳过;不是只复用单分支结果。最终APK SHA256 bf8ed021d2bfcc05d49eda54a7b40240a30eda2d1e93199c8dee408c052b1dbe,版本仍0.9.69/82;本轮未安装或发布APK。
  • 合并后Go六个受影响包 purchase/access/migrations/common/middleware/version-local/cmd/api 无缓存通过,Server构建通过。实际Android合成导出器产生的ZIP经 TestFailureSnapshotAndroidExporterContract 验证通过,含双窗口、不可见/重复节点、特殊字符/UTF16编码、密码字段排除及null root;未使用生产数据。
  • Web构建通过;最终真实Chrome+mock接口13/13回归通过(44.7s),覆盖管理员下载/partial/未截取/失败/旧响应、purchaser/after_sales/API key无入口,以及7项既有订单回填回归。新组件和API文件ESLint通过,无真实业务写操作。
  • Android/Server/Web分别经过规格及质量复核,阻断项已修正;最后三行及三单生产组合独立只读审核PASS。代码路径对比确认#364诊断/service与4581ee5一致,automation与d2083e7一致,未相互覆盖。

本地数据库(已单独授权)

  • 本机换机后 127.0.0.1:3308 MySQL8.4.3无goauto库,经用户授权创建全新库并完成70个注册baseline迁移,包含1791400000000;根目录已忽略config.yaml仅端口改为3308,不提交凭据。旧售后迁移所需初始密码仅当前进程临时生成,不记录或复用。
  • TestFailureSnapshotAuthorizedLocalMySQL 通过:#364追加迁移重复执行幂等,完整既有API目录/采购员权限对账,专用两接口无非管理员授权;64KiB manifest与2MiB BLOB合成数据回读及attempt唯一约束验证后事务回滚,采购任务仍0条。
  • 该验证有本地schema/权限写入,不是只读;未启动业务API/worker、未触发SYB/采购、未连接或修改线上数据库。

文档

  • Android-Agent-API-Contract:2102252a3f0ecb646b49941cc603423abb76ca2c
  • Architecture-and-Code-Map:6a6a1a9a13aaf5284e5a81a79f849ad28525ceba
  • Business-Rules-and-Glossary:b9efffa2045d01399bfe02ce626cfd1bb379ec41
  • Local-Development-and-Verification:4349298c740c0cb94d8c6bf33423c9c7965ff1c7
  • Deployment-and-Operations:6ca0b35cb1e8058b2930a9a01029822af8b571d2

在线全文回读成功后只运行一轮 harness.py sync + sync --check,17页一致;本次诊断排查说明收敛至Local-Development-and-Verification和Deployment-and-Operations,原Troubleshooting的#365/#366内容不重复改写。五份镜像已提交,不直接改写镜像。

已知限制及待验收

  • Go全量此前失败的12项SYB解析测试在相同生产基线7622795的干净源码重新执行也失败;origin/main到5f45932仅AGENTS文案变化,本单未改sybimport。完整套件不是全绿,不混入相邻修复。受影响六包及构建独立通过。
  • harness.py check --strict 仍因基线已有 docs/evidence/pdd-home-35727-summary.md 未登记失败;Wiki一致性检查通过,未把二者混同。Web既有CSS/chunk提示保留,可选Android lint扫描未完成,不记通过。
  • 首次集成构建因默认SDK误指D:/platform-tools失败;仅设置当前进程ANDROID_HOME/ANDROID_SDK_ROOT为已安装用户SDK后通过,不新增许可接受、不修改全局环境或产品代码。
  • 未进行真机、Binder实际耗时、Android框架SQLite升级与#361真实回调链、“失败现场→上传→管理员下载”的设备闭环;尚未据代表性现场确认限额。代码合并不等于这些验收完成,也不能保证所有历史失败的根因已修复。
  • 本轮只合并main,不执行线上迁移/发布/重启/安装/改地址/下单/付款;工单保持打开待验收。开发worktree及合成验证产物暂留,配套发布/现场验收后再按规则清理,不删除其中本地资料。

2026-10-08 已授权合并 main(#364/#365/#366)

  • 用户明确要求“把#364 #365 #366合并到main”;三单已合入并推送 e450ac28989de6827328b411e4bdc050dded9ab3,远端回读一致。合并前main为5f45932;#364实现4581ee5,原#365/#366集成d2083e7,三单生产集成8c4940d。保留源分支及全部提交,无生产代码冲突,没有压缩或丢弃历史。
  • 合并组合完整Android Debug/Release各56 suites、564项,0失败/错误/跳过,test+assembleDebug成功(10m9s)。正式main合并后的文件树与已验证组合完全一致,再次执行相同命令exit0/UP-TO-DATE;不将增量检查冒称第二次全量执行。
  • 合并后Server六个受影响包及构建通过,Web构建、13项浏览器mock回归通过;独立集成代码审核PASS。未改动的SYB解析12项测试在同一生产基线7622795也失败,strict存在旧未登记镜像问题,未宣称全仓检查全绿或扩范围修复。
  • 集成APK仍0.9.69/82,路径 D:/OPC/goauto-worktrees/issue-364/android/app/build/outputs/apk/debug/app-debug.apk,SHA256 bf8ed021d2bfcc05d49eda54a7b40240a30eda2d1e93199c8dee408c052b1dbe。未安装或发布;若通过Admin分发需按后续发布流程处理版本号,不把同versionCode产物宣称自动更新已启用。
  • #366仅合入已交付②编辑框就绪等待及hint保护,①仍缺真实地址列表证据,未实现;③④不改。#364现场真机端到端、容量/Binder实测与真实Android SQLite链路待验。
  • 本轮无线上迁移/权限写入/发布/重启,无本地业务服务重启,无真机改地址/下单/付款。临时Web测试服务已停止。
  • 三张工单保持打开,合并不等于验收。配套发布及真机验收尚未完成,worktree及其本地合成验证产物暂留,不删除资料;后续发布完成后按项目清理规则处理。
> **2026-10-08 拆单(用户确认)**:原「A:核单等待调整」已拆至 #365 单独实施;**本单只实施「B:采购失败现场诊断」**(Android 截取与本机存储、上传队列、服务端存储与清理、管理端下载),B 不再按端拆分。下文 A 节保留为历史设计,以 #365 为准。 ## 基本信息 - 类型:功能增强(采购失败现场诊断完整闭环;核单等待调整已拆至 #365)。 - 状态:**代码已实现并合并 main,待真机验收,尚未线上发布。** 2026-10-08 已获v1设计、本地数据库初始化/迁移/权限及三单合并授权;本次不包含线上迁移、重启发布、手机安装或真实下单。此前各阶段“只更新工单/未授权”描述仅作历史记录,以本节和末尾最新交付为准。 - 本文整合评论 8953、8962 及 Codex 对 SynapBus #83/#84 的代码核验建议;正文为当前方案,评论保留历史,冲突表述以本正文为准。 - 来源:2026-10-07~08 用户与 Claude Code 讨论 CG-1910 `order_result_unknown`。用户原话摘要:「延长待付款界面的等待时间。如果失败,要不要把最后现场的无障碍控件树数据保存到 sqlite 或提交给 admin,用来后续 debug 修复」;「内部系统,不需要门禁……要把原始控件树数据保存下载,才能完整 debug」。 - 规则依据:AGENTS.md 已于 2026-10-08 合并 `4ae33d8`,新增「失败现场控件树」条款(用户 2026-10-07 确认):只在失败/结果待核对时保存;存本机 SQLite 和服务端;仅管理员下载;不进代码、普通日志、工单、Wiki、任务快照、SynapBus、采购员/Android 普通接口;密码框内容不存;整屏截图、付款、OCR/VLM 限制不变;两端自动清理,默认保留 30 天。 ## 已核验事实与证据边界 - 代码核验基线:origin/main `4ae33d8`,2026-10-08。主要路径:`PurchaseLiveAutomation.readOrderResult()`、`AgentForegroundService`、`AgentDiagnosticStore`、`GoAutoAccessibilityService` 与 `accessibility_service_config.xml`。 - 原工单现场调查记录:一次采购核单在观察到支付页面并执行一次返回后,约 15 秒以 `PURCHASE_ORDER_CONTEXT_NOT_FOUND` 结束。本轮沿用原调查记录,未重新查询线上数据。 - 现有核单循环未见待付款标记时最多 60 次采样,间隔 200ms,另有截取和动作耗时;进入待付款分支后有独立的 30 次采样及最多 4 次滑动预算。**不能仅凭总耗时约 15 秒断言从未进入待付款分支**,实际路径须由诊断证据验证。 - 尚不能区分返回后停在挽留弹窗/商品页、页面加载慢、页面文字未暴露给无障碍服务等原因;延长观察不代表已证明或修复根因。 - 原调查记录中的历史待核对采购另行人工处理,不在本单补录或重试。 - 现有本机 `goauto_diagnostics.db` / `AgentDiagnosticStore` schema v3 保存白名单结构化诊断,保留 7 天、最多 50 条;不是原始树存储,也未发现相应服务端树上传接口。 - #361 的“安装后未见新增诊断记录”仍属疑点;可能是诊断回调未触发、参数校验或写入链路问题,不能直接认定 SQLite 失效。现有诊断并非覆盖全部核单失败路径。 - XML 配置已启用 `canRetrieveWindowContent`、`flagRetrieveInteractiveWindows` 等;服务代码也设置 `FLAG_RETRIEVE_INTERACTIVE_WINDOWS`,本方案不新增相关权限或服务配置。 ## 目标与非目标 目标: 1. 尽可能完整保存采购失败或结果待核对时系统实际暴露的窗口与节点,供管理员下载分析。 2. 覆盖采购任务 spec_probe 与 purchase 两阶段的真实失败,明确未截取原因及重启恢复现场边界;核单等待调整由 #365 独立实施。 3. 诊断采集、存储或上传失败不改变采购结果,不阻塞结果提交与设备释放。 非目标: - 不付款,不保存整屏截图,不使用 OCR/VLM,不自动处理挽留弹窗。 - 不新增点击目标、返回次数或滑动次数;不重复提交订单,不改服务端采购/核单状态机。 - 不修改正常采集和采购共用的 `capture()` 及解析输入,不混入规格匹配修复。 - 首版**仅采购任务 spec_probe/purchase 两阶段的失败和结果待核对**;普通采集任务失败不纳入,不保存连续多帧。仅由服务端判定且手机未观察到的失败(例如 AI 匹配无结果、离线租约超时)不要求手机快照,不能事后补抓当前页面冒充失败现场。 - 不处理历史待核对任务,不声称快照能像素级还原页面或取得系统没有暴露的节点。 ## 实施单元划分 按两个可独立验收的单元落单: - **A:核单等待调整**,只影响 Android 核单观察预算。 - **B:采购失败现场诊断完整功能**,一次交付 Android 保存与上传、Server 存储与清理、Web 管理员下载。 拆单已完成:A 为 #365,B 为 #364;B 不按 Android / Server / Web 继续拆分。本单只验收 B,以下 A 的设计及验收条目保留历史,以 #365 为准,不计入本单实施范围。 ## A. 核单等待设计(历史,已拆至 #365) - 仅在实际识别支付页并**成功执行既有的一次安全返回**后,为尚未取得订单上下文的观察阶段设置最多约 30 秒预算;从该次返回成功起计时,使用单调时钟,不因重复采样重置。 - 不能仅使用 `paymentPageObserved` 判定该起点:当前该标记也会在待付款页面出现时置真,不等价于已执行安全返回。 - 无该返回事件的路径保持原预算;正常读到完整订单证据立即返回。 - 已进入待付款页面后的独立预算和最多 4 次滑动不变;不扩大其他分支动作额度。新增等待部分只读。 - 禁止简单把全局 `ORDER_RESULT_MAX_SAMPLES` 从 60 改为 120:现有循环含按采样序号触发的滑动,直接翻倍可能增加动作。 - 持续支付页、持续空窗口、微信恢复失败、未授权应用等既有独立失败条件不变;30 秒不是保证所有失败都等待满 30 秒。 - 测试采用可控时钟与动作计数,核验等待上限、证据出现立即退出、预算不重置及动作不增加。延长时间只能缓解加载慢,不能保证识别问题消失。 ## B. 失败现场诊断设计 ### B1. 时机、窗口与属性 - 覆盖采购任务 `spec_probe` 与 `purchase` 两个阶段,manifest 必须记录真实 `phase`。每个有效 attempt UUID 最多一份快照,可包含多个窗口;针对 `failed`、`order_result_unknown` 以及本节约束内的执行阶段异常现场进行诊断,不捕获成功任务。 - 正常执行出口以 `PurchaseSpecProbePolicy.demote(rawOutcome)` 之后的最终结果为准,避免漏掉“探测返回成功但空规格被降为失败”的情况。`spec_probe_completed`、`order_created`、`rehearsal_completed` 成功结果均不生成快照。 - 在本次失败后的结果悬浮窗显示、页面跳转/切回 Agent、下一任务开始之前完成有界截取;随后持久化与上传解耦。上传重试只复用原快照,不能重新抓取后续页面。 - 当前 `dismissPurchaseResultBubble()` 位于任务开始处,是清除上一次提示;不要求本次失败快照在该操作前截取,也不为诊断改变这一既有清理顺序。 - 新增独立失败现场导出器,不修改正常 `capture()` 或解析输入。 - 读取 `getWindows()` 返回的默认显示屏可交互窗口,记录窗口 ID、类型、层级、标题、活动/焦点、边界、截取时间及可取得的包名/Activity。 - 模态窗口可能使底层窗口不可获取;root 为 null、窗口消失、读取失败、内容不可访问时显式记录,不静默遗漏或声称完整。无法抓取时记录诊断状态,但不改变任务结果。 - 全程只读,不为补全数据点击、滑动、返回或关闭弹窗,不执行节点 action。 - 采用有版本号的有限公开属性清单:path、class、text、contentDescription、resource ID、屏幕边界、visible、selected、checked、clickable、enabled、scrollable、editable、password、focusable/focused、long-clickable/checkable、hint、stateDescription、tooltip、action ID/label、inputType、maxTextLength、collection/range、子节点数量、drawingOrder 等;按 API 可用性读取。 - 区分值为空、系统不支持、读取失败;不反射导出任意 extras/Parcelable。保留原始顺序和系统暴露的不可见节点,不去重、不归一化。 - 容量、节点、耗时达到上限时输出合法的部分快照,标记 `partial` 及原因;具体节点/耗时上限在实现前用代表性样本确定,不能无限遍历。 ### B1a. 统一诊断入口与异常边界 - 复用统一的诊断决策/保存入口,由执行结果出口、执行异常出口及恢复分支按其真实上下文调用;不按错误码逐个补丁,也不在 `catch/finally` 中无条件抓取。 - 覆盖执行器明确失败(包括转换为结果的 `PurchaseLiveException`)、执行阶段超时/意外异常、结果待核对,以及有有效 attempt 的规则解析失败等前置失败。 - 诊断判断须使用实际已领取/开始的任务及 attempt,而非可能尚为 pending 的 initial 对象。没有有效 attempt UUID 时,只记录本机最小未截取原因,不伪造 UUID、不复用旧 attempt,不上传错误归属的数据。 - 前置失败未进入设备执行或无障碍未就绪时,允许仅保存“未进入设备执行/无障碍不可用”等未截取原因;不得把尚未执行任务时的 Agent 或其他页面冒充 PDD 失败现场。已取得有效任务上下文但窗口 root 为 null 时,沿用窗口缺失诊断。规则哈希无效等情况须如实标记,不为满足诊断字段校验伪造有效规则事实。 - 区分执行失败与传输/后处理异常:`executePurchaseTask()` 当前 try/catch 同时包含领取、执行、本机结果入队、悬浮窗和上传。订单成功且结果已落本机库后,即使 `flushPurchaseOutbox()` 网络失败、显示提示失败,也**不得新增采购失败快照或改掉成功结果**。原执行已失败但上传失败时复用已存快照,不重复截取。 - 对执行阶段异常中断,可在仍有本次有效上下文和现场时记录异常诊断;不为了统一诊断入口新增任务终态转换、结果上报、重试或订单动作。尤其跨过下单不可逆边界后,不得将待核对/中断改成普通可重试失败,保留已有本地标记、恢复和结果上报机制。 - 进程被杀或崩溃到无法执行诊断时,不能保证现场抓取;不可把“所有失败覆盖”解释为事后必能补拍。诊断超时、SQLite/压缩/上传错误只影响诊断状态,不覆盖原采购错误码、成功结果或不可逆标记。 - 防重复以持久化的 attempt UUID 归属和幂等约束为准,避免结果出口、异常处理和恢复分支重复保存;不能仅靠函数内布尔变量宣称跨重启去重完成。 ### B1b. 重启与恢复现场 - `recoverInterruptedPurchases()` 处理的是重启后状态,不等于原失败时页面。已有快照直接复用/补传,不能覆盖;原现场没有保存时记录“进程中断,原现场未保存”。 - 下单前中断被恢复为 `AGENT_RESTARTED_DURING_EXECUTION` 时,不抓重启后的页面充当旧任务原现场。 - 对既有恢复核单本身产生的失败/待核对,若该 attempt 尚无快照且当前确有对应恢复执行上下文,可以保存一份**明确标识为恢复核单现场**的快照;记录当前截取时间、恢复来源与 phase,不能标成原执行时现场。仍每 attempt 最多一份。 - 恢复过程只有网络/API错误、尚未开始核单,或缺少可靠任务归属时不补抓页面,仅记录未截取原因;恢复成功不新增快照。 - 只观察现有恢复行为,不因保存诊断主动重新进入商品、滑动、点击、恢复下单或改变业务状态机。 ### B2. 密码保护与导出格式 - `password=true` 节点除 text 外,contentDescription、hint、stateDescription、tooltip、action label 等可能承载内容的文字字段均不保存,保留结构和状态。 - 普通订单/地址诊断信息仅按已确认的失败现场私有存储规则处理,不增加额外审批或复杂门禁。 - 格式统一 **ZIP**,包含 `manifest.json` 及每窗口一个 `window-<id>.xml`。ZIP 已压缩,不再套一层 gzip。 - manifest 包含任务/设备/attempt UUID、规则快照哈希及有效性、真实 phase、错误码、Agent 版本、真实截取时间、现场来源(当前执行或恢复核单)、窗口列表、属性清单版本、partial 状态与原因。未能截取时有独立诊断状态/原因,不伪造 XML 或把空文件算成成功快照。具体字段名在 API 契约设计中统一。 - 每个 XML 只有一个 `hierarchy` 根,标准属性沿用 UIAutomator dump 命名,扩展属性使用合法名称或已声明命名空间。不能把多个根直接拼接成一个 XML。 - 转义、换行、emoji 及压缩解压后需完整回读;XML 无法直接表示的字符须采用可回读编码并标明,不能静默丢弃。 - 不仅测试标准解析器,还要验证现有 XML 分析脚本实际能读取各窗口文件。 ### B3. 本机存储与 #361 依赖 - 复用现有诊断数据库,通过同一数据库版本升级管理新增独立快照表及持久化待上传状态;不把原始树塞进现有 `agent_diagnostic` 白名单结构化记录。 - 原结构化记录继续原有 7 天/最多 50 条策略;新快照独立按默认 30 天和容量上限清理,不能误套旧记录上限。 - 关联 `taskId`、`deviceId`、服务端 `taskAttemptId`(UUID)、规则快照哈希、阶段和错误码;尝试序号可用于显示,但不能代替 UUID 作为归属/幂等键。 - 先验证 #361 的“回调触发 → 参数校验 → SQLite 写入 → 回读”最小链路,区分没有事件和写入失败。新导出器开发不必等待所有历史疑点查清;若证实共用数据库迁移/写入有故障,则先解决该真实阻塞再验收 B。 - 覆盖旧 schema 升级与新装建库;诊断失败不覆盖原始采购错误。 ### B4. 上传、服务端与容量 - 使用**独立 Agent 上传接口 + 本地持久化待上传队列**,不将 ZIP 附加到采购结果正文;结果上报优先,诊断网络失败不阻塞结果提交和设备释放。 - Device Token 鉴权,服务端验证设备、任务与 attempt UUID 的实际归属;上传须支持原 attempt 已结束后的异步补传,不能要求任务仍在 running 才接受。 - 以设备/任务/attempt UUID 关联固定快照,重复上传幂等;重试上传原文件,不重复抓取现场。队列在进程重启后可恢复。 - 接口路径与响应、幂等约束在实施前补齐到共享 API 契约,以 attempt UUID 而非仅 attemptNumber 寻址。 - 服务端独立表保存 ZIP 与元数据,原始内容不进入普通日志、任务历史通用接口、工单、Wiki 或消息。 - **容量测试起点,非实测结论**:单份解压后总量 8 MiB、ZIP 2 MiB、本机快照总量 64 MiB;需使用代表性真机数据确认并记录最终数值。双方同时校验压缩与解压大小。 - 超限时保留可解析、带原因的 partial 快照,不截断 ZIP/XML 字节冒充有效文件;本机总量淘汰及过期清理同步处理待上传状态,避免无限重试。 - 两端默认保留 30 天并自动清理;清理不能只依赖新增快照发生,需覆盖无新任务时的过期处理。 ### B5. Admin 下载与设计证据 - 采购任务详情的 attempt 历史中,仅管理员可看到并使用「下载控件树」,下载 ZIP。 - 无快照时不显示可用下载按钮;加载/下载失败有明确提示,partial 状态可识别;普通采购员无入口且服务端拒绝访问,不能只靠前端隐藏。 - 小范围页面改动,实施前补一份标注截图或低保真设计,记录可审阅链接/版本及用户确认;不新增独立管理页面。 - 本文是流程与数据设计,不冒充已通过的 UI 原型。 ## 子项目影响、依赖与并行边界 - 本单 B:Android 导出器、统一诊断入口、SQLite 升级、队列,Server 迁移/接口/清理,Web 下载入口;A 的核单等待及测试属于 #365,不在本单实施。 - A 与 B 不互为算法依赖;B 的数据库最小链路验证为交付前置,不以旧诊断疑点笼统阻断全部设计工作。 - 两单可分别验证,与不涉及同一核单/诊断代码的任务可并行;同一文件有重叠时串行集成,避免覆盖。 - 本地数据库初始化、迁移及权限实施已获授权并验证;main合并已获授权。线上迁移、发布、安装和真机操作仍须另行授权。 - Android 迁移与服务端 schema/API 先明确兼容契约;未具备服务端接收能力时不得将“手机已保存”称为端到端交付完成。 ## 验收标准 ### A:等待调整(历史验收项,移交 #365,不计入本单) - [ ] 仅支付页安全返回成功后的无订单上下文阶段使用约 30 秒固定观察预算;不循环重置。 - [ ] 订单证据出现即结束;未经历该返回的正常路径和既有独立失败条件不变。 - [ ] 待付款额外预算及滑动次数不变,所有路径不新增点击、返回或滑动;合成测试核对动作计数。 - [ ] 记录自动化验证和未覆盖真机项,不以延长等待宣称根因已修复。 ### B:完整诊断功能 - [ ] spec_probe 与 purchase 两阶段失败/待核对均覆盖;manifest phase 正确。探测原结果为成功但经 demote 判空失败时也触发。 - [ ] 执行器返回失败、明确执行异常、意外执行异常、执行超时、结果待核对分别验证;每个有可用现场的有效 attempt 最多一份,重复出口/重试/恢复不重复。 - [ ] spec_probe_completed、order_created、rehearsal_completed 及普通采集均不生成快照。 - [ ] **成功后上传失败不截取**:订单成功且本机结果已入队后模拟网络异常,快照数仍为0,原成功结果及待上传结果保留;已失败任务上传异常时不重复抓取。 - [ ] **重启不伪造原现场**:模拟下单前任务中断并在不同页面恢复,记录原现场缺失、不保存当前页面充当旧现场;已有快照内容及截取时间保持不变。 - [ ] 恢复核单失败且无旧快照时,若保存当前恢复现场则明确恢复来源、真实截取时间及phase;重复恢复最多一份。恢复成功、仅API失败且未开始核单时不新增快照。 - [ ] 规则解析失败、无障碍不可用、无有效attempt、无效规则哈希、root为空等前置/诊断异常有真实未截取或缺失原因,不伪造任务关联、有效哈希或快照;服务端单独判定的失败明确不在手机现场覆盖范围。 - [ ] 任务开始清除旧悬浮窗顺序不变;本次失败截取在新结果悬浮窗/切回Agent/下一任务前有界结束,超时后不继续抓取下一任务现场。 - [ ] 注入诊断抓取、存储、压缩和上传失败,原错误码、成功结果和下单不可逆标记不变,不新增业务状态转换、采购重试或订单动作。 - [ ] 双窗口、窗口消失/null root、系统版本缺字段、不可见节点、读取失败及超限 partial 均有测试。 - [ ] 密码多字段无泄漏,普通日志及普通业务接口不返回树。 - [ ] ZIP/XML 特殊字符与压缩解压回读正确,现有分析脚本读取成功。 - [ ] #361 最小触发/写入/回读核验有证据,旧库升级、新装、新表与旧表保留策略隔离均验证。 - [ ] taskId/deviceId/attempt UUID/规则哈希关联正确;重复上传幂等,越权拒绝,终态 attempt 可补传。 - [ ] 断网/失败/重启后待上传任务可恢复;诊断故障不影响结果上报和设备释放。 - [ ] 容量上下限及部分快照行为有验证,最终数值记录真机依据,不把测试起点当成实测。 - [ ] 两端 30 天与本机容量清理有测试,无新任务时也能清理;清理后不无限重试。 - [ ] 管理员可从对应 attempt 下载,采购员无权下载;轻量界面设计经确认。 - [ ] 经另行授权完成一次真实失败现场“手机保存 → 上传 → Admin 下载 → 解析”验证;不得为了造现场执行付款,也不得未经授权创建订单。 ## 风险及实施前待补齐项 - 现场树可能包含个人信息,只存设备私有 SQLite 和服务端数据库,管理员下载,不进入普通日志或公共资料;密码内容排除。 - 抓取需在现场尚未变化时完成,但必须有界,避免诊断本身卡住任务退出。 - 30 秒只读等待不是已确认根因修复;现有独立超时可能更早结束,应在验收中明确。 - 拆单、API/表结构、v1设计、实施授权和代码闭环已完成;限额仍是合成验证的开发保护值,代表性真机样本、实际回调/Android SQLite闭环及线上端到端验收仍待完成。 ## 文档影响 早期设计整合仅更新工单;本次实施已更新长期Wiki,revision及镜像验证见交付记录。未导出任务快照。 实施后需更新长期 Wiki:Business-Rules-and-Glossary(两阶段失败现场存储例外及恢复边界;核单预算由 #365 维护)、Architecture-and-Code-Map(导出器/存储/队列)、Agent-API-Contract(上传与管理员下载、attempt UUID 与幂等边界)、Troubleshooting(下载、partial 含义及诊断排查)。准确区分普通接口禁返原始树与专用诊断接口;先改线上 Wiki 并读取 revision,再一轮 sync + sync --check。 ## 方案整合记录 2026-10-08,Codex 按用户“把这些决策和 8953 整合进正文,发消息给 Claude”更新。本次解决原草案全部六项待建议问题;容量数字仍为明确标记的测试起点,工单拆分尚未实际执行。评论 8952/8953 保留审阅历史,以本正文为当前设计依据;不表示实施、迁移、合并、部署或验收完成。 ### 2026-10-08 评论 8962 与失败出口审核整合 用户授权采纳并整合 Codex 对消息 #83/#84 的审核,补充“成功后上传失败不截取”“重启不伪造原现场”等回归验收。代码核验基线 `7622795d97d4da3dd638619af99628b0352a42dd`:`AgentForegroundService.executePurchaseTask()` 576–694、`recoverInterruptedPurchases()` 736–775、`PurchaseOutboxUploader` 和 `PurchaseRehearsalExecutor`。本次明确 demote 后的最终失败、统一诊断入口的条件、成功结果上传异常排除、无有效 attempt/无障碍的未截取原因、原现场与恢复现场区别及不可逆状态保护,并修正已拆单后残留的待拆分描述。历史 A 节仅作引用,不纳入本单实施。只更新方案与待执行测试清单,未编写/运行测试、未修改代码、未实施迁移或发布。 ## 2026-10-08 v1 设计确认 - 用户确认原话:“原型通过”。确认人:用户;确认日期:2026-10-08。 - 确认版本与证据:[轻量设计v1 / 评论8989](https://git.ilapage.cn/OPC/goauto/issues/364#issuecomment-8989),该评论保留审核时原稿,不原位改写。 - 确认范围:采购详情执行记录末尾管理员专属“现场诊断”列;完整/部分ZIP下载、空/未截取、加载、失败、下载中及采购员无入口边界。不新增独立管理页面。 - 此次仅确认设计,不作为Android SQLite升级、Server诊断表追加迁移、专用接口权限实施及本地迁移验证的专项授权;上述事项仍待用户明确确认。线上迁移、合并main、发布、装机、真机截取和订单操作均未授权。 - 先核定共享API/表结构及幂等契约,再按获授权范围实施;容量等测试起点不冒充真机实测。未修改生产代码或执行迁移,不宣称功能交付。 ## 2026-10-08 实施授权与工作计划 用户明确授权“#364 实施 Android 本地诊断库升级、服务端追加迁移、专用接口权限及本地迁移验证”。沿用评论8989已确认v1,仅本地实施/验证,不含线上迁移、合并main、发布、安装、真实现场抓取或下单。工作区 `D:/OPC/goauto-worktrees/issue-364`;分支 `feat/364-purchase-failure-snapshot`,基线最新origin/main `7622795`;#365/#366不混入此分支,后续集成另行验证。 ### 实施检查点(代码/合成验证完成,真机差距见交付记录) - [x] 核定DeviceToken上传、管理员元数据/ZIP下载及attempt UUID幂等契约;先写入线上Agent-API-Contract为明确标注的目标契约,不冒充已实现。 - [x] Server:追加独立诊断表与迁移、严格归属/管理员校验、ZIP/XML和大小校验、30天清理;先失败测试,再最小实现。 - [x] Android:补齐#361无事件/校验/SQLite写入失败区分及共享SQL回读测试;实际无障碍→Android SQLite真机链路待验;独立有界导出器、密码多字段保护、合法partial、SQLite独立快照/队列/清理、统一失败及恢复入口;先测试再实现,不改正常capture/采购状态机。 - [x] Web:复用采购详情attempt表新增管理员现场诊断列,覆盖v1各状态;不泄漏正文到普通接口。 - [x] 运行跨端合成上传/下载/解析、越权/终态补传/幂等/恢复及故障隔离回归,本地MySQL追加迁移验证;代码规格和质量审核后修正阻断项。 - [x] 更新实际长期Wiki并在线回读,再统一sync/check,提交推送及工单证据回写;真机样本限额/现场闭环保留未验证,不把合成验证宣称现场达标。 具体容量测试起点保持正文,正式限额依据仍须代表性现场样本;本轮不因缺少真机授权伪造验证或扩大采集权限。 ## 2026-10-08 实施交付及三单合并 ### 代码与范围 - 实现 `4581ee5`(35个任务相关文件);文档 `0c24c38`;main合并提交 `e450ac28989de6827328b411e4bdc050dded9ab3`,已推送远端。合并前origin/main `5f45932`;隔离集成 `8c4940d` 保留 #365/#366 集成分支 `d2083e7` 全部提交,无生产代码冲突。#366仅②/hint保护,不包含仍缺证据的①。 - Android独立有界导出器、不改正常capture或解析输入;实际attempt/phase/token-device身份绑定;成功结果后的后处理异常不截取,恢复现场明确标记且已有ZIP不覆盖。schema v4独立快照/队列/context,30天/64MiB逻辑容量,256KiB分块读BLOB;结果Outbox优先,独立线程上传冻结字节,不占设备互斥,旧响应按fingerprint隔离。 - Server追加私有表迁移 `1791400000000_purchase_failure_snapshot.go`,LONGBLOB/LONGTEXT、attempt唯一;专用DeviceToken上传、严格管理员摘要与ZIP下载;所有权/终态/幂等/恢复升级、ZIP/XML及容量验证、日志正文排除和30天定期清理。Web复用已确认v1,在执行记录末尾加200px管理员专属列。 - 密码节点及密码标识缺失/不可读时,所有内容文字和编码备份/action label均排除。1500ms/5000节点/2MiBZIP/8MiB展开/128窗口/深度保护为开发限额,不冒充代表性现场实测。 ### 自动化及审核 - TDD已记录迁移v4、SQL核心、BLOB分块、空ZIP拒绝等RED→GREEN。#364单独完整基线Debug/Release各524项通过,最后三行身份/上传忙机保护调整后31项定向+assemble通过。 - **最终三单集成**:`gradlew.bat :app:test :app:assembleDebug` 退出0;Debug/Release各564项,均0失败/错误/跳过;不是只复用单分支结果。最终APK SHA256 `bf8ed021d2bfcc05d49eda54a7b40240a30eda2d1e93199c8dee408c052b1dbe`,版本仍0.9.69/82;本轮未安装或发布APK。 - 合并后Go六个受影响包 `purchase/access/migrations/common/middleware/version-local/cmd/api` 无缓存通过,Server构建通过。实际Android合成导出器产生的ZIP经 `TestFailureSnapshotAndroidExporterContract` 验证通过,含双窗口、不可见/重复节点、特殊字符/UTF16编码、密码字段排除及null root;未使用生产数据。 - Web构建通过;最终真实Chrome+mock接口13/13回归通过(44.7s),覆盖管理员下载/partial/未截取/失败/旧响应、purchaser/after_sales/API key无入口,以及7项既有订单回填回归。新组件和API文件ESLint通过,无真实业务写操作。 - Android/Server/Web分别经过规格及质量复核,阻断项已修正;最后三行及三单生产组合独立只读审核PASS。代码路径对比确认#364诊断/service与4581ee5一致,automation与d2083e7一致,未相互覆盖。 ### 本地数据库(已单独授权) - 本机换机后 `127.0.0.1:3308` MySQL8.4.3无goauto库,经用户授权创建全新库并完成70个注册baseline迁移,包含1791400000000;根目录已忽略config.yaml仅端口改为3308,不提交凭据。旧售后迁移所需初始密码仅当前进程临时生成,不记录或复用。 - `TestFailureSnapshotAuthorizedLocalMySQL` 通过:#364追加迁移重复执行幂等,完整既有API目录/采购员权限对账,专用两接口无非管理员授权;64KiB manifest与2MiB BLOB合成数据回读及attempt唯一约束验证后事务回滚,采购任务仍0条。 - 该验证有本地schema/权限写入,不是只读;未启动业务API/worker、未触发SYB/采购、未连接或修改线上数据库。 ### 文档 - Android-Agent-API-Contract:`2102252a3f0ecb646b49941cc603423abb76ca2c` - Architecture-and-Code-Map:`6a6a1a9a13aaf5284e5a81a79f849ad28525ceba` - Business-Rules-and-Glossary:`b9efffa2045d01399bfe02ce626cfd1bb379ec41` - Local-Development-and-Verification:`4349298c740c0cb94d8c6bf33423c9c7965ff1c7` - Deployment-and-Operations:`6ca0b35cb1e8058b2930a9a01029822af8b571d2` 在线全文回读成功后只运行一轮 `harness.py sync` + `sync --check`,17页一致;本次诊断排查说明收敛至Local-Development-and-Verification和Deployment-and-Operations,原Troubleshooting的#365/#366内容不重复改写。五份镜像已提交,不直接改写镜像。 ### 已知限制及待验收 - Go全量此前失败的12项SYB解析测试在**相同生产基线7622795**的干净源码重新执行也失败;origin/main到5f45932仅AGENTS文案变化,本单未改sybimport。完整套件不是全绿,不混入相邻修复。受影响六包及构建独立通过。 - `harness.py check --strict` 仍因基线已有 `docs/evidence/pdd-home-35727-summary.md` 未登记失败;Wiki一致性检查通过,未把二者混同。Web既有CSS/chunk提示保留,可选Android lint扫描未完成,不记通过。 - 首次集成构建因默认SDK误指D:/platform-tools失败;仅设置当前进程ANDROID_HOME/ANDROID_SDK_ROOT为已安装用户SDK后通过,不新增许可接受、不修改全局环境或产品代码。 - 未进行真机、Binder实际耗时、Android框架SQLite升级与#361真实回调链、“失败现场→上传→管理员下载”的设备闭环;尚未据代表性现场确认限额。代码合并不等于这些验收完成,也不能保证所有历史失败的根因已修复。 - 本轮只合并main,不执行线上迁移/发布/重启/安装/改地址/下单/付款;工单保持打开待验收。开发worktree及合成验证产物暂留,配套发布/现场验收后再按规则清理,不删除其中本地资料。 ## 2026-10-08 已授权合并 main(#364/#365/#366) - 用户明确要求“把#364 #365 #366合并到main”;三单已合入并推送 `e450ac28989de6827328b411e4bdc050dded9ab3`,远端回读一致。合并前main为5f45932;#364实现4581ee5,原#365/#366集成d2083e7,三单生产集成8c4940d。保留源分支及全部提交,无生产代码冲突,没有压缩或丢弃历史。 - 合并组合完整Android Debug/Release各56 suites、564项,0失败/错误/跳过,test+assembleDebug成功(10m9s)。正式main合并后的文件树与已验证组合完全一致,再次执行相同命令exit0/UP-TO-DATE;不将增量检查冒称第二次全量执行。 - 合并后Server六个受影响包及构建通过,Web构建、13项浏览器mock回归通过;独立集成代码审核PASS。未改动的SYB解析12项测试在同一生产基线7622795也失败,strict存在旧未登记镜像问题,未宣称全仓检查全绿或扩范围修复。 - 集成APK仍0.9.69/82,路径 `D:/OPC/goauto-worktrees/issue-364/android/app/build/outputs/apk/debug/app-debug.apk`,SHA256 `bf8ed021d2bfcc05d49eda54a7b40240a30eda2d1e93199c8dee408c052b1dbe`。未安装或发布;若通过Admin分发需按后续发布流程处理版本号,不把同versionCode产物宣称自动更新已启用。 - **#366仅合入已交付②编辑框就绪等待及hint保护,①仍缺真实地址列表证据,未实现;③④不改。#364现场真机端到端、容量/Binder实测与真实Android SQLite链路待验。** - 本轮无线上迁移/权限写入/发布/重启,无本地业务服务重启,无真机改地址/下单/付款。临时Web测试服务已停止。 - 三张工单保持打开,合并不等于验收。配套发布及真机验收尚未完成,worktree及其本地合成验证产物暂留,不删除资料;后续发布完成后按项目清理规则处理。
Author
Owner

方案补充:控件树必须是完整原始现场(2026-10-08,Claude Code,用户确认补充)

用户要求失败时保存的应是原始控件树,能完整还原现场。原草案 B.1/B.2 有两处会导致数据不完整,补充如下,取代原草案中对应表述:

  1. 截取所有窗口,而不只是 rootInActiveWindow。
    • 使用 getWindows() 截取屏幕上的全部窗口,记录每个窗口的类型、层级(layer)、标题、是否活动、是否获得焦点及窗口边界,再分别保存各窗口的完整节点树。
    • 原因:PDD 挽留弹窗、系统对话框、输入法、悬浮层等可能是独立窗口。只截活动窗口时,可能只看到弹窗下面的页面,恰好漏掉本单最想定位的现场(CG-1910 推测的情况①)。
    • 无障碍服务需具备读取窗口列表的配置(如 flagRetrieveInteractiveWindows);如需修改服务配置,请在方案中说明影响。
  2. 保存节点可读取的全部属性,而不只是列出的字段。
    • 在原列表之外,至少补充:hint 文字、stateDescription/tooltip(API 可用时)、节点支持的 action 列表、focusable/focused/long-clickable/checkable、inputType、maxTextLength、collection/range 信息、子节点数量、可见区域(bounds in screen 与 visible-to-user)以及 drawingOrder(API 可用时)。
    • 唯一例外:password=true 节点不保存 text。
  3. 导出格式尽量兼容 uiautomator dump XML。
    • 管理员下载得到每个窗口一份 XML(或一个包含多个 <hierarchy> 的文件),节点属性名沿用 uiautomator dump 的命名(text、resource-id、class、package、content-desc、checkable、checked、clickable、enabled、focusable、focused、scrollable、long-clickable、password、selected、bounds 等),扩展属性另加前缀。这样可以直接复用现有 XML 分析脚本。
    • 存储时可压缩,压缩不得改变内容。
  4. 验收补充:
    • 合成测试:存在两个窗口(主页面 + 独立弹窗)时,两个窗口的节点都被完整保存,并可区分层级。
    • 导出的 XML 能被标准 XML 解析器读取,属性名与 uiautomator dump 一致;密码框 text 为空。

以上补充不改变 AGENTS.md「失败现场控件树」条款的存放、访问与 30 天清理限制。

## 方案补充:控件树必须是完整原始现场(2026-10-08,Claude Code,用户确认补充) 用户要求失败时保存的应是原始控件树,能完整还原现场。原草案 B.1/B.2 有两处会导致数据不完整,补充如下,取代原草案中对应表述: 1. **截取所有窗口,而不只是 `rootInActiveWindow`。** - 使用 `getWindows()` 截取屏幕上的全部窗口,记录每个窗口的类型、层级(layer)、标题、是否活动、是否获得焦点及窗口边界,再分别保存各窗口的完整节点树。 - 原因:PDD 挽留弹窗、系统对话框、输入法、悬浮层等可能是独立窗口。只截活动窗口时,可能只看到弹窗下面的页面,恰好漏掉本单最想定位的现场(CG-1910 推测的情况①)。 - 无障碍服务需具备读取窗口列表的配置(如 `flagRetrieveInteractiveWindows`);如需修改服务配置,请在方案中说明影响。 2. **保存节点可读取的全部属性,而不只是列出的字段。** - 在原列表之外,至少补充:hint 文字、stateDescription/tooltip(API 可用时)、节点支持的 action 列表、focusable/focused/long-clickable/checkable、inputType、maxTextLength、collection/range 信息、子节点数量、可见区域(bounds in screen 与 visible-to-user)以及 drawingOrder(API 可用时)。 - 唯一例外:`password=true` 节点不保存 text。 3. **导出格式尽量兼容 `uiautomator dump` XML。** - 管理员下载得到每个窗口一份 XML(或一个包含多个 `<hierarchy>` 的文件),节点属性名沿用 `uiautomator dump` 的命名(`text`、`resource-id`、`class`、`package`、`content-desc`、`checkable`、`checked`、`clickable`、`enabled`、`focusable`、`focused`、`scrollable`、`long-clickable`、`password`、`selected`、`bounds` 等),扩展属性另加前缀。这样可以直接复用现有 XML 分析脚本。 - 存储时可压缩,压缩不得改变内容。 4. **验收补充**: - 合成测试:存在两个窗口(主页面 + 独立弹窗)时,两个窗口的节点都被完整保存,并可区分层级。 - 导出的 XML 能被标准 XML 解析器读取,属性名与 `uiautomator dump` 一致;密码框 text 为空。 以上补充不改变 AGENTS.md「失败现场控件树」条款的存放、访问与 30 天清理限制。
Author
Owner

方案修订:采纳 Codex 对评论 8952 的审核建议(2026-10-08,Claude Code,用户确认补充)

依据:SynapBus #goauto 消息 #75/#76(Codex 对 #73/#74 与评论 8952 的审核)。Claude Code 已核对,6 条全部采纳;其中第 2 条已对照 origin/main 核实:accessibility_service_config.xml:5,7 已配置 flagRetrieveInteractiveWindows、canRetrieveWindowContent="true",GoAutoAccessibilityService.kt:45 已设置 FLAG_RETRIEVE_INTERACTIVE_WINDOWS。本评论修订评论 8952 及正文 B 节的对应表述,以下内容优先。

  1. 目标表述:由「截取所有窗口、完整还原现场」改为「尽可能完整保存失败时系统实际暴露的窗口与节点」。
    • getWindows() 只返回默认显示屏上的可交互窗口,存在模态窗口时底层窗口可能不返回;窗口 root 为 null、截取过程中窗口变化、内容不可访问时,必须在快照中显式记录,不能静默缺失。
    • 截取过程严格只读:不得为补全数据而点击、滑动、返回或关闭弹窗。
  2. 实现边界:不新增无障碍权限或服务配置。新增独立的「失败现场导出器」,不修改采集与采购正常流程使用的 capture() 和解析输入。
  3. 属性范围:由「所有可读属性」改为有版本号的有限公开属性清单。
    • 包括文本、边界、可见/选中/可点击等状态、action ID 与 label、hint、stateDescription 等,按 API 版本读取。
    • 区分「值为空」「当前系统不支持」「读取失败」。
    • 不通过反射导出任意 extras/Parcelable,不执行任何 action。
    • 保留节点原始顺序和系统暴露的不可见节点,不去重、不归一化原始值。
    • 达到容量、节点数或时间上限时,明确标记为 partial,不能冒充完整快照。
  4. 密码保护:password=true 的节点除 text 外,contentDescription、hint、stateDescription、tooltip、action label 等可能承载文字的字段也一律不保存;保留结构和状态,并补其他字段的泄漏测试。订单、地址等普通诊断数据仍按 AGENTS.md「失败现场控件树」条款保存,不增加额外门禁。
  5. 导出格式:管理员下载 ZIP,包含 manifest.json(快照元数据、窗口列表、partial 标记、属性清单版本等)和每个窗口一个 window-<id>.xml。
    • 每个 XML 只有一个 hierarchy 根,属性名沿用 UIAutomator dump 命名;扩展属性使用合法命名或命名空间。
    • 字符转义、换行、emoji 及压缩/解压后必须可完整回读;无法直接表示的字符不能静默丢弃。
    • Agent 与服务端仍只保存在私有数据库,不写入普通日志。
  6. 采集时机与验收:
    • 一次失败只保存一份快照(可包含多个窗口),不扩展为持续多帧采集。
    • 快照必须在弹出结果悬浮窗、切回 Agent 之前抓取。
    • 验收除双窗口、XML 可解析外,还需覆盖:窗口消失或 root 为 null;低版本系统缺失属性;密码节点多字段;特殊字符;超限 partial;ZIP 导出后回读;现有 XML 分析脚本实际读取成功。

仍待 Codex 回答的问题(正文「待 Codex 建议的问题」)

第 2 题(单帧还是多帧)已由上面第 6 点回答:单帧。其余仍待建议:

  1. 核单等待由约 15 秒延长到约 30 秒是否合适,是否只在观察到支付页后延长;
  2. 本机存储是否复用 AgentDiagnosticStore,与 #361「安装后诊断库无新增记录」疑点的关系及处理顺序;
  3. 上传方式(独立接口 + 本地 outbox,还是随结果上报)、单份大小上限和压缩方式;
  4. 采集任务失败是否纳入本单;
  5. 是否拆分为多张单元工单。
## 方案修订:采纳 Codex 对评论 8952 的审核建议(2026-10-08,Claude Code,用户确认补充) 依据:SynapBus `#goauto` 消息 #75/#76(Codex 对 #73/#74 与评论 8952 的审核)。Claude Code 已核对,6 条全部采纳;其中第 2 条已对照 origin/main 核实:`accessibility_service_config.xml:5,7` 已配置 `flagRetrieveInteractiveWindows`、`canRetrieveWindowContent="true"`,`GoAutoAccessibilityService.kt:45` 已设置 `FLAG_RETRIEVE_INTERACTIVE_WINDOWS`。本评论修订评论 8952 及正文 B 节的对应表述,以下内容优先。 1. **目标表述**:由「截取所有窗口、完整还原现场」改为「**尽可能完整保存失败时系统实际暴露的窗口与节点**」。 - `getWindows()` 只返回默认显示屏上的可交互窗口,存在模态窗口时底层窗口可能不返回;窗口 root 为 null、截取过程中窗口变化、内容不可访问时,必须在快照中显式记录,不能静默缺失。 - 截取过程严格只读:不得为补全数据而点击、滑动、返回或关闭弹窗。 2. **实现边界**:不新增无障碍权限或服务配置。新增独立的「失败现场导出器」,**不修改**采集与采购正常流程使用的 `capture()` 和解析输入。 3. **属性范围**:由「所有可读属性」改为**有版本号的有限公开属性清单**。 - 包括文本、边界、可见/选中/可点击等状态、action ID 与 label、hint、stateDescription 等,按 API 版本读取。 - 区分「值为空」「当前系统不支持」「读取失败」。 - 不通过反射导出任意 extras/Parcelable,不执行任何 action。 - 保留节点原始顺序和系统暴露的不可见节点,不去重、不归一化原始值。 - 达到容量、节点数或时间上限时,明确标记为 partial,不能冒充完整快照。 4. **密码保护**:`password=true` 的节点除 text 外,contentDescription、hint、stateDescription、tooltip、action label 等可能承载文字的字段也一律不保存;保留结构和状态,并补其他字段的泄漏测试。订单、地址等普通诊断数据仍按 AGENTS.md「失败现场控件树」条款保存,不增加额外门禁。 5. **导出格式**:管理员下载 **ZIP**,包含 `manifest.json`(快照元数据、窗口列表、partial 标记、属性清单版本等)和每个窗口一个 `window-<id>.xml`。 - 每个 XML 只有一个 `hierarchy` 根,属性名沿用 UIAutomator dump 命名;扩展属性使用合法命名或命名空间。 - 字符转义、换行、emoji 及压缩/解压后必须可完整回读;无法直接表示的字符不能静默丢弃。 - Agent 与服务端仍只保存在私有数据库,不写入普通日志。 6. **采集时机与验收**: - 一次失败只保存**一份快照**(可包含多个窗口),不扩展为持续多帧采集。 - 快照必须在弹出结果悬浮窗、切回 Agent 之前抓取。 - 验收除双窗口、XML 可解析外,还需覆盖:窗口消失或 root 为 null;低版本系统缺失属性;密码节点多字段;特殊字符;超限 partial;ZIP 导出后回读;现有 XML 分析脚本实际读取成功。 ### 仍待 Codex 回答的问题(正文「待 Codex 建议的问题」) 第 2 题(单帧还是多帧)已由上面第 6 点回答:单帧。其余仍待建议: 1. 核单等待由约 15 秒延长到约 30 秒是否合适,是否只在观察到支付页后延长; 3. 本机存储是否复用 `AgentDiagnosticStore`,与 #361「安装后诊断库无新增记录」疑点的关系及处理顺序; 4. 上传方式(独立接口 + 本地 outbox,还是随结果上报)、单份大小上限和压缩方式; 5. 采集任务失败是否纳入本单; 6. 是否拆分为多张单元工单。
ila changed title from feat(android): 采购失败保存现场控件树供管理员下载,并延长核单等待时间 to feat: 采购失败保存现场控件树供管理员下载(诊断闭环) 2026-10-08 09:34:49 +08:00
Author
Owner

拆单记录(2026-10-08,Claude Code,用户确认)

  • A(支付页安全返回后延长核单只读观察时间)已拆至 #365,按 A 节原设计单独实施,可先行。
  • #364 只保留 B(失败现场诊断完整闭环),不再按 Android / Server / Web 拆分。B 的交付前置仍是先验证 #361 诊断链路(触发→校验→写入→回读)。
  • 标题已改为只描述 B;正文顶部加拆单说明,A 节保留为历史。
  • 两单实施前均需用户明确授权;B 的管理端下载入口实施前需补标注截图或低保真图并经用户确认。
## 拆单记录(2026-10-08,Claude Code,用户确认) - A(支付页安全返回后延长核单只读观察时间)已拆至 #365,按 A 节原设计单独实施,可先行。 - #364 只保留 B(失败现场诊断完整闭环),不再按 Android / Server / Web 拆分。B 的交付前置仍是先验证 #361 诊断链路(触发→校验→写入→回读)。 - 标题已改为只描述 B;正文顶部加拆单说明,A 节保留为历史。 - 两单实施前均需用户明确授权;B 的管理端下载入口实施前需补标注截图或低保真图并经用户确认。
Author
Owner

方案补充:覆盖规格探测阶段,并在统一失败出口截取(2026-10-08,Claude Code,用户确认补充)

正文 B1 写的是「每个采购 attempt……覆盖 failed 和 order_result_unknown」,以下两点需要明确:

1. 规格探测(spec_probe)阶段的失败也要保存

  • 采购任务分为 spec_probe 和 purchase 两个阶段,两者都是在手机上实际执行的 attempt。例如 #1789 多次 PURCHASE_SPEC_PROBE_EMPTY 都发生在探测阶段。
  • 要求:两个阶段只要以失败结束(含 failed、order_result_unknown),都保存一份快照;manifest 记录 phase。
  • 成功结束的 spec_probe_completed 和 order_created 不保存。
  • 服务端判定的失败(例如 AI 规格匹配无结果、手机离线导致租约超时)没有手机现场,不在范围内;验收时应明确列出这类情况。

2. 截取挂在统一的失败出口,不按错误码逐个添加

  • 失败来源很多:执行器返回的失败结果、PurchaseLiveException 等明确失败、超时、意外异常,以及规则解析失败、无障碍未就绪等前置失败。依据 origin/main 4ae33d8,AgentForegroundService.executePurchaseTask()(约 576 行起)汇总执行结果并上报,另有 AgentApiException 和通用 Exception 的处理分支(约 687、689 行)及结果待核对的上报路径(约 740~770 行)。
  • 要求:在所有失败结果最终上报前都会经过的统一位置触发一次截取,而不是只在某几个错误码或某个函数里添加。每个 attempt 仍然最多一份。
  • 截取必须在 dismissPurchaseResultBubble/结果悬浮窗显示、切回 Agent 之前完成,并有时间上限,不能延误结果上报和设备释放。
  • 截取本身失败(无障碍服务断开、root 为 null 等)时,在本机诊断状态中记录原因,不改变原采购错误码。
  • 验收补充:
    • 合成测试覆盖探测阶段失败、下单阶段失败、明确失败、意外异常、结果待核对,每种都只生成一份快照;
    • 成功路径不生成快照;
    • 无障碍未就绪等无法截取的前置失败,有明确的「未截取」原因记录。
## 方案补充:覆盖规格探测阶段,并在统一失败出口截取(2026-10-08,Claude Code,用户确认补充) 正文 B1 写的是「每个采购 attempt……覆盖 `failed` 和 `order_result_unknown`」,以下两点需要明确: ### 1. 规格探测(spec_probe)阶段的失败也要保存 - 采购任务分为 `spec_probe` 和 `purchase` 两个阶段,两者都是在手机上实际执行的 attempt。例如 #1789 多次 `PURCHASE_SPEC_PROBE_EMPTY` 都发生在探测阶段。 - 要求:**两个阶段**只要以失败结束(含 `failed`、`order_result_unknown`),都保存一份快照;manifest 记录 `phase`。 - 成功结束的 `spec_probe_completed` 和 `order_created` 不保存。 - 服务端判定的失败(例如 AI 规格匹配无结果、手机离线导致租约超时)没有手机现场,不在范围内;验收时应明确列出这类情况。 ### 2. 截取挂在统一的失败出口,不按错误码逐个添加 - 失败来源很多:执行器返回的失败结果、`PurchaseLiveException` 等明确失败、超时、意外异常,以及规则解析失败、无障碍未就绪等前置失败。依据 origin/main `4ae33d8`,`AgentForegroundService.executePurchaseTask()`(约 576 行起)汇总执行结果并上报,另有 `AgentApiException` 和通用 `Exception` 的处理分支(约 687、689 行)及结果待核对的上报路径(约 740~770 行)。 - 要求:在**所有**失败结果最终上报前都会经过的统一位置触发一次截取,而不是只在某几个错误码或某个函数里添加。每个 attempt 仍然最多一份。 - 截取必须在 `dismissPurchaseResultBubble`/结果悬浮窗显示、切回 Agent 之前完成,并有时间上限,不能延误结果上报和设备释放。 - 截取本身失败(无障碍服务断开、root 为 null 等)时,在本机诊断状态中记录原因,不改变原采购错误码。 - 验收补充: - 合成测试覆盖探测阶段失败、下单阶段失败、明确失败、意外异常、结果待核对,每种都只生成一份快照; - 成功路径不生成快照; - 无障碍未就绪等无法截取的前置失败,有明确的「未截取」原因记录。
Author
Owner

#364 轻量设计 v1(2026-10-08,待用户确认)

用户本轮指令“做#364”已收到。核对正文B5及风险项后,尚未有管理员下载入口的设计确认;本轮先补设计证据,未编写生产代码、执行迁移或发布。仅复用现有采购任务详情的“执行记录”表,不新增页面。

布局(合成示意,无生产数据)

采购任务详情
└─ 执行记录
   次数 | 阶段       | 状态 | 结果 | 说明 | 开始时间 | 现场诊断(仅管理员)
   #1   | spec_probe | 失败 | …    | …    | …        | 完整  [下载控件树]
   #2   | purchase   | 待核对 …    | …    | …        | 部分  [下载控件树]
   #3   | purchase   | 失败 | …    | …    | …        | 未保存:无障碍不可用
  • 管理员的表格末尾追加“现场诊断”列,建议宽度200px;其他列顺序/功能不变。每一行按对应attempt UUID查询,不能把其他尝试的快照混入。
  • 完整/部分:均可下载ZIP;部分状态有说明(例如节点/大小/耗时上限、窗口不可读),仅展示固定诊断原因,不展示节点正文。
  • 未有服务器快照:显示“暂无现场数据”,不暗示知道手机是否已截取;服务端已收到未截取原因时显示相应固定原因;过期数据不可下载,不把“无数据”一律解释为失败。
  • 加载:该列显示“加载中…”;元数据读取失败显示“读取失败,重试”,不影响其他采购详情操作。
  • 点击下载:该行按钮加载并防重复点击;成功获得ZIP,失败明确提示并允许重试,禁止在线渲染原始XML。
  • 采购员:整列及下载入口均不显示;服务端独立校验管理员身份,不能只依赖UI隐藏。普通采购任务接口不携带原始树。
  • 不增加批量下载、在线树查看器、独立管理页或新的采购操作。沿用现有Element表格、标签和按钮规范。

实施与授权边界

批准v1后,先核定共享API/表结构及幂等契约,再实施Android→Server→Web闭环。SQLite新增独立快照/待上传表;Server新增独立诊断存储及管理员专用接口。保留正文所有隐私、单attempt一份、结果优先、恢复来源及清理边界。

需另行明确授权:Android本地诊断库追加升级、服务端诊断表追加迁移及管理员/设备专用接口权限实施、本地迁移验证。线上迁移、合并main、发布、安装、真机现场截取与订单操作不包含在该授权内。

容量/节点/耗时最终值仍需代表性样本验证,正文2MiB ZIP/8MiB解压/64MiB本机只是测试起点;不能因为v1界面通过就把它们认定为实测。无授权真机数据时可先合成测试,但正式现场闭环验收仍待用户授权。

设计记录

版本:v1;确认人/时间:待用户确认;覆盖:采购详情执行记录的正常、partial、空、加载、失败、下载中及管理员/采购员边界。
文档影响:本次仅补待审核设计到工单,不改当前实现Wiki,不创建本地任务快照。

## #364 轻量设计 v1(2026-10-08,待用户确认) 用户本轮指令“做#364”已收到。核对正文B5及风险项后,尚未有管理员下载入口的设计确认;本轮先补设计证据,未编写生产代码、执行迁移或发布。仅复用现有采购任务详情的“执行记录”表,不新增页面。 ### 布局(合成示意,无生产数据) ```text 采购任务详情 └─ 执行记录 次数 | 阶段 | 状态 | 结果 | 说明 | 开始时间 | 现场诊断(仅管理员) #1 | spec_probe | 失败 | … | … | … | 完整 [下载控件树] #2 | purchase | 待核对 … | … | … | 部分 [下载控件树] #3 | purchase | 失败 | … | … | … | 未保存:无障碍不可用 ``` - 管理员的表格末尾追加“现场诊断”列,建议宽度200px;其他列顺序/功能不变。每一行按对应attempt UUID查询,不能把其他尝试的快照混入。 - 完整/部分:均可下载ZIP;部分状态有说明(例如节点/大小/耗时上限、窗口不可读),仅展示固定诊断原因,不展示节点正文。 - 未有服务器快照:显示“暂无现场数据”,不暗示知道手机是否已截取;服务端已收到未截取原因时显示相应固定原因;过期数据不可下载,不把“无数据”一律解释为失败。 - 加载:该列显示“加载中…”;元数据读取失败显示“读取失败,重试”,不影响其他采购详情操作。 - 点击下载:该行按钮加载并防重复点击;成功获得ZIP,失败明确提示并允许重试,禁止在线渲染原始XML。 - 采购员:整列及下载入口均不显示;服务端独立校验管理员身份,不能只依赖UI隐藏。普通采购任务接口不携带原始树。 - 不增加批量下载、在线树查看器、独立管理页或新的采购操作。沿用现有Element表格、标签和按钮规范。 ### 实施与授权边界 批准v1后,先核定共享API/表结构及幂等契约,再实施Android→Server→Web闭环。SQLite新增独立快照/待上传表;Server新增独立诊断存储及管理员专用接口。保留正文所有隐私、单attempt一份、结果优先、恢复来源及清理边界。 需另行明确授权:Android本地诊断库追加升级、服务端诊断表追加迁移及管理员/设备专用接口权限实施、本地迁移验证。线上迁移、合并main、发布、安装、真机现场截取与订单操作不包含在该授权内。 容量/节点/耗时最终值仍需代表性样本验证,正文2MiB ZIP/8MiB解压/64MiB本机只是测试起点;不能因为v1界面通过就把它们认定为实测。无授权真机数据时可先合成测试,但正式现场闭环验收仍待用户授权。 ### 设计记录 版本:v1;确认人/时间:待用户确认;覆盖:采购详情执行记录的正常、partial、空、加载、失败、下载中及管理员/采购员边界。 文档影响:本次仅补待审核设计到工单,不改当前实现Wiki,不创建本地任务快照。
Author
Owner

本地验证环境补充授权(2026-10-08)

用户确认已更换电脑,本机 MySQL 为 127.0.0.1:3308,原本尚未初始化 goauto。经单独确认,授权创建新的本地 goauto 库、执行项目初始化及 #364 迁移/专用权限验证。仅本地空库;不涉及线上服务、生产数据、主分支合并、APK 安装或真机抓取。主仓库忽略配置 config.yaml 仅将数据库端口由旧 3307 调整为 3308;凭据未写入工单、源码或日志。

### 本地验证环境补充授权(2026-10-08) 用户确认已更换电脑,本机 MySQL 为 `127.0.0.1:3308`,原本尚未初始化 `goauto`。经单独确认,授权创建新的本地 `goauto` 库、执行项目初始化及 #364 迁移/专用权限验证。仅本地空库;不涉及线上服务、生产数据、主分支合并、APK 安装或真机抓取。主仓库忽略配置 `config.yaml` 仅将数据库端口由旧 3307 调整为 3308;凭据未写入工单、源码或日志。
Author
Owner

已按用户授权合并并推送main e450ac28989de6827328b411e4bdc050dded9ab3,正文已整合实现/验证及边界。三单组合Android Debug/Release各564项全部通过,APK构建通过;Server六个受影响包/构建和Web13项mock回归/构建通过。#366只合入②/hint保护,①待证据;#364真机诊断闭环仍待验。本次未部署、未执行线上迁移/重启、未安装手机。各项未验证与基线检查失败已明确记录,工单保持打开。

已按用户授权合并并推送main `e450ac28989de6827328b411e4bdc050dded9ab3`,正文已整合实现/验证及边界。三单组合Android Debug/Release各564项全部通过,APK构建通过;Server六个受影响包/构建和Web13项mock回归/构建通过。#366只合入②/hint保护,①待证据;#364真机诊断闭环仍待验。本次未部署、未执行线上迁移/重启、未安装手机。各项未验证与基线检查失败已明确记录,工单保持打开。
Author
Owner

用户在三单合并后确认本地联调方案,并要求“按照你的建议来增加配置”。本轮仅把干净、无运行引用的 D:/OPC/goauto-runtime 更新到已合并main e450ac2,在 D:/supervisord 添加GoAuto前后端两个实例,读取根目录已忽略config.yaml,后端SkipMigration,autostart=false。只做配置/依赖及静态验证,不启动业务服务、不reload全局Supervisor、不触发定时任务、迁移、权限写入或线上操作。作为本单本地联调运行准备记录,不新增产品行为;长期运维路径更新Deployment-and-Operations。

用户在三单合并后确认本地联调方案,并要求“按照你的建议来增加配置”。本轮仅把干净、无运行引用的 D:/OPC/goauto-runtime 更新到已合并main e450ac2,在 D:/supervisord 添加GoAuto前后端两个实例,读取根目录已忽略config.yaml,后端SkipMigration,autostart=false。只做配置/依赖及静态验证,不启动业务服务、不reload全局Supervisor、不触发定时任务、迁移、权限写入或线上操作。作为本单本地联调运行准备记录,不新增产品行为;长期运维路径更新Deployment-and-Operations。
Author
Owner

本地 Supervisor 运行准备完成(2026-10-08,用户追加授权)

  • 用户要求增加前/后端实例配置,并确认本轮启动是其手动点击。未切换或覆盖主仓库 D:/OPC/goauto 的旧分支及未提交资料。
  • D:/supervisord/supervisord.conf 引入 programs/goauto.conf,实例 goauto-admin-api / goauto-admin-ui;autostart=false、autorestart=true。运行目录 D:/OPC/goauto-runtime 固定于已合并 main 的 e450ac28989de6827328b411e4bdc050dded9ab3,两实例读取根仓库已忽略的 config.yaml,使用本地3308数据库、API8010/Web9527。后端带 SkipMigration。
  • 修复本机启动环境:仅后端进程前置已有 Go1.26.5 工具链并设 GOTOOLCHAIN=local,避免继承 GOSUMDB=off 导致自动工具链验证失败;该环境 Go build 通过,不修改全局设置或业务代码。
  • 前端旧依赖树先后缺失 vite/fdir/fast-glob,普通及强制安装未彻底恢复;仅停止失败的前端实例,验证路径后将旧依赖移动保留为 D:/OPC/goauto-runtime/web/node_modules.before-local-20261008,再按 frozen-lockfile 干净安装(成功),完整 Vite 配置 import 通过。随后恢复用户已启动的前端,未重启后端,未重启整个 Supervisor。
  • 实测 API /api/v1/health、Web首页、Web代理 /api/v1/health 均 HTTP200,首页含 Vite 入口。这只证明启动及代理就绪,不代表登录、诊断下载或真机端到端验收。
  • 本地预置 SYB/Yeeke 同步任务为启用状态,本轮未修改;用户启动API后会按既有调度运行,不能将本地环境视为外部访问自动隔离。
  • 运维 Wiki Deployment-and-Operations 已更新并回读 revision c2b8ffc6e686f6a97d45361bcc30c9dce4d69b78;一次 sync、一次 sync --check 完成,17页一致。镜像提交 002ab9b 已推送 integration/364-365-366-main(仅文档,未追加合并main)。
  • 未执行新业务库迁移、线上发布或APK安装。旧依赖备份为本机保留的未跟踪内容,不提交;工作树涉及既有真机待验收与产物,未清理。#364 保持待验收。
### 本地 Supervisor 运行准备完成(2026-10-08,用户追加授权) - 用户要求增加前/后端实例配置,并确认本轮启动是其手动点击。未切换或覆盖主仓库 D:/OPC/goauto 的旧分支及未提交资料。 - D:/supervisord/supervisord.conf 引入 programs/goauto.conf,实例 goauto-admin-api / goauto-admin-ui;autostart=false、autorestart=true。运行目录 D:/OPC/goauto-runtime 固定于已合并 main 的 e450ac28989de6827328b411e4bdc050dded9ab3,两实例读取根仓库已忽略的 config.yaml,使用本地3308数据库、API8010/Web9527。后端带 SkipMigration。 - 修复本机启动环境:仅后端进程前置已有 Go1.26.5 工具链并设 GOTOOLCHAIN=local,避免继承 GOSUMDB=off 导致自动工具链验证失败;该环境 Go build 通过,不修改全局设置或业务代码。 - 前端旧依赖树先后缺失 vite/fdir/fast-glob,普通及强制安装未彻底恢复;仅停止失败的前端实例,验证路径后将旧依赖移动保留为 D:/OPC/goauto-runtime/web/node_modules.before-local-20261008,再按 frozen-lockfile 干净安装(成功),完整 Vite 配置 import 通过。随后恢复用户已启动的前端,未重启后端,未重启整个 Supervisor。 - 实测 API /api/v1/health、Web首页、Web代理 /api/v1/health 均 HTTP200,首页含 Vite 入口。这只证明启动及代理就绪,不代表登录、诊断下载或真机端到端验收。 - 本地预置 SYB/Yeeke 同步任务为启用状态,本轮未修改;用户启动API后会按既有调度运行,不能将本地环境视为外部访问自动隔离。 - 运维 Wiki Deployment-and-Operations 已更新并回读 revision c2b8ffc6e686f6a97d45361bcc30c9dce4d69b78;一次 sync、一次 sync --check 完成,17页一致。镜像提交 002ab9b 已推送 integration/364-365-366-main(仅文档,未追加合并main)。 - 未执行新业务库迁移、线上发布或APK安装。旧依赖备份为本机保留的未跟踪内容,不提交;工作树涉及既有真机待验收与产物,未清理。#364 保持待验收。
Author
Owner

真机安装记录(2026-10-08)

用户指定192.168.0.226:35977安装集成APK。原0.9.64/77与本机新签名不同,首次覆盖安装被INSTALL_FAILED_UPDATE_INCOMPATIBLE拒绝,未改变旧数据。用户随后明确授权卸载旧Agent并重新安装。

已卸载且重新安装成功:cn.ilapage.goauto.agent,0.9.69/versionCode82;设备回读APK SHA256与已验证构建一致:bf8ed021d2bfcc05d49eda54a7b40240a30eda2d1e93199c8dee408c052b1dbe。包含已合并main的#364/#365/#366集成变更(#366仅已交付范围)。

卸载已清除旧Agent私有配置及本地记录,未备份,不可由本次操作恢复;需重新配置服务器/设备注册及检查无障碍授权。未清除PDD数据、未更改Admin业务数据、未启动采集采购或付款,安装成功不代表真机业务验收通过。

### 真机安装记录(2026-10-08) 用户指定192.168.0.226:35977安装集成APK。原0.9.64/77与本机新签名不同,首次覆盖安装被INSTALL_FAILED_UPDATE_INCOMPATIBLE拒绝,未改变旧数据。用户随后明确授权卸载旧Agent并重新安装。 已卸载且重新安装成功:cn.ilapage.goauto.agent,0.9.69/versionCode82;设备回读APK SHA256与已验证构建一致:bf8ed021d2bfcc05d49eda54a7b40240a30eda2d1e93199c8dee408c052b1dbe。包含已合并main的#364/#365/#366集成变更(#366仅已交付范围)。 卸载已清除旧Agent私有配置及本地记录,未备份,不可由本次操作恢复;需重新配置服务器/设备注册及检查无障碍授权。未清除PDD数据、未更改Admin业务数据、未启动采集采购或付款,安装成功不代表真机业务验收通过。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#364