Files

168 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: T-104
title: 验证规格选择能力安全退出
phase: 1
deps: [T-103]
status: DONE
created: 2026-08-04
vikunja_task_id: 32
context_ref: 29fb528
work_branch: task/t-104-safe-exit
needs_device: true
needs_human_review: true
write_paths:
- docs/tasks/T-104.md
- client/src/cmbuyer_client/pdd/sku_selection.py
- client/src/cmbuyer_client/pdd/sku_selection_runner.py
- client/tests/pdd/test_sku_selection.py
- client/tests/pdd/fixtures/product_exit_8_17_0.xml
- client/scripts/capture_sku_exit_spike.py
- client/scripts/run_t103_sku_selection.py
- docs/current-state.md
---
<!-- BEGIN VIKUNJA EXPORT id=32 synced=2026-08-05T09:57:21Z sha256=8469785af6c7ffd0d42dd7f2bb56bad15996a1f696c854823074304739bf652e -->
## 问题 / 背景
T-103 已实现受控规格选择、闸门一读价、规格面板原始截图和单次 Back,但当前 `exit_sku_panel_safely()` 只要 hierarchy 变化且规格面板判据不再成立就返回成功。现有纯 mock 测试还把 Back 后的空 hierarchy 当成成功并允许 runner 发布 `safe_exit=completed`。2026-08-04 的只读诊断同时证明:通知栏覆盖时 `app_current` 仍可能报告拼多多,而节点树全部属于 SystemUI。因此“面板消失”或“前台包仍是 PDD”都不是安全退出的充分条件;T-104 必须先取得独立 post-exit 真机证据,再把后置条件收紧为稳定回到同一目标商品详情页。
## 关联需求与交互
- 功能:F-006 单趟流程中的安全退出边界。
- 架构/API:复用并收紧 `SkuSelectionFlow.exit_sku_panel_safely()` 与现有 runner;不改变服务端 API,不新建第二套采购流程。
- 依赖:T-103 完成真机规格选择、读价、原始截图和人工安全退出验收后才可开工;T-105 与 T-306 消费本任务结果。
- `status` 保持 `TODO`,`needs_device` 与 `needs_human_review` 均保持 true;开始后也必须由人完成真机验收。
## 方案
1. 阶段 A 只新增窄取证脚本。人先把手机停在已验证的拼多多 8.17.0、goods_id `937122477375` 目标规格面板;脚本必须先在内存中核验版本、PDD 前台、已知面板结构、目标颜色/尺码选中态与已取证当前价,任一不符时零动作、零产物。
2. 阶段 A 唯一状态改变是一次命名 Android Back。脚本不得发送商品 intent,不得调用任何页面 `click`,不得点击关闭坐标、空白、购买、数量、确认、提交或支付控件;Back 调用前即封存本次机会,超时、响应不明或故障调和均不得重试。
3. Back 返回后只读采集 post-exit screenshot/XML/App/设备/goods_id 摘要并原子发布本机证据。此阶段 manifest 只能声明 `human_review_required` 与 `back_attempts=1`,人工确认前绝不能写 `safe_exit=completed`,也不得由 agent 根据“面板消失”自行判定成功。
4. 人必须确认 post-exit 截图确为同一 goods_id `937122477375` 商品详情、规格面板已关闭、未进入确认/提交/支付或外部支付页、未创建订单,且截图/XML 对应。只有完成该确认后,阶段 B 才能从本项目 post-exit XML 提取不含个人信息或支付凭据的最小 `product_exit_8_17_0.xml` fixture。
5. T-103 的旧 `product_entry_8_17_0.xml` 只有通用“快要抢光”五层入口链和“免拼购买”,不能单独证明是同一商品。它最多作为新证据中的候选结构,必须由本轮 post-exit 证据重新验证,并与新证据里的同商品唯一正锚共同成立;不得把 Activity、旧 XML、前序项目、OCR、裸坐标、相似文本或其他购买文案当作同商品证明。
6. 如果新的真实 XML 没有可唯一绑定同一目标商品的正锚,任务开始后保持 `DOING` 并记录等待新证据,不编造 selector、不把通用入口链升级为同商品判据,也不放宽为“任意 PDD 商品页”。
7. 阶段 B 只收紧现有 Flow/runner:版本和前台包正确、节点树属于证据绑定的 PDD 受控页面、同一商品详情正判据精确唯一,并连续两次轮询命中后才算退出成功。两次之间发生漂移必须重新计数,超时仍失败。
8. 空 hierarchy、SystemUI、锁屏、非 PDD overlay、PDD 其他页、面板仍在、正锚缺失/重复/属性漂移、版本或前台漂移、确认/提交/支付危险语义均不得成功,且不得发布 completed manifest。危险词只能作为保守硬拒绝;没有 T-106/T-107 真机证据时不得把合成反例宣称为真实确认页或支付页分类器。
9. 所有正常、失败、超时和 finally 调和路径的 Back 总数都必须小于等于 1;Back 结果不明不重试。只有规格选择、闸门一读价、规格面板截图和严格安全退出全部成功,runner 才能原子发布结果与 `safe_exit=completed`。
10. `SkuSelectionRunResult` 必须显式返回规格面板原始 `screenshot_path` 和 UTC-aware `captured_at`。`captured_at` 在截图 RPC 成功附近获取,并把同一个值写入 manifest,禁止在退出后重新猜时间。若本机另存 post-exit 截图,必须使用独立文件名和独立角色;它没有 `SKU_PANEL_GATE_1` 资格,不能成为 `result.screenshot_path`,也不能被 T-306 上传。
11. 原始完整 post-exit XML 只保存在 `%LOCALAPPDATA%\cmbuyer\artifacts\T-104\...`,不得上传、提交 Git、写入 Vikunja 或普通日志;runner/CLI 不输出页面正文、设备 serial 或敏感路径。
## 验收要点
- 阶段 A 证据记录 screenshot/XML 路径与 SHA-256、PKG110、Android 16、Wi-Fi/USB、拼多多 8.17.0、goods_id 和 `back_attempts=1`;人工确认前没有 `safe_exit=completed`。
- 用新的 `product_exit_8_17_0.xml` 正例替换旧“空 hierarchy 即成功”的 fake;空 hierarchy 必须失败。成功需连续两次正判据,单次命中后漂移必须重置。
- 离线覆盖面板未退、SystemUI/锁屏、非 PDD overlay、PDD 其他页、正锚缺失/重复/漂移、版本/前台漂移和保守危险语义反例;所有分支 Back 调用总数小于等于 1,失败无 target、manifest 或 staging 残留。
- runner 成功结果的 `screenshot_path` 精确指向规格面板原始 PNG,`captured_at` 是 UTC-aware 且与 manifest 同值;post-exit 图不能冒充 Gate1。
- 静态 API、AST 和导入检查证明取证脚本零 intent/页面 click,T-104 不可达数量、确认页导航、授权、提交围栏、`submit_order_once()` 或任何支付能力。
- 只有人能确认真实最终页面为同一商品详情、未进入确认/提交/支付页且未创建订单;agent 完成离线实现后仍保持 `DOING`,不得自行标 `DONE`。
- client focused/full unittest、compileall、完整 init、上下文校验、Vikunja 导出检查与 diff-check 全部通过。
## 执行记录
### 2026-08-04T13:46:33Z · ila
2026-08-04 T-306 预研反向约束 T-104:成功 SkuSelectionRunResult 必须显式携带规格面板原始 screenshot_path 与实际 UTC captured_at;只有规格选择、闸门一读价、截图和严格安全退出全成功才返回。T-306 不枚举目录、不读 manifest 猜文件/时间,post-exit 商品详情截图不得冒充 gate1 证据。
### 2026-08-05T09:04:13Z · ila
2026-08-05 T-103 已在 git/线上完成,T-104 依赖解除,现由 root 领取并转 DOING;领取基线 29fb528,沿用工作路径 task/t-104-safe-exit。
先证据后判据的实施顺序保持不变:
1. 先只读审计 T-103 最新生产 Flow、最终 Back 前/后本地证据与 T-104 任务边界。T-103 的 sku-selection-production-20260805-final 与 sku-selection-after-back-20260805-final 已获人确认,可用于设计窄取证脚本和寻找候选正锚;但它们不是同一 T-104 原子 manifest,不能直接冒充阶段 A 验收证据。
2. 唯一 client 写入者先实现 capture_sku_exit_spike.py:输入只允许显式 serial 与全新 output-dir;无商品 intent、页面 click、坐标、规格动作、数量、确认、提交或支付能力。前置复用 T-103 已证目标颜色/M/12.88 classifier;唯一 mutation 为 RPC 前封存且最多一次的 Android Back。
3. Back 后只读连续采集 screenshot/XML/App/版本并原子发布本机证据;manifest 仅记录 back_attempts=1、RPC outcome、哈希和 human_review_required,人工确认前绝不写 safe_exit=completed。结果不明不重试、不发送任何后续 mutation。
4. 离线脚本、原子性、超时/重入、静态 API/AST 测试经独立审计和 root 门禁通过后,才由项目所有者把手机手工停在已验目标规格面板并执行阶段 A;人确认同 goods 商品详情、面板关闭、未进入确认/提交/支付且未创建订单。
5. 只有上述人验后,才从新 raw 提取 privacy-safe 的 product_exit_8_17_0.xml 最小 fixture,并设计阶段 B。若 raw 不含唯一同商品正锚,保持 DOING,不编造 selector、不放宽为任意 PDD 商品页。
6. 阶段 B 收紧现有 Flow/runner:版本/PDD 包、同 goods 正锚、无危险页与连续两帧稳定全部成立才返回安全退出;空 hierarchy/SystemUI/锁屏/其他 PDD 页/面板未退/锚点漂移全部失败。成功结果同时补齐规格面板 screenshot_path 与截图附近 UTC captured_at,post-exit 图不得冒充 Gate1。
写路径严格限于 T-104 frontmatter;共享 current-state 由 root 合入。needs_device/needs_human_review 保持,agent 不得自行 DONE。
### 2026-08-05T09:27:14Z · ila
2026-08-05 阶段 A 离线实现与独立审查完成,代码 commit 43fd8ce 已推送 main。
仅新增窄取证器/CLI/测试:人工先停驻目标规格面板,内存中连续两次核验 PDD 8.17.0、PKG110/Android 16/1080x2376、目标颜色+M+12.88;唯一 mutation 为 RPC 前封存且最多一次的命名 Android Back。Back 响应不明不重试,只允许发布 ambiguous_reconciled + human_review_required。Back 后在任何截图/XML 前先核验仍为 PDD 8.17.0,采集后复核 package/activity/version 稳定;外部包、版本或页面漂移、PNG/XML/读取/发布失败均清 staging、零 target。
manifest 不含 safe_exit 或 Gate1 资格;post-exit 图/XML 仅本地人工复核。静态检查证明脚本不可达 intent、页面 click、规格动作、数量、确认、围栏、提交或支付能力。
root 独立门禁:focused 71/71、client full 293/293、compileall、validate_agent_context、git diff --check、完整 init.ps1 全部通过。独立 reviewer 终审 PASS,无 P1/P2。
任务保持 DOING,下一步等待项目所有者把手机手工停在 goods_id 937122477375 的目标规格面板(黑色CHA(纯棉)、M(建议100-115)、12.88),执行一次阶段 A 脚本并人工确认 Back 后为同一商品详情、面板关闭、未进入确认/提交/支付、未创建订单。
### 2026-08-05T09:38:22Z · ila
2026-08-05 阶段 A 首次真机尝试在唯一 Back 前置核验处 fail closed,未发布 target/staging。项目所有者随后人工确认规格面板仍打开,因此本次 Back 没有送达;后续仅做只读诊断。
只读诊断在 PDD 8.17.0、PKG110、Android 16、1080x2376、Wi-Fi 设备上证明:目标颜色与 M 均为 selected=true,尺码仍为既有精确 bounds `[439,1582][831,1667]`;同一目标颜色卡片因横向列表滚动,从既有 `[372,1000][684,1024]` 精确移动为 `[126,1000][438,1024]`,其余完整 TARGETS_SELECTED profile 与 12.88 双价格布局均未漂移。
新本地只读证据:`%LOCALAPPDATA%\cmbuyer\artifacts\T-104\sku-exit-precondition-color-shift-937122477375-20260805`。screenshot SHA-256 `aef1281c09e72990f06ef5a301d701efc37eaeb0f3ed5866f617e81313b22028`;hierarchy SHA-256 `3ffd7fa673e3b83e288c37eecaf65ab03480f9471814dd18019480fdeff0a41e`;captured_at `2026-08-05T09:35:40.312763+00:00`。项目所有者在运行前已按要求声明已选“黑色CHA(纯棉)”与“M(建议100-115)”、价格 12.88 并保持面板打开。
最小兼容边界:只新增上述第二个 exact color bounds 的完整 TARGETS_SELECTED spec;文字、selected、价格、摘要、尺码和其余结构继续全部精确匹配,任意相邻/未知 bounds 仍在 Back 前零动作失败。真机只读双 precheck 已通过;尚未再次发送 Back。
### 2026-08-05T09:47:20Z · ila
2026-08-05 T-104 阶段 A 真机取证完成并通过项目所有者人工复核。
最终证据目录:`%LOCALAPPDATA%\cmbuyer\artifacts\T-104\sku-exit-phase-a-937122477375-20260805-final-v3`;captured_at `2026-08-05T09:44:33.891598+00:00`;Wi-Fi;PKG110;Android 16;PDD 8.17.0;goods_id `937122477375`。manifest 明确 `back_attempts=1`、`rpc_outcome=completed`、`post_exit_status=human_review_required`,不含 safe_exit 或 Gate1 资格。
原子产物:post_exit_screenshot.png SHA-256 `2b9d931d8dfa7325629f53a996d65dc2e95bfa185ddeae174110fa1237221e2e`;post_exit_hierarchy.xml SHA-256 `d57e835e9d4b1b7ac1b96d6178ed5d9de09f53b2b64ec2ef76740980fc9243bc`;post_exit_app.json SHA-256 `b86205c3a7a865ce14eb57e4776184fcf7cc8aefe6d5f7442839be6218f6abf5`。
生产 capturer 外仅包了一层只记录方法名的诊断代理,调用序列证明:两次前置核验 → 唯一一次 pressKey → 前台/版本 → screenshot → XML → 前台/版本;没有 intent、页面 click、规格、数量、确认、提交或支付动作,也没有第二次 Back。
项目所有者完成三项人工确认:①手机与截图均为 goods_id 937122477375 的同一商品详情;②规格面板已关闭,未进入确认、提交或支付页面;③未创建订单。截图/XML 由同一 staging 原子发布且前后 package/activity/version 稳定。
阶段 A 达标。任务保持 DOING,下一步只从上述本项目真实 post-exit XML 提取无个人信息/支付凭据的最小 `product_exit_8_17_0.xml`,再实现版本绑定、同商品正锚、连续两帧稳定的阶段 B 判据。
### 2026-08-05T09:56:55Z · ila
2026-08-05 T-104 阶段 B 完成,代码 commit `1bb5492` 已推送 main。
从已人工确认 raw XML 提取最小 `client/tests/pdd/fixtures/product_exit_8_17_0.xml`:同商品正锚为唯一 exact `tv_title` 容器、完整标题 content-desc、两段直接只读文本及固定属性;同时要求已取证底部规格入口完整投影唯一。安全退出不解析或返回商品页价格,规格入口中的金额只保持既有身份常量,Gate1 仍只来自规格面板原始截图。
`exit_sku_panel_safely()` 现在每帧重查 PDD 8.17.0/前台包,要求同商品投影连续两次一致;单次命中后发生漂移会清空稳定计数。空 hierarchy、面板仍在、SystemUI、锁屏、非 PDD overlay、PDD 其他页、正锚缺失/重复/属性漂移、版本/前台漂移与合成危险动作语义全部失败关闭,所有路径 Back≤1。
runner 只有在精确规格、Gate1 读价、原始规格 PNG、严格同商品退出和完整动作审计全部成功后才发布 `safe_exit=completed`;`SkuSelectionRunResult` 明确返回原始规格 `screenshot_path` 与截图 RPC 成功附近的 UTC-aware `captured_at`,manifest 使用同一时间。post-exit 图不进入 runner 结果,也无 Gate1 资格。
验证:真实 post-exit raw 与最小 fixture 均被新投影命中;focused 76/76、client full 298/298、compileall、validate_agent_context、git diff --check、完整 init.ps1 全部通过。静态边界继续证明 T-104 不可达数量、确认、授权、提交围栏、submit_order_once 或支付能力。
结合阶段 A 的最终 manifest 与项目所有者三项人工确认,T-104 全部验收要点已满足,可转 DONE。
<!-- END VIKUNJA EXPORT -->
## 边界
- T-104 必须等待 T-103 完成人工真机验收后才可转 `DOING`。本任务先取证、后写退出成功判据;不得从
当前实现、前序项目、旧 XML、Activity 名或推理直接声明商品详情页。
- 阶段 A 取证脚本必须先在内存中核验拼多多 8.17.0、goods_id `937122477375` 的已知目标规格面板、
目标颜色/尺码选中态和已取证当前价。唯一状态改变是一次命名 Android Back;不得发送 intent,不得调用
页面 `click`,不得点击关闭坐标、空白、购买、数量、确认、提交或支付控件。Back 调用前即封存机会,
超时、响应不明和 finally 调和均不得重试。
- 阶段 A 在 Back 后只能只读采集 post-exit screenshot/XML/App/设备摘要并原子发布本机证据;manifest
只能声明 `human_review_required` 与 `back_attempts=1`。人工确认前绝不能写 `safe_exit=completed`,
agent 也不得把“面板消失”或“前台包仍是 PDD”自行解释为安全退出。
- 人确认 post-exit 截图确为同一目标商品详情、面板已关闭、未进入确认/提交/支付或外部支付页、未创建
订单,且截图/XML 对应后,才允许从本项目 XML 提取最小 `product_exit_8_17_0.xml` fixture。
- 旧 `product_entry_8_17_0.xml` 只有通用“快要抢光”五层入口链和“免拼购买”,不能单独证明同一
商品;它最多是新证据中的候选结构,必须与新证据的同商品唯一正锚共同成立。若真实 XML 没有唯一
正锚,任务开始后保持 `DOING` 等待新证据,不臆造 selector、不放宽为任意 PDD 商品页。
- “规格面板已消失”不是退出成功。阶段 B 成功必须由本项目 post-exit 证据证明:版本和前台包正确、
节点树属于证据绑定的 PDD 受控页面、同一目标商品详情正判据精确唯一,并连续两次轮询命中;中间漂移
必须重新计数。空 hierarchy、SystemUI/锁屏、非 PDD overlay、PDD 其他页、面板仍在、正锚缺失/
重复/属性漂移、版本或前台漂移一律失败且不得发布 completed manifest。
- 确认/提交/支付文字只能作为保守硬拒绝。T-106/T-107 真机取证前,不得把合成反例宣称为真实确认页、
提交页或支付页分类器;未知页面只能 fail closed,不能加同义、包含、OCR、裸坐标或 Activity 兜底。
- 只收紧现有 `SkuSelectionFlow` 与 runner 的退出后置条件,不新建第二套采购流程,不新增通用 `click`、
数量、确认页导航、授权、提交围栏、创建待付款订单或支付能力。第一趟与任何真实提交函数继续静态隔离。
- 原始完整 XML 只保存在 `%LOCALAPPDATA%\cmbuyer\artifacts\T-104\...`,不得上传、提交 Git、写入
Vikunja 或日志。Git 中最小 fixture 只能保留退出判据所需结构,并由人确认不含地址、手机号或支付凭据。
- 所有正常、失败、超时和 finally 调和路径的 Back 总数都必须小于等于 1;Back 结果不明不重试。
只有规格选择、闸门一读价、规格面板截图和严格安全退出全部成功,runner 才能原子发布结果与
`safe_exit=completed`;旧的空 hierarchy success 测试必须改成失败反例。
- 供 T-306 消费的 `SkuSelectionRunResult` 必须显式携带规格面板原始 `screenshot_path` 与截图 RPC
成功附近取得的 UTC-aware `captured_at`,并把同一个时间值写入 manifest。不得枚举目录或读取
manifest 猜文件/时间;post-exit 商品详情截图必须使用独立文件名和角色,绝不能冒充 Gate1 证据。
- `needs_device: true`:agent 不得自行标 `DONE`。只有人确认 post-exit 截图为同一目标商品详情、面板
已关闭、未进入确认/提交/支付页、未创建订单,且截图/XML 对应后,任务才可完成。
- 本任务不实现、不调用 `set_quantity_and_readback()`、`go_to_order_confirm()`、
`create_submission_fence()`、`submit_order_once()`、支付、免密支付、先用后付或任何扣款能力;既有
三道价格闸门、服务端围栏、只点一次且不重试的约束不得放宽。