修复:采购路径寻找规格入口时零等待,底部操作栏未渲染即判失败 #211

Open
opened 2026-09-03 17:36:28 +08:00 by ila · 2 comments
Owner

来源与目标

2026-09-03,CG67(purchase_task #67)采购失败,停留在商品页,错误为「没有找到安全的商品规格入口」。用户原话:「最新agent,cg67采购失败:没有找到安全的商品规格入口,停留在商品页.分析原因」。

经排查,这不是 #209 的回归,而是采购路径在寻找规格入口时完全没有等待导致的竞态:商品页底部操作栏尚未挂载到无障碍树时就被判定为「找不到入口」并立即失败。

目标:让采购路径在判定规格入口不存在之前,具备与采集路径同等的、有界的入口就绪等待能力。

当前事实(真机与数据库证据,2026-09-03)

服务端已落库的错误(诊断串由 #209 引入,本次正是靠它定位):

purchase_task #67
error_code:    PURCHASE_SPEC_ENTRY_NOT_FOUND
error_message: 没有找到安全的商品规格入口
               [specEntryCandidates=0;explicit=0;nested=0;bottomPurchase=0;
                panelAlreadyOpen=false;reviewPage=false;pageEvidence=true]

关键:bottomPurchase=0。#209 修复的是 bottomPurchase=2(候选不唯一),本次是一个候选都没有,属于不同成因。

两次 attempt:

attempt phase result_type 起止 耗时
124 purchase spec_probe_completed 17:27:21.013 → 17:27:25.640 4.6s
125 purchase failed 17:27:25.751 → 17:27:29.501 3.75s

attempt 124 的规格探测成功打开了规格面板,证明 #209 的底部入口最右优先在该页面形态上生效。失败的是其后仅 111ms 启动的采购 attempt。

设备与页面核对:

  • 从设备 3B65BD02H7F00000 拉回实际安装的 base.apk 解包核对,dex 中确认包含 bottom_purchase_rightmost、点击目标不唯一、paymentBackAttempts,即设备上运行的确为含 #209、#210 的构建,排除装错包。
  • 失败后原地读取控件树:商品页两个底部按钮存在且位置与 CG63 一致,[446,2166][685,2328](单独购买)与 [685,2166][1080,2328](发起拼单),屏幕 1080x2376,safeBottomSpecEntries 的五项几何约束(centerX≥0.4W、centerY≥0.8H、bottom≥0.88H、width≥0.08W、height≤0.3H)全部通过。即页面本身完全合格,只是失败瞬间这些节点不在树中。

代码事实

android/app/src/main/java/cn/ilapage/goauto/agent/automation/PurchaseRehearsalExecutor.kt:

  1. currentScreen() 为单次抓取,无等待、无重采样:
private fun currentScreen(input: PurchaseExecutionInput): ParsedPddScreen =
    PddScreenParser.parse(driver.capture(), DEFAULT_COLLECTOR, input.goodsId, null)
  1. openSpecPanel() 只调用一次 currentScreen(),候选为空即刻返回失败,中间没有 pause 也没有重采样。

  2. 动作循环中形似重试的写法实际不重试——这些函数返回 null 表示成功,?: 链在失败(返回非 null)时直接短路:

OPEN_SPEC_PANEL -> openSpecPanel(input, action) ?: recoverSoldOut(input, closeSpecPanel = true) ?: openSpecPanel(input, action)
  1. 其前置守门 verifyProduct() 的放行条件极弱,任意一个可见节点即放行,包含尚在加载、底部操作栏未挂载的中间帧:
if (snapshot.packageName == PDD_PACKAGE && snapshot.nodes.any { it.visible }) {
    return recoverSoldOut(input, PddScreenParser.parse(snapshot, DEFAULT_COLLECTOR, input.goodsId, null))
}
  1. 同一函数内耐心程度自相矛盾:点击入口之后等待最多 3 秒(repeat(30) { pause(100) } 等待面板出现),寻找入口之前等待 0 秒。

采集路径早已解决同一问题,PddProductDetailCollector 中存在有界的入口就绪等待,随后还有向上滑动重试:

val entryReadyDeadline = minOf(deadline, now() + 2_000)
while (!current.specPanelOpen && specEntry == null && now() <= entryReadyDeadline) {
    pause(100)
    current = parse(goodsId, config, evidence)
    ...
}

该能力从未被带到采购路径。

未能取得的证据(限制说明)

尝试实测商品页底部操作栏的渲染延迟,但 uiautomator dump 单次往返约 2.3 秒,无法捕捉亚秒级窗口;第一个可采样点(约 5 秒)时按钮已存在。因此底部栏的确切渲染耗时未测得,仅能确定:失败的 attempt 全程仅 3.75 秒(含 openProduct 的 waitAfterMs=1000 与 PDD 前台检测),而按钮在约 5 秒时稳定存在。等待窗口的取值需要实施方在此约束下自行判断,并说明理由。

范围

  1. Android:在 openSpecPanel() 判定 SPEC_ENTRY_NOT_FOUND 之前,增加有界的入口就绪等待与重采样,参照采集路径 PddProductDetailCollector 中 entryReadyDeadline 的既有做法。等待期间必须继续执行既有的 reviewPageOpen、screen.problem、specPanelOpen 判定,不得跳过任何安全检查。
  2. Android:收紧 verifyProduct() 的放行条件,不再以「任意可见节点」放行。ParsedPddScreen.hasPurchaseProductEvidence() 是既有的、语义正确的判据(packageMatched && rootAvailable && (specPanelOpen || specEntry != null || quickConfirmationEntry != null || summary.title != null)),且已在 recoverSoldOut 中使用;应优先复用它而非新造判据。收紧后仍须保留既有的超时失败路径(PDD_DETAIL_ENTRY_FAILED),不得因页面始终不达标而无限等待。
  3. Android:等待超时后的失败诊断沿用 #209 已有的标量串写法,并使诊断能够反映「等待后仍无候选」这一事实(例如追加等待轮次或已用时长的标量),不得包含节点文本、路径、控件树或截图。
  4. 补充单测:覆盖「首次采样无底部入口、等待若干轮后入口出现并成功打开面板」「等待窗口内始终无任何候选仍在有界时间内失败」「verifyProduct 在仅有加载中间帧时不放行、在具备商品页证据后放行」三类场景。

非目标

  • 不修改服务端、采购任务状态机、采购规则快照或规则契约默认别名。
  • 不放宽 safeBottomSpecEntries 的几何约束、buyWords 语义要求、nonConfigurableClickDenylist 与评价上下文排除,不放宽 isSpecEntry / isSpecEntryContext 的语义判据。本工单只解决「等得不够久」,不解决「认得不够宽」。
  • 不引入无界等待或无界重试;所有新增等待必须有明确、有限的上限。
  • 不改动 openSpecPanel 点击之后既有的面板出现等待逻辑。
  • 不调查 attempt 124 为何在 spec_source=exact_match 的任务上执行了规格探测(见下方观察),该问题另行处理。
  • 不自动重试 CG67、不创建订单、不执行支付、不使用 OCR/VLM、不保存原始控件树或截图。

相邻观察(不在本工单范围)

attempt 124 的 phase 为 purchase,但回传 result_type=spec_probe_completed,而任务 spec_source=exact_match。按 server/app/goauto/purchase/reset.go 的逻辑,仅当 spec_source == "unresolved" 时才应进入探测阶段。这次多余的规格探测导致采购 attempt 在 111ms 后对商品页发起第二次导航,是本次竞态的直接触发条件之一。此现象需要单独确认,不在本工单范围内修改。

方案与设计证据

属于恢复既有「打开商品规格面板」行为的 Android 缺陷修复,无新页面、导航或用户可见交互变化,不需要 UI 原型。设计证据为上述数据库与真机证据、PurchaseRehearsalExecutor 现有安全语义,以及 PddProductDetailCollector 中已验证的同类等待实现。

验收

  • 首次采样缺少底部入口、随后入口出现的场景,能在有界等待内成功识别并打开规格面板。
  • 页面始终不具备任何安全候选时,仍在有界时间内失败,不产生无界等待。
  • verifyProduct 不再被纯加载中间帧放行,且仍保留原有超时失败路径。
  • 既有安全过滤(几何约束、购买词语义、拒绝名单、评价上下文排除、评价页处理)行为不变。
  • 失败诊断不含节点文本、路径、原始控件树或截图。
  • Android 单测与 :app:assembleDebug 通过。
  • 真机验证需人工授权后单独进行,本工单实施阶段不执行下单或支付。

文档影响

若最终方案改变了 Business-Rules-and-Glossary 中已记录的商品规格入口识别安全边界的对外描述,则需按 Wiki-first 流程更新;若仅为等待时序的内部实现调整、未改变对外安全语义,则记录为无长期文档影响并说明原因。

工作区已知存在与本工单无关的既有改动 docs/12-syb-erp-interface.md(曾在 #208、#209 两次阻止 sync),若再次阻止,记录原因,不得重置或覆盖。

依赖

无阻塞依赖。与 #209(已实施,待验收)为同一函数的不同成因:#209 解决候选不唯一,本工单解决候选尚未渲染即判失败。基于 494969d(#210 完成态),以保持与设备上已安装构建一致。

实施分工

实施由 Codex 执行;本仓库 Gitea 无 codex 账号,指派人记为 ila。实施完成后由 Claude 按本工单验收标准审核。

## 来源与目标 2026-09-03,CG67(`purchase_task` #67)采购失败,停留在商品页,错误为「没有找到安全的商品规格入口」。用户原话:「最新agent,cg67采购失败:没有找到安全的商品规格入口,停留在商品页.分析原因」。 经排查,这不是 #209 的回归,而是采购路径在**寻找规格入口时完全没有等待**导致的竞态:商品页底部操作栏尚未挂载到无障碍树时就被判定为「找不到入口」并立即失败。 目标:让采购路径在判定规格入口不存在之前,具备与采集路径同等的、有界的入口就绪等待能力。 ## 当前事实(真机与数据库证据,2026-09-03) **服务端已落库的错误(诊断串由 #209 引入,本次正是靠它定位):** ``` purchase_task #67 error_code: PURCHASE_SPEC_ENTRY_NOT_FOUND error_message: 没有找到安全的商品规格入口 [specEntryCandidates=0;explicit=0;nested=0;bottomPurchase=0; panelAlreadyOpen=false;reviewPage=false;pageEvidence=true] ``` 关键:`bottomPurchase=0`。#209 修复的是 `bottomPurchase=2`(候选不唯一),本次是**一个候选都没有**,属于不同成因。 **两次 attempt:** | attempt | phase | result_type | 起止 | 耗时 | |---|---|---|---|---| | 124 | purchase | `spec_probe_completed` | 17:27:21.013 → 17:27:25.640 | 4.6s | | 125 | purchase | `failed` | 17:27:25.751 → 17:27:29.501 | **3.75s** | attempt 124 的规格探测**成功打开了规格面板**,证明 #209 的底部入口最右优先在该页面形态上生效。失败的是其后仅 111ms 启动的采购 attempt。 **设备与页面核对:** - 从设备 3B65BD02H7F00000 拉回实际安装的 `base.apk` 解包核对,dex 中确认包含 `bottom_purchase_rightmost`、`点击目标不唯一`、`paymentBackAttempts`,即设备上运行的确为含 #209、#210 的构建,排除装错包。 - 失败后原地读取控件树:商品页两个底部按钮存在且位置与 CG63 一致,`[446,2166][685,2328]`(单独购买)与 `[685,2166][1080,2328]`(发起拼单),屏幕 1080x2376,`safeBottomSpecEntries` 的五项几何约束(centerX≥0.4W、centerY≥0.8H、bottom≥0.88H、width≥0.08W、height≤0.3H)全部通过。即页面本身完全合格,只是失败瞬间这些节点不在树中。 ## 代码事实 `android/app/src/main/java/cn/ilapage/goauto/agent/automation/PurchaseRehearsalExecutor.kt`: 1. `currentScreen()` 为单次抓取,无等待、无重采样: ```kotlin private fun currentScreen(input: PurchaseExecutionInput): ParsedPddScreen = PddScreenParser.parse(driver.capture(), DEFAULT_COLLECTOR, input.goodsId, null) ``` 2. `openSpecPanel()` 只调用一次 `currentScreen()`,候选为空即刻返回失败,中间没有 `pause` 也没有重采样。 3. 动作循环中形似重试的写法实际不重试——这些函数返回 `null` 表示成功,`?:` 链在失败(返回非 null)时直接短路: ```kotlin OPEN_SPEC_PANEL -> openSpecPanel(input, action) ?: recoverSoldOut(input, closeSpecPanel = true) ?: openSpecPanel(input, action) ``` 4. 其前置守门 `verifyProduct()` 的放行条件极弱,任意一个可见节点即放行,包含尚在加载、底部操作栏未挂载的中间帧: ```kotlin if (snapshot.packageName == PDD_PACKAGE && snapshot.nodes.any { it.visible }) { return recoverSoldOut(input, PddScreenParser.parse(snapshot, DEFAULT_COLLECTOR, input.goodsId, null)) } ``` 5. 同一函数内耐心程度自相矛盾:**点击入口之后**等待最多 3 秒(`repeat(30) { pause(100) }` 等待面板出现),**寻找入口之前**等待 0 秒。 **采集路径早已解决同一问题**,`PddProductDetailCollector` 中存在有界的入口就绪等待,随后还有向上滑动重试: ```kotlin val entryReadyDeadline = minOf(deadline, now() + 2_000) while (!current.specPanelOpen && specEntry == null && now() <= entryReadyDeadline) { pause(100) current = parse(goodsId, config, evidence) ... } ``` 该能力从未被带到采购路径。 ## 未能取得的证据(限制说明) 尝试实测商品页底部操作栏的渲染延迟,但 `uiautomator dump` 单次往返约 2.3 秒,无法捕捉亚秒级窗口;第一个可采样点(约 5 秒)时按钮已存在。因此**底部栏的确切渲染耗时未测得**,仅能确定:失败的 attempt 全程仅 3.75 秒(含 openProduct 的 `waitAfterMs=1000` 与 PDD 前台检测),而按钮在约 5 秒时稳定存在。等待窗口的取值需要实施方在此约束下自行判断,并说明理由。 ## 范围 1. Android:在 `openSpecPanel()` 判定 `SPEC_ENTRY_NOT_FOUND` 之前,增加**有界**的入口就绪等待与重采样,参照采集路径 `PddProductDetailCollector` 中 `entryReadyDeadline` 的既有做法。等待期间必须继续执行既有的 `reviewPageOpen`、`screen.problem`、`specPanelOpen` 判定,不得跳过任何安全检查。 2. Android:收紧 `verifyProduct()` 的放行条件,不再以「任意可见节点」放行。`ParsedPddScreen.hasPurchaseProductEvidence()` 是既有的、语义正确的判据(`packageMatched && rootAvailable && (specPanelOpen || specEntry != null || quickConfirmationEntry != null || summary.title != null)`),且已在 `recoverSoldOut` 中使用;应优先复用它而非新造判据。收紧后仍须保留既有的超时失败路径(`PDD_DETAIL_ENTRY_FAILED`),不得因页面始终不达标而无限等待。 3. Android:等待超时后的失败诊断沿用 #209 已有的标量串写法,并使诊断能够反映「等待后仍无候选」这一事实(例如追加等待轮次或已用时长的标量),不得包含节点文本、路径、控件树或截图。 4. 补充单测:覆盖「首次采样无底部入口、等待若干轮后入口出现并成功打开面板」「等待窗口内始终无任何候选仍在有界时间内失败」「`verifyProduct` 在仅有加载中间帧时不放行、在具备商品页证据后放行」三类场景。 ## 非目标 - 不修改服务端、采购任务状态机、采购规则快照或规则契约默认别名。 - 不放宽 `safeBottomSpecEntries` 的几何约束、`buyWords` 语义要求、`nonConfigurableClickDenylist` 与评价上下文排除,不放宽 `isSpecEntry` / `isSpecEntryContext` 的语义判据。本工单只解决「等得不够久」,不解决「认得不够宽」。 - 不引入无界等待或无界重试;所有新增等待必须有明确、有限的上限。 - 不改动 `openSpecPanel` 点击之后既有的面板出现等待逻辑。 - 不调查 attempt 124 为何在 `spec_source=exact_match` 的任务上执行了规格探测(见下方观察),该问题另行处理。 - 不自动重试 CG67、不创建订单、不执行支付、不使用 OCR/VLM、不保存原始控件树或截图。 ## 相邻观察(不在本工单范围) attempt 124 的 `phase` 为 `purchase`,但回传 `result_type=spec_probe_completed`,而任务 `spec_source=exact_match`。按 `server/app/goauto/purchase/reset.go` 的逻辑,仅当 `spec_source == "unresolved"` 时才应进入探测阶段。这次多余的规格探测导致采购 attempt 在 111ms 后对商品页发起第二次导航,是本次竞态的直接触发条件之一。此现象需要单独确认,不在本工单范围内修改。 ## 方案与设计证据 属于恢复既有「打开商品规格面板」行为的 Android 缺陷修复,无新页面、导航或用户可见交互变化,不需要 UI 原型。设计证据为上述数据库与真机证据、`PurchaseRehearsalExecutor` 现有安全语义,以及 `PddProductDetailCollector` 中已验证的同类等待实现。 ## 验收 - 首次采样缺少底部入口、随后入口出现的场景,能在有界等待内成功识别并打开规格面板。 - 页面始终不具备任何安全候选时,仍在有界时间内失败,不产生无界等待。 - `verifyProduct` 不再被纯加载中间帧放行,且仍保留原有超时失败路径。 - 既有安全过滤(几何约束、购买词语义、拒绝名单、评价上下文排除、评价页处理)行为不变。 - 失败诊断不含节点文本、路径、原始控件树或截图。 - Android 单测与 `:app:assembleDebug` 通过。 - 真机验证需人工授权后单独进行,本工单实施阶段不执行下单或支付。 ## 文档影响 若最终方案改变了 `Business-Rules-and-Glossary` 中已记录的商品规格入口识别安全边界的对外描述,则需按 Wiki-first 流程更新;若仅为等待时序的内部实现调整、未改变对外安全语义,则记录为无长期文档影响并说明原因。 工作区已知存在与本工单无关的既有改动 `docs/12-syb-erp-interface.md`(曾在 #208、#209 两次阻止 `sync`),若再次阻止,记录原因,不得重置或覆盖。 ## 依赖 无阻塞依赖。与 #209(已实施,待验收)为同一函数的不同成因:#209 解决候选不唯一,本工单解决候选尚未渲染即判失败。基于 `494969d`(#210 完成态),以保持与设备上已安装构建一致。 ## 实施分工 实施由 Codex 执行;本仓库 Gitea 无 codex 账号,指派人记为 ila。实施完成后由 Claude 按本工单验收标准审核。
ila self-assigned this 2026-09-03 17:36:28 +08:00
Author
Owner

实施完成(待验收)

基于基线 494969d(#210 完成态),在分支 feat/211-spec-entry-ready-wait 完成 #211,提交 22755e0:fix(agent): 有界等待采购规格入口就绪并收紧商品页证据放行 (#211)。

方案落实(对照工单范围)

  1. 有界入口就绪等待与重采样:openSpecPanel() 在判定 SPEC_ENTRY_NOT_FOUND 前改为 while (target == null) 循环,每次迭代继续执行既有的 reviewPageOpen、screen.problem、specPanelOpen 判定,不跳过任何安全检查;无候选时 pause(SPEC_ENTRY_READY_POLL_MILLIS) 并重采样。等待上限取 SPEC_ENTRY_READY_WAIT_POLLS = 20 x SPEC_ENTRY_READY_POLL_MILLIS = 100L = 2000ms,与采集路径 PddProductDetailCollector 既有 entryReadyDeadline 的 2 秒有界上限保持一致。窗口取值理由:工单已说明无法测得底部栏亚秒级渲染耗时,且首个稳定采样点(约 5 秒)时按钮已存在,故采用与采集路径一致的 2 秒有界窗口,把「等得不够久」的竞态窗口纳入处理,同时保持明确有限上限。

  2. 收紧 verifyProduct() 放行条件:不再以「任意可见节点」放行,改为复用既有语义判据 screen.hasPurchaseProductEvidence()(packageMatched && rootAvailable && (specPanelOpen || specEntry != null || quickConfirmationEntry != null || summary.title != null))。纯加载中间帧(仅有 content 节点)不再放行;保留原有 repeat(50) 有界轮询与超时失败路径 PDD_DETAIL_ENTRY_FAILED。

  3. 失败诊断完善:specEntryEvidence() 追加标量 entryReadyWaitPolls 与 entryReadyWaitMillis,超时路径在 SPEC_ENTRY_NOT_FOUND 前用 entryReadyWaitPolls=20;entryReadyWaitMillis=2000 表达「等待后仍无候选」;不包含任何节点文本、路径、控件树或截图。

  4. 单测补充:新增/调整三类场景——「首次采样无底部入口、等待若干轮后入口出现并成功打开面板」;「等待窗口内始终无任何候选仍在有界时间内失败」;「verifyProduct 在仅有加载中间帧时不放行、在具备商品页证据后放行」。entryReadyWaitPolls 计数断言一并校验等待迭代确切为 20(另 1 个 100ms pause 来自既有 open-product 前台轮询)。

验证

  • android: gradlew clean test:testDebugUnitTest 与 testReleaseUnitTest 全部通过(BUILD SUCCESSFUL)。
  • android: gradlew assembleDebug:通过。
  • 未执行真机验证、未创建订单、未支付、未使用 OCR/VLM。

文档影响

无长期文档影响并说明原因:本次仅为商品规格入口的等待时序内部实现调整(有界就绪等待 + 收紧商品页放行判据),未改变对外安全语义(几何约束、购买词语义、拒绝名单、评价上下文排除、评价页处理均保持不变),未新增/删除页面、导航或用户可见交互,故不触发 Wiki 更新;未运行 sync。

工作区已知的无关改动 docs/12-syb-erp-interface.md 未触及、未重置、未覆盖;另发现 server/config/settings.yml(writertimeout 2 -> 620)亦与本工单无关,同样未纳入本次提交。本次提交仅含两个 Android 文件。

遗留

需在真机(含 #209/#210 构建的设备形态)人工授权后单独验证:首次采样无底部入口、随后入口出现能打开的竞态场景。

## 实施完成(待验收) 基于基线 `494969d`(#210 完成态),在分支 `feat/211-spec-entry-ready-wait` 完成 #211,提交 `22755e0`:`fix(agent): 有界等待采购规格入口就绪并收紧商品页证据放行 (#211)`。 ### 方案落实(对照工单范围) 1. **有界入口就绪等待与重采样**:`openSpecPanel()` 在判定 `SPEC_ENTRY_NOT_FOUND` 前改为 `while (target == null)` 循环,每次迭代继续执行既有的 `reviewPageOpen`、`screen.problem`、`specPanelOpen` 判定,不跳过任何安全检查;无候选时 `pause(SPEC_ENTRY_READY_POLL_MILLIS)` 并重采样。等待上限取 `SPEC_ENTRY_READY_WAIT_POLLS = 20` x `SPEC_ENTRY_READY_POLL_MILLIS = 100L` = **2000ms**,与采集路径 `PddProductDetailCollector` 既有 `entryReadyDeadline` 的 2 秒有界上限保持一致。窗口取值理由:工单已说明无法测得底部栏亚秒级渲染耗时,且首个稳定采样点(约 5 秒)时按钮已存在,故采用与采集路径一致的 2 秒有界窗口,把「等得不够久」的竞态窗口纳入处理,同时保持明确有限上限。 2. **收紧 `verifyProduct()` 放行条件**:不再以「任意可见节点」放行,改为复用既有语义判据 `screen.hasPurchaseProductEvidence()`(`packageMatched && rootAvailable && (specPanelOpen || specEntry != null || quickConfirmationEntry != null || summary.title != null)`)。纯加载中间帧(仅有 `content` 节点)不再放行;保留原有 `repeat(50)` 有界轮询与超时失败路径 `PDD_DETAIL_ENTRY_FAILED`。 3. **失败诊断完善**:`specEntryEvidence()` 追加标量 `entryReadyWaitPolls` 与 `entryReadyWaitMillis`,超时路径在 `SPEC_ENTRY_NOT_FOUND` 前用 `entryReadyWaitPolls=20;entryReadyWaitMillis=2000` 表达「等待后仍无候选」;不包含任何节点文本、路径、控件树或截图。 4. **单测补充**:新增/调整三类场景——「首次采样无底部入口、等待若干轮后入口出现并成功打开面板」;「等待窗口内始终无任何候选仍在有界时间内失败」;「`verifyProduct` 在仅有加载中间帧时不放行、在具备商品页证据后放行」。`entryReadyWaitPolls` 计数断言一并校验等待迭代确切为 20(另 1 个 100ms pause 来自既有 open-product 前台轮询)。 ### 验证 - `android: gradlew clean test`:`testDebugUnitTest` 与 `testReleaseUnitTest` 全部通过(`BUILD SUCCESSFUL`)。 - `android: gradlew assembleDebug`:通过。 - 未执行真机验证、未创建订单、未支付、未使用 OCR/VLM。 ### 文档影响 无长期文档影响并说明原因:本次仅为商品规格入口的**等待时序**内部实现调整(有界就绪等待 + 收紧商品页放行判据),未改变对外安全语义(几何约束、购买词语义、拒绝名单、评价上下文排除、评价页处理均保持不变),未新增/删除页面、导航或用户可见交互,故不触发 Wiki 更新;未运行 `sync`。 工作区已知的无关改动 `docs/12-syb-erp-interface.md` 未触及、未重置、未覆盖;另发现 `server/config/settings.yml`(`writertimeout` 2 -> 620)亦与本工单无关,同样未纳入本次提交。本次提交仅含两个 Android 文件。 ### 遗留 需在真机(含 #209/#210 构建的设备形态)人工授权后单独验证:首次采样无底部入口、随后入口出现能打开的竞态场景。
Author
Owner

真机安装(补充)

用户授权后,已构建 assembleDebug 并安装到真机 3B65BD02H7F00000(adb install -r,Success,lastUpdateTime=2026-09-03 17:56:17)。

从设备拉回安装的 base.apk 解包核对 dex(classes8.dex):确认包含 entryReadyWaitPolls、entryReadyWaitMillis(本工单新增诊断标量)、hasPurchaseProductEvidence(verifyProduct 收紧判据),并仍包含 bottom_purchase_rightmost(#209,证明是在 #209/#210 之上的累积构建),即设备上运行的确为含 #211 的构建。

尚未执行真实采购演练任务的功能性真机验证(需单独运行任务,涉及采购流程,未做下单/支付)。

## 真机安装(补充) 用户授权后,已构建 `assembleDebug` 并安装到真机 `3B65BD02H7F00000`(`adb install -r`,`Success`,`lastUpdateTime=2026-09-03 17:56:17`)。 从设备拉回安装的 `base.apk` 解包核对 dex(`classes8.dex`):确认包含 `entryReadyWaitPolls`、`entryReadyWaitMillis`(本工单新增诊断标量)、`hasPurchaseProductEvidence`(verifyProduct 收紧判据),并仍包含 `bottom_purchase_rightmost`(#209,证明是在 #209/#210 之上的累积构建),即设备上运行的确为含 #211 的构建。 尚未执行真实采购演练任务的功能性真机验证(需单独运行任务,涉及采购流程,未做下单/支付)。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#211