更重要的是:即使该 ROM 行为正常、事件确实补发,仍存在纯代码层面的时序竞争——ClipboardRelayActivity.readFresh() 返回后 runner 立即 capture(),该事件可能尚未送达。因此「按包隔离」这个修法对该假设不敏感,两种情况都能修好。建议在工单中写明这一点,避免实施时花时间去证伪 ROM 行为。
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
所属与来源
daf7406)真机验证 #109 时反馈:Agent 已能取得goods_id,画面仍停留在 PDD 商品详情页,但约 30 秒后任务失败并返回 Agent,错误为PDD_DETAIL_ENTRY_FAILED:未进入 PDD 商品详情页。Codex 随后按用户要求完成只读诊断。当前事实与根因(提交
daf7406复核)真机及本地数据库证据:
PDD_DETAIL_ENTRY_FAILED失败。identity_resolved_at到finished_at约 30116ms,与当前规则collector.timeoutsMs.page=30000一致,说明失败来自采集器等待页面强证据超时,而非短链或 goods_id 解析。com.xunmeng.pinduoduo/.activity.NewPageActivity。com.xunmeng.pinduoduocom.xunmeng.pinduoduo.activity.NewPageActivityandroid:id/content+android.widget.FrameLayout代码根因:
CurrentPageIdentityRunner点击复制后调用ClipboardRelayActivity.readFresh()。该透明 Activity 使用独立 taskAffinity 读取新鲜剪贴板,完成后finishAndRemoveTask(),视觉上重新露出原 PDD 页面。GoAutoAccessibilityService的ActivityEvidenceTracker只保存一个全局activityName。收到透明中转页的窗口事件时,原 PDDNewPageActivity会被 Agent 的ClipboardRelayActivity覆盖。ClipboardRelayActivity.readFresh()返回后,runner 可立即进入下一次capture(),而中转 Activity 结束与 PDD 窗口恢复事件的处理存在时序竞争。待确认的 ROM 假设是:ColorOS 在透明中转页移除并重新露出原 PDD 窗口时可能不再补发 PDDTYPE_WINDOW_STATE_CHANGED。修复不得依赖该假设成立;无论事件迟到还是不补发,都必须保持同包 Activity 证据一致。PddScreenParser.pageEvidenceMatched要求当前 root 包名、记录的 Activity 和节点选择器三项同时匹配。上述“PDD root + Agent Activity”组合持续无法匹配,最终在 30 秒页面超时后报PDD_DETAIL_ENTRY_FAILED。现有单元测试只覆盖同一包内 PopupWindow 不得覆盖已确认 Activity,没有覆盖“PDD Activity → 跨包 Agent 透明 Activity → 无新的 PDD 窗口事件但 PDD root 已恢复”的真实序列。
目标
UiSnapshot根据当前rootInActiveWindow.packageName读取该包对应的 Activity 证据。非目标
实施方案
Activity 证据隔离与原子快照
ActivityEvidenceTracker改为按规范化 packageName 保存最后一次由 PackageManager 确认的 Activity;内部容器必须支持无障碍事件线程写入与采集任务线程读取,使用ConcurrentHashMap、同步保护或等效的线程安全不可变快照。current(packageName));UiDriver.currentActivity()的无参签名保持不变,避免波及RuleExecutor、采购路径及 stub。GoAutoAccessibilityService.currentActivity()的语义调整为:读取当前rootInActiveWindow的 packageName,再查询该包对应的 Activity;包名为空或该包从未观察到已声明 Activity 时返回 null,禁止回退到其他包。capture()必须用同一个局部root原子组装UiSnapshot:主路径先固定rootPackage,再以activityTracker.current(rootPackage)取得 Activity 并遍历该 root;不得在组装期间再次读取rootInActiveWindow。当 root 为 null 时直接返回UiSnapshot(null, null, emptyList()),不得拼接另一次读取到的包名或 Activity。详情入口诊断
PddProductDetailCollector页面证据等待阶段增加可选、默认 no-op 的安全诊断回调;当前页面采集路径传入现有SafeAgentDiagnosticRecorder,普通管理端采集和采购路径行为保持不变。AgentDiagnosticStore表结构和字段,不新增 SQLite 版本、服务端字段或共享接口。新增DETAIL_ENTRY阶段和必要原因枚举,超时至少区分ROOT_UNAVAILABLE、PACKAGE_MISMATCH、ACTIVITY_MISMATCH、SELECTOR_MISMATCH;记录仅限packageMatched、activityMatched、选择器匹配数量、尝试次数和耗时。DETAIL_ENTRY_MATCHED(或等价成功原因),用于真机量化“采集器启动到首次页面证据匹配”的耗时。诊断不得包含包外敏感数据、控件文本/树、截图、链接、goods_id 或剪贴板内容。衔接与回归
ActivityEvidenceTrackerTest:NewPageActivity→ AgentClipboardRelayActivity后,查询 PDD 仍返回NewPageActivity,查询 Agent 返回ClipboardRelayActivity;UiSnapshot,交由页面解析/采集入口验证强证据匹配;同时覆盖 Agent root、未知包、Activity 不匹配和选择器不匹配均不得通过。finishAndRemoveTask()与 PDD root 恢复之间仍有短暂窗口,只允许等待“PDD root + 同包 Activity + 节点证据”在既有有界轮询内恢复;不得加入固定长等待、放宽页面证据或重新打开 PDD 深链掩盖问题。安全边界
验收标准
NewPageActivity,并证明 tracker 的并发读写容器是线程安全实现。capture()的 root 存在与 root 缺失两条路径均只返回同一次 root 读取形成的一致快照;当前 root 属于 PDD 时绝不返回 Agent Activity,未知包无证据时返回 null。PDD_DETAIL_ENTRY_FAILED;PKG110 上“采集器启动到首次页面证据匹配”小于 3 秒,并以DETAIL_ENTRY成功诊断的耗时证据记录;任务最终按真实采集结果进入completed、completed_partial或其他具体采集失败码。DETAIL_ENTRY超时诊断可区分 root、包名、Activity 和选择器失配,且不新增 SQLite schema/版本;诊断、日志和数据库不新增链接、goods_id、剪贴板、控件文本/树或截图。验证方式
cd android && .\gradlew.bat :app:testDebugUnitTest :app:assembleDebugUiSnapshot+ parser/collector 的可控组合覆盖衔接。依赖、并行与风险
daf7406已完成并安装到 PKG110;本工单是其真机端到端验收发现的后续阻塞缺陷。GoAutoAccessibilityService、ActivityEvidenceTracker或当前页面身份流程的工单并行。文档影响
DETAIL_ENTRY本地诊断后,实施时应检查现有 Wiki 是否枚举诊断阶段。若未枚举则在工单说明无长期文档影响并跳过同步;若已有枚举则按 Wiki-first 门禁更新。状态
方案已按 2026-08-27 全栈评审结论收敛,待 Codex 实施。
评审意见(对提交
daf7406复核)根因判断与代码一致,「按包隔离 Activity 证据」方向正确,可以实施。以下四点建议在动手前定下来,其中第 1 点会直接减少改动面。
1. 不要修改
currentActivity()的签名(重要)方案第 2 条提议改为
current(packageName),第 5 条留待「实施时通过调用路径检查确认影响范围」。该范围已经确定,共 4 处:android/app/src/main/java/cn/ilapage/goauto/agent/automation/GoAutoAccessibilityService.kt:93:180(capture()的root == null分支):204(capture()主路径)android/app/src/main/java/cn/ilapage/goauto/agent/automation/RuleExecutor.kt:53(经UiDriver接口)android/app/src/main/java/cn/ilapage/goauto/agent/automation/PddProductDetailCollector.kt:970的 stub 实现UiDriver.currentActivity()是采购路径同样在用的共享接口,改签名会无谓波及采购代码。建议改为:
ActivityEvidenceTracker内部按规范化 packageName 保存证据,GoAutoAccessibilityService.currentActivity()的语义改为「当前rootInActiveWindow包名对应的 Activity 证据」,由服务内部取 root 包名后查表。接口签名保持不变,RuleExecutor与采购侧实现一行都不用改,语义反而更准确。请据此改写方案第 2 条与第 5 条。2.
capture()的两条返回路径必须一起改工单只覆盖了
:204的主路径。:180的root == null分支同样调用currentActivity(),若不走同一套按包查询,页面过渡期仍会漏出被污染的 Activity 值。请在方案中显式列出这两处。3. 根因第 3 条应标注为假设,且修复不应依赖它
「ColorOS 在透明中转页移除后可能不再发送 PDD
TYPE_WINDOW_STATE_CHANGED事件」目前没有证据支撑,按AGENTS.md的事实边界要求应记为待确认假设,不与代码事实混列。更重要的是:即使该 ROM 行为正常、事件确实补发,仍存在纯代码层面的时序竞争——
ClipboardRelayActivity.readFresh()返回后 runner 立即capture(),该事件可能尚未送达。因此「按包隔离」这个修法对该假设不敏感,两种情况都能修好。建议在工单中写明这一点,避免实施时花时间去证伪 ROM 行为。4. 补充可观测手段
当前
PDD_DETAIL_ENTRY_FAILED把「包名匹配但 Activity 不匹配」和「节点选择器不匹配」归为同一个错误码,这正是本次只能靠 30 秒超时倒推的原因。建议在页面证据等待超时时,复用 #106 的AgentDiagnosticStore记录一条可区分二者的原因枚举。目标 4 坚持精确 Activity 门禁是对的,但门禁需要配套诊断,否则同类问题复发时仍然缺乏证据。诊断仍不得记录控件树、截图、链接、goods_id 与剪贴板内容,与本工单安全边界一致。
两点小建议
非目标、安全边界、回退方式各段无需调整。
开始实施。已将全栈评审决策合并进工单正文并删除冲突方案:保留
UiDriver.currentActivity()无参接口;tracker 使用线程安全的按包证据;capture()以同一 root 原子生成快照;详情入口复用现有本地诊断表记录安全匹配状态。本次只改 Android,不改服务端、数据库、共享 API、规则和任务时序。实施完成,待验收
已按正文中的最终方案完成 Android 修复,提交:
e606096e497?(本地提交短哈希e606096,完整哈希以仓库提交为准)。实现
ActivityEvidenceTracker改为ConcurrentHashMap按包保存已确认 Activity;跨包 Agent 剪贴板中转不再覆盖 PDD 证据,同包新 Activity 仍可更新,PopupWindow 等未声明窗口仍被忽略。UiDriver.currentActivity()无参契约;Accessibility Service 根据当前 root 包查询对应 Activity。capture()只读取一次局部 root,并用其 package/activity/nodes 组成一致快照;root 缺失返回全 null/空节点快照。DETAIL_ENTRY本地诊断,支持DETAIL_ENTRY_MATCHED、ROOT_UNAVAILABLE、PACKAGE_MISMATCH、ACTIVITY_MISMATCH、SELECTOR_MISMATCH;仅复用现有字段记录布尔匹配、候选数、次数和耗时。自动验证
.\android\gradlew.bat -p android :app:testDebugUnitTest :app:assembleDebug:通过。.\scripts\verify.ps1 -Component android:通过(Debug/Release 单元测试和 Debug APK 构建)。python dev_scripts/harness.py check --strict:通过。git diff --check:通过。PKG110
工单保持 open / 待验收。
更正上一条实施记录中的提交哈希:完整提交为
e606096034dcd9484af213101ea92e24fa48dfdc。上一条中的e606096e497?是回写时的占位笔误,不是有效提交哈希;其余实施与验证记录不变。用户于 2026-08-28 明确确认本工单验收通过。按项目流程记录验收结论并关闭工单;本次仅更新工单状态,无新增长期文档事实,不重复同步 Wiki。