fix(android): PDD 截短的规格显示文字被拆成独立选项,导致正式采购无法确认颜色选中 #362

Open
opened 2026-10-06 17:25:23 +08:00 by ila · 2 comments
Owner

基本信息

  • 类型:缺陷(Android Agent 规格解析与选中确认)。
  • 阶段:代码及自动化验证完成,已按用户后续授权构建并覆盖安装,待真机验收;未合并 main,未发布线上,未执行真实采购。
  • 来源:用户报告「#361 新版 APK 安装后重试采购 #1789 成功,重试采购 #1783 失败」,并保留设备现场供分析。
  • 关联:#361(规格探测点击保护与诊断)、#332/task 535(同一选项块多节点合并)。

前置依赖与交付边界

  • 代码审核已对照 main 64f0e49 与 #361 实现 a49dc69(文档提交 75ab256);本问题涉及的 optionBlock 和选中确认逻辑未被 #361 改动,故障在旧版本也存在。不能仅凭错误码相同断言 #361 对所有场景均无影响。
  • 只影响 Android 规格解析及必要测试、对应长期 Wiki,不改 Server/Web、业务数据库、AI 匹配、规则数据、人工映射及下单流程;第一步不修改选中确认算法。
  • 解析分析与合成回归可独立开展;集成/装机的 APK 必须包含 #361。实施前核对 #361 是否已合并:已合并则从最新 origin/main 建本单分支;未合并则先明确已授权的集成方式,不擅自合并 main,不发布缺少 #361 的 APK。
  • #361 诊断写入疑点不混入本单修复;须核对对应分支、设备版本和执行时间,未合并 main 本身不能解释安装了分支 APK 的设备为何无记录。

核验基线与事实

  • 代码基线:origin/main 64f0e49;设备 7「采购2」PLY110(ColorOS),Agent 0.9.68(#361 分支构建,versionCode 81);#361 尚未合并 main。
  • 服务端(只读):#1783 为 live、syb_order,目标「黑色 / 3XL」,spec_source=manual_mapping,映射为颜色「黑色长裤【有抽绳】 有口袋不起球」、尺码「3XL 170-185斤」。
    • attempt 1(15:56,spec_probe)completed;
    • attempt 2(15:57,旧版 0.9.64)以及 3、4、5(17:16~17:18,新版 0.9.68)均为 purchase 阶段失败,PURCHASE_SPEC_SELECTION_UNCONFIRMED「无障碍点击和受控手势后未能确认精确选中状态」,每次约 15 秒;未下单。
    • 该故障在 #361 之前已存在;相关解析和确认代码在 #361 未改动。是否还有其他影响不能仅凭相同错误码排除。
  • 现场界面(只读界面结构,分析后已删除,未保存):
    • 摘要「请选择: 尺码」;目标颜色卡片处于 selected;尺码区在列表视口外,从未被滚出。
    • 只滑动不点击露出尺码区后确认:「3XL 170-185斤」存在、enabled、未选中,与映射完全一致。
    • 同一颜色卡片中:外层 ViewGroup 和图片的标签为完整的「黑色长裤【有抽绳】 有口袋不起球 ¥18.8」;卡片下方的 TextView 文字被 PDD 截短为「黑色长裤【有抽绳】 有口袋 ¥18.8」。三者 selected 均为 true。
    • 其他卡片同理,例如「黑色长裤【无抽绳】 有口袋不 ¥16.8」(截断在半个词)。
  • attempt 1 的探测结果上报了 10 个颜色:5 个真实颜色,加 5 个截短后的「影子」,例如「黑色长裤【有抽绳】 有口袋」「黑色长裤【无抽绳】 有口袋不」「黑色七分裤【无抽绳】 有口袋」。尺码 7 个均正常。
  • 对照:#1789 的颜色名短(深灰色 / 白色 / 黑色),卡片内各节点文字一致,正常选中并已下单。

根因(代码证据 + 现场数据)

  1. 截短的显示文字没有并入所属选项块。 PddProductDetailCollector.kt:591 optionBlock 只有在块内所有候选成员的文字都以外层块完整标签开头时才合并(:609 members.any { !compact(it.label).startsWith(outer) } 即 break)。PDD 把显示文字截短后,「…有口袋¥18.8」不以「…有口袋不起球¥18.8」开头,合并失败,截短的 TextView 成为独立选项(:390 分组、:420 按 rawText 合并后仍保留),经 normalizeColor 去价格后成为「黑色长裤【有抽绳】 有口袋」。
  2. 影子选项被选中,导致当前样例的即时确认持续失败。 PurchaseRehearsalExecutor.kt:690 isExactSpecSelected 第一条规则(:692):同一维度只要有任一「text ≠ target 且 selected/checked」的候选,就判定未选中。选中真实颜色后,影子选项也是 selected,于是点击和手势兜底后都确认失败,报 PURCHASE_SPEC_SELECTION_UNCONFIRMED,流程停在颜色一步,没有进入尺码。最终复核 verifyExactSpecSelection 也有冲突检查,但它先接受唯一目标已选中;两者执行顺序不同,按 v2 方案分别记录边界。
  3. 文字截短是 PDD 的行为:卡片宽度固定,需要同时显示「规格名 + 价格」,名字过长时 PDD 在设置文字前就截短(读到的 text 属性本身就是截短版,不是显示层的省略号),而无障碍描述保留完整名。规格名越长越容易触发。

影响

  • 出现“截短文字被解析为独立候选且同步 selected/checked”的结构时,可能导致正式采购颜色确认持续失败;不泛化为所有截短文字商品必然失败。
  • 规格探测和采集会上报多余的「影子」规格值,可能误导人工映射和 AI 匹配,例如映射到截短值。

方案(v2:先修解析去重,再验证确认逻辑)

2026-10-06 最初授权仅更新方案;随后用户明确授权 v2.1 实施,并在实施中追加授权构建 APK 后安装到指定设备。合并 main、线上发布和真实采购不在本次授权内。

  1. 第一步只修同一卡片内的解析去重。 在 optionBlock 既有合并能力上兼容“完整外层标签/图片标签 + 截短显示文字”。尾部价格处理复用 SpecValueNormalizer,空白压缩只用于块内身份比较;归一后两侧均须非空。成员是外层完整名的前缀时,只能作为同一卡片的重复显示证据,不放宽为任意包含关系。最终规格名保留外层完整标签按现有规则去价格后的值,不把比较用的去空白文本变成下发目标。
  2. 保留已有合并分支。 不能用“外层以成员开头”简单替换原判断;已有“成员以外层开头”的完整标签+附加说明/徽标场景仍需兼容并回归。不得扩大尾部价格清理到任意数字、推荐体重或款式文字。
  3. 同一外层容器不等于同一选项块。 合并须有可复核的单一卡片结构证据,不能仅凭共享可点击祖先及前缀关系认定同一规格。共同容器下面如果存在两个独立规格卡片(包括嵌套可点击块),必须保留为两个值;无法确认归属时不合并。不跨卡片、不跨维度。
  4. 先不修改选中确认。 用合成树验证解析去重后,现有 isExactSpecSelected、选中证明及最终复核能否正常完成;若能通过,本单不改这些判断。取消原方案中“直接排除目标点击节点子树内选中项”的做法:当前 VisibleSpecValue.node 由 safeOptionRank 选择,可能是图片或文字,其他显示节点可能是它的兄弟节点,并不位于它的子树。
  5. 如确认仍有残余失败,先补证据再决定最小防御。 只能依据解析器确认的同一选项块身份处理重复成员,不能用点击节点路径前缀泛化排除。先记录失败用例、必要的块身份传递和影响,再更新本单设计;涉及确认行为扩大时先确认,不默认调整所有采购复核逻辑。
  6. 精确定位不变。 不改 locate 的精确匹配、服务端下发目标或最终点击对象选择规则,不引入模糊匹配、相似值点击或根据截短词猜测完整商品规格。

v2.1 用户确认补充(2026-10-06,开始实施)

用户确认:「先把这两点补进 #362,再按最小范围实施」。

  1. 区分卡片自身显示节点与独立卡片。 接受评论 8879 的严格非空前缀、双尾价一致和结构证据方向,但不能机械要求“无任何带标签可点击子节点”:同一卡片的图片、完整标签和截短字幕本身可能都可点击。应保留这些重复显示叶节点,拒绝跨越另一个有自身标签/显示成员的独立规格卡片。合成测试同时覆盖正常图片/字幕同卡,以及共同父容器、嵌套独立卡片、同价前缀独立卡片;不确定归属时不合并。复用现有尾价规范化,不改变完整规格身份、点击对象选择及选中确认算法。
  2. #361 合并与验收分开。 诊断未写入仍是未核实项,不因另一个商品采购成功就视为 #361 整体验收通过,也不凭 Agent 消息自动合并。本单可先从当前 origin/main 64f0e49 建独立分支完成解析代码、单元测试及编译验证;#361 尚未合并,集成/装机 APK 必须包含其修复,集成方式与装机留待明确授权。本次不构建用于覆盖手机的 main-only APK,不改 #361 诊断、不触发真实订单。
  3. 本次恢复既有解析行为,无 UI/接口/数据迁移;设计证据为本单 v2.1 算法、反例及用户确认,不要求额外界面原型。当前无 Gitea MCP,按规则回退 REST API,凭据从用户级私有配置读取。

选中冲突的现有行为边界

isExactSpecSelected 先检查其他值选中,再接受目标;但 verifyExactSpecSelection 当前先接受唯一目标已选中,之后才检查其他值冲突。因此“目标和另一张卡片同时 selected 时,所有复核路径都会拒绝”不是已证实的现有能力。

本单需记录即时确认与最终复核的基线差异,并保证解析合并不隐藏真实独立卡片。若要统一最终复核检查顺序,属于额外行为决策,先记录并确认,不在本单默认顺手修改,也不把已有提前返回当作本次验收通过。

不做

  • 不改 Server、Web、数据库、AI 匹配、人工映射数据和下单流程。
  • 不清理历史探测数据和已有映射(如有映射到截短值的情况,另行只读统计后再议)。
  • 不用 OCR/VLM,不保存原始控件树或整屏截图。

风险

  • 前缀只证明文字关系,不能独立证明同一 SKU。相邻卡片“黑色 / 黑色加绒”,以及同一有标签的可点击外层容器内两个独立规格,都必须保持独立;空标签、只有价格、缺少完整标签等不能利用空前缀合并。
  • optionBlock 为颜色和尺码共用,修改不能误并带体重说明的尺码、数字尺码或不同款式;先以当前颜色故障为目标,不扩展为通用规格模糊归一化。
  • 不使用泛化子树排除掩盖错选;若第一步解析修复足以恢复采购,就保留原确认算法。
  • 历史已映射到截短值的任务,在新解析只返回完整值后可能找不到精确目标。本单不得将其静默改为完整值;保留明确失败,不改任务快照、人工映射或历史探测结果,后续另做只读统计及授权处理。
  • 演练任务按 PDD 商品建立,不自动绑定原 SYB 明细,且档案匹配可能跳过 spec_probe。真机验收应分别确认探测去重和 purchase 选中路径确实覆盖;不能把“演练完成”当成两者均已测试。正式任务探测成功后可能继续下单,装机/真机验证需授权;正式创建订单需单独授权,永久不付款。

验收标准

  • 合成树:外层块和图片为完整标签、文字为截短版且均 selected;解析仅得到1个完整规格,无影子值;探测与普通采集都不新增截短重复项。
  • 解析去重后,目标颜色在现有 isExactSpecSelected 中确认成功,并能继续精确选择目标尺码及完成演练复核。若无需确认层改动,明确记录“确认算法保持不变”。
  • 相邻“黑色 / 黑色加绒”保持两个值;共同外层容器(包括带标签、可点击的容器)内部两个独立规格也不合并;不跨维度。
  • 合并后保持各卡片真实 selected/checked 状态;仅其他卡片选中、以及目标与其他卡片同时选中,都有测试。即时确认应继续识别独立卡片冲突;最终复核已有提前返回差异单独记录,不将其声称为本次已修复,也不新增排除逻辑掩盖它。
  • “完整标签+附加说明/徽标”的原合并场景、短名商品、体重说明尺码、数字尺码及 #332/task 535 场景回归;尾部价格清理使用现有方法,不更改原始完整目标语义。
  • 空标签、纯价格、无完整标签证据、非前缀不同文案不误并;历史截短目标不能通过模糊定位被悄悄改选完整目标。
  • 构建包含 #361 修复,保留其等待/跳过预滑/尺码建议拒绝及诊断代码;明确提交基线和 APK 版本,不从缺少 #361 的 main 单独构建覆盖新版。
  • 用户另行授权后,正常故障样例商品能选中预期完整颜色和尺码并通过确认;同时分别验证探测候选不再有影子值。保护性失败不算正常商品修复成功。
  • 测试结果、未验证项、提交哈希、APK 版本回写工单,待用户验收;不提前关闭。

验证方式

  • 先做解析合成树和即时/最终确认基线测试,证明根因及反例,再实施最小解析修复;自动化测试不得创建真实订单。
  • Android 单元测试覆盖采集器、执行器及 #361 回归,运行 scripts/verify.ps1 -Component android 或等效测试/构建,真实记录未通过项。
  • 真机验证需另行授权;演练和正式任务的阶段/安全边界按本单风险章节执行。不修改线上商品、规则或历史任务来强行走测试分支。
  • #361 诊断是否实际写入属于独立核查,不因其尚待核实而省略 #362 的结构化回归测试;如真机无法读取必要证据,应明确保留未验证项,不宣称修复验收通过。

文档影响

  • 业务规则:补充「选项显示文字可能被 PDD 截短;同一选项块内以完整标签为准」。
  • 如诊断或排障方式有变化,同步 Troubleshooting。先改线上 Wiki 并回读 revision,再执行一次 sync 与 sync --check。

附:#361 验收相关的观察(不属本单范围)

0.9.68 安装后,设备上的 databases/goauto_diagnostics.db 修改时间仍为 2026-09-12,表中最新记录是 0.9.59 写入的;#1789 在 17:15 的规格探测按 #361 本应写入点击诊断,实际没有任何新记录。可能是诊断表升级失败或写入被 SafeAgentDiagnosticRecorder 吞掉,请在 #361 中排查。

备注

  • 本单由 Claude Code 依据只读查询、代码阅读和设备现场只读分析整理;建单使用 Gitea REST API(当前会话无 Gitea MCP)。现场界面结构和导出的诊断库副本已删除,工单不含收货人、地址、订单号等个人信息。

  • 2026-10-06 Codex 按用户授权将审核结论整合入正文;当前无 Gitea MCP 工具,回退 Gitea REST API,凭据仅从本机私有配置读取。本次未修改代码、数据、Wiki,未实施/安装/发布。

实施与交付(2026-10-06)

授权变化与依赖处理

  • 用户在实施中追加:「做完 #362 后,构建 APK 后安装到 192.168.0.9:37755」。
  • 为保留手机现有 #361,已在 #362 工单分支集成 origin/fix/361-spec-probe-click 的 75ab256(含实现 a49dc69),合并提交 35119ea;没有合并 main,没有改变 #361 的诊断逻辑,更不表示 #361 整体验收通过。前文 v2.1 的“暂不装机”是追加授权前边界,以本节为准。
  • 工单分支 fix/362-truncated-spec-card 已推送;worktree D:/OPC/goauto-worktrees/issue-362 未合并,按规则保留。主目录用户改动未动。

最小实现

  • 9a4495a:完整外层/图片标签 + 截短字幕的同卡去重,复用尾价语法。新增兼容只认同卡内一个完整图片叶节点和一个截短文字叶节点、合理边界/上下排列;非空严格前缀,已出现的尾价数值一致。无价格不替代结构证据;嵌套独立卡片或不明结构不放宽。
  • 1f5bab8:验收反例发现共同容器“黑色”可被原前缀分支用于吞并“黑色加绒”子卡片。补充同一解析函数的结构边界:含不同名称、可点击、有自身子节点的卡片时,外层容器既不成为规格值,也不能成为合并块,避免其聚合选中状态污染其他卡片。原同名包装/徽标分支通过回归。
  • 正式确认层没有修改:isExactSpecSelected、selectionProof、verifyExactSpecSelection 与 #361 一致;下单、地址、支付、AI、Server/Web、接口及数据库无本单改动。旧的即时/最终多选冲突顺序差异通过测试固定为基线,不声称修复。
  • 新增 TruncatedSpecCardTest 20 项合成测试(不是真机树):正常去重、价格与空值边界、独立/嵌套/共同容器、selected/checked、徽标与尺码保留、共享采集输出、颜色确认后实际模拟点击尺码并完成演练、旧截短目标仍精确失败。只采用人工构造的示例数据。

验证

  • 最终 #362 解析实现基线 1f5bab8:完整 testDebugUnitTest 49 套 / 483 项,0 失败、0 错误、0 跳过,耗时 4m40s。
  • 集成 #361 并升级版本后的 35119ea:重新执行采集器、采购执行器、截短卡片、旧去重、规格归一化、尺码建议点击保护、诊断/迁移、无障碍策略、当前页采集回归,11 套 / 232 项,0 失败、0 错误、0 跳过;assembleDebug 成功。此处是集成后的受影响范围回归,不将之前完整测试冒充集成版本全量测试。
  • git diff --check 通过。harness check --strict 仍有 main 原有错误:docs/evidence/pdd-home-35727-summary.md 未登记 Wiki 镜像;该文件与 origin/main blob 均为 41f74c4a467062e109d61ad1ac7f52b12755455e。本单不修改无关登记。
  • Wiki 业务规则已在线更新并回读 revision 81cec55e15c968efcc2cb4c4e5babcd17a40a3f4,记录解析边界与授权后的集成版本;最终镜像检查及文档提交见完工评论。后续真实反例补齐、用户追加安装授权造成文档事实变化,因此在这些变化点更新镜像;没有手改镜像反推 Wiki。同步保留线上已有 #361 架构/排障说明,不宣称其诊断问题已解决。

APK 与安装

  • Agent 0.9.69 / versionCode 82,同时包含 #361 与 #362;不从缺少 #361 的 main-only 构建覆盖手机。
  • APK:D:/OPC/goauto-worktrees/issue-362/android/app/build/outputs/apk/debug/app-debug.apk
  • SHA-256:B58A351BD21D4E61C8C1AB7942D26D5C2E555B726B3C1937EEDD9D087392F7BA
  • 安装前两次只读核对设备 ONLINE 且无 current_task_id;使用 adb install -r,返回 Success,保留应用数据。设备回读 versionName=0.9.69、versionCode=82、lastUpdateTime=2026-10-06 18:01:02。
  • 未启动采购、未重试任务、未修改地址、未创建订单、未付款。真机目标颜色/尺码结果以及 #361 诊断实际写入仍未验证,工单保持打开待验收。
## 基本信息 - 类型:缺陷(Android Agent 规格解析与选中确认)。 - 阶段:代码及自动化验证完成,已按用户后续授权构建并覆盖安装,待真机验收;未合并 main,未发布线上,未执行真实采购。 - 来源:用户报告「#361 新版 APK 安装后重试采购 #1789 成功,重试采购 #1783 失败」,并保留设备现场供分析。 - 关联:#361(规格探测点击保护与诊断)、#332/task 535(同一选项块多节点合并)。 ## 前置依赖与交付边界 - 代码审核已对照 main `64f0e49` 与 #361 实现 `a49dc69`(文档提交 `75ab256`);本问题涉及的 `optionBlock` 和选中确认逻辑未被 #361 改动,故障在旧版本也存在。不能仅凭错误码相同断言 #361 对所有场景均无影响。 - 只影响 Android 规格解析及必要测试、对应长期 Wiki,不改 Server/Web、业务数据库、AI 匹配、规则数据、人工映射及下单流程;第一步不修改选中确认算法。 - 解析分析与合成回归可独立开展;集成/装机的 APK 必须包含 #361。实施前核对 #361 是否已合并:已合并则从最新 origin/main 建本单分支;未合并则先明确已授权的集成方式,不擅自合并 main,不发布缺少 #361 的 APK。 - #361 诊断写入疑点不混入本单修复;须核对对应分支、设备版本和执行时间,未合并 main 本身不能解释安装了分支 APK 的设备为何无记录。 ## 核验基线与事实 - 代码基线:origin/main `64f0e49`;设备 7「采购2」PLY110(ColorOS),Agent 0.9.68(#361 分支构建,versionCode 81);#361 尚未合并 main。 - 服务端(只读):#1783 为 live、syb_order,目标「黑色 / 3XL」,spec_source=manual_mapping,映射为颜色「黑色长裤【有抽绳】 有口袋不起球」、尺码「3XL 170-185斤」。 - attempt 1(15:56,spec_probe)completed; - attempt 2(15:57,旧版 0.9.64)以及 3、4、5(17:16~17:18,新版 0.9.68)均为 purchase 阶段失败,`PURCHASE_SPEC_SELECTION_UNCONFIRMED`「无障碍点击和受控手势后未能确认精确选中状态」,每次约 15 秒;未下单。 - **该故障在 #361 之前已存在;相关解析和确认代码在 #361 未改动。是否还有其他影响不能仅凭相同错误码排除。** - 现场界面(只读界面结构,分析后已删除,未保存): - 摘要「请选择: 尺码」;目标颜色卡片处于 selected;尺码区在列表视口外,从未被滚出。 - 只滑动不点击露出尺码区后确认:「3XL 170-185斤」存在、enabled、未选中,与映射完全一致。 - 同一颜色卡片中:外层 ViewGroup 和图片的标签为完整的「黑色长裤【有抽绳】 有口袋不起球 ¥18.8」;**卡片下方的 TextView 文字被 PDD 截短**为「黑色长裤【有抽绳】 有口袋 ¥18.8」。三者 selected 均为 true。 - 其他卡片同理,例如「黑色长裤【无抽绳】 有口袋不 ¥16.8」(截断在半个词)。 - attempt 1 的探测结果上报了 **10 个颜色**:5 个真实颜色,加 5 个截短后的「影子」,例如「黑色长裤【有抽绳】 有口袋」「黑色长裤【无抽绳】 有口袋不」「黑色七分裤【无抽绳】 有口袋」。尺码 7 个均正常。 - 对照:#1789 的颜色名短(深灰色 / 白色 / 黑色),卡片内各节点文字一致,正常选中并已下单。 ## 根因(代码证据 + 现场数据) 1. **截短的显示文字没有并入所属选项块。** `PddProductDetailCollector.kt:591` `optionBlock` 只有在块内所有候选成员的文字都**以外层块完整标签开头**时才合并(:609 `members.any { !compact(it.label).startsWith(outer) }` 即 break)。PDD 把显示文字截短后,「…有口袋¥18.8」不以「…有口袋不起球¥18.8」开头,合并失败,截短的 TextView 成为独立选项(:390 分组、:420 按 rawText 合并后仍保留),经 `normalizeColor` 去价格后成为「黑色长裤【有抽绳】 有口袋」。 2. **影子选项被选中,导致当前样例的即时确认持续失败。** `PurchaseRehearsalExecutor.kt:690` `isExactSpecSelected` 第一条规则(:692):同一维度只要有任一「text ≠ target 且 selected/checked」的候选,就判定未选中。选中真实颜色后,影子选项也是 selected,于是点击和手势兜底后都确认失败,报 `PURCHASE_SPEC_SELECTION_UNCONFIRMED`,流程停在颜色一步,没有进入尺码。最终复核 `verifyExactSpecSelection` 也有冲突检查,但它先接受唯一目标已选中;两者执行顺序不同,按 v2 方案分别记录边界。 3. 文字截短是 PDD 的行为:卡片宽度固定,需要同时显示「规格名 + 价格」,名字过长时 PDD 在设置文字前就截短(读到的 text 属性本身就是截短版,不是显示层的省略号),而无障碍描述保留完整名。规格名越长越容易触发。 ## 影响 - 出现“截短文字被解析为独立候选且同步 selected/checked”的结构时,可能导致正式采购颜色确认持续失败;不泛化为所有截短文字商品必然失败。 - 规格探测和采集会上报多余的「影子」规格值,可能误导人工映射和 AI 匹配,例如映射到截短值。 ## 方案(v2:先修解析去重,再验证确认逻辑) 2026-10-06 最初授权仅更新方案;随后用户明确授权 v2.1 实施,并在实施中追加授权构建 APK 后安装到指定设备。合并 main、线上发布和真实采购不在本次授权内。 1. **第一步只修同一卡片内的解析去重。** 在 `optionBlock` 既有合并能力上兼容“完整外层标签/图片标签 + 截短显示文字”。尾部价格处理复用 `SpecValueNormalizer`,空白压缩只用于块内身份比较;归一后两侧均须非空。成员是外层完整名的前缀时,只能作为同一卡片的重复显示证据,不放宽为任意包含关系。最终规格名保留外层完整标签按现有规则去价格后的值,不把比较用的去空白文本变成下发目标。 2. **保留已有合并分支。** 不能用“外层以成员开头”简单替换原判断;已有“成员以外层开头”的完整标签+附加说明/徽标场景仍需兼容并回归。不得扩大尾部价格清理到任意数字、推荐体重或款式文字。 3. **同一外层容器不等于同一选项块。** 合并须有可复核的单一卡片结构证据,不能仅凭共享可点击祖先及前缀关系认定同一规格。共同容器下面如果存在两个独立规格卡片(包括嵌套可点击块),必须保留为两个值;无法确认归属时不合并。不跨卡片、不跨维度。 4. **先不修改选中确认。** 用合成树验证解析去重后,现有 `isExactSpecSelected`、选中证明及最终复核能否正常完成;若能通过,本单不改这些判断。取消原方案中“直接排除目标点击节点子树内选中项”的做法:当前 `VisibleSpecValue.node` 由 `safeOptionRank` 选择,可能是图片或文字,其他显示节点可能是它的兄弟节点,并不位于它的子树。 5. **如确认仍有残余失败,先补证据再决定最小防御。** 只能依据解析器确认的同一选项块身份处理重复成员,不能用点击节点路径前缀泛化排除。先记录失败用例、必要的块身份传递和影响,再更新本单设计;涉及确认行为扩大时先确认,不默认调整所有采购复核逻辑。 6. **精确定位不变。** 不改 `locate` 的精确匹配、服务端下发目标或最终点击对象选择规则,不引入模糊匹配、相似值点击或根据截短词猜测完整商品规格。 ### v2.1 用户确认补充(2026-10-06,开始实施) 用户确认:「先把这两点补进 #362,再按最小范围实施」。 1. **区分卡片自身显示节点与独立卡片。** 接受评论 8879 的严格非空前缀、双尾价一致和结构证据方向,但不能机械要求“无任何带标签可点击子节点”:同一卡片的图片、完整标签和截短字幕本身可能都可点击。应保留这些重复显示叶节点,拒绝跨越另一个有自身标签/显示成员的独立规格卡片。合成测试同时覆盖正常图片/字幕同卡,以及共同父容器、嵌套独立卡片、同价前缀独立卡片;不确定归属时不合并。复用现有尾价规范化,不改变完整规格身份、点击对象选择及选中确认算法。 2. **#361 合并与验收分开。** 诊断未写入仍是未核实项,不因另一个商品采购成功就视为 #361 整体验收通过,也不凭 Agent 消息自动合并。本单可先从当前 origin/main 64f0e49 建独立分支完成解析代码、单元测试及编译验证;#361 尚未合并,集成/装机 APK 必须包含其修复,集成方式与装机留待明确授权。本次不构建用于覆盖手机的 main-only APK,不改 #361 诊断、不触发真实订单。 3. 本次恢复既有解析行为,无 UI/接口/数据迁移;设计证据为本单 v2.1 算法、反例及用户确认,不要求额外界面原型。当前无 Gitea MCP,按规则回退 REST API,凭据从用户级私有配置读取。 ### 选中冲突的现有行为边界 `isExactSpecSelected` 先检查其他值选中,再接受目标;但 `verifyExactSpecSelection` 当前先接受唯一目标已选中,之后才检查其他值冲突。因此“目标和另一张卡片同时 selected 时,所有复核路径都会拒绝”不是已证实的现有能力。 本单需记录即时确认与最终复核的基线差异,并保证解析合并不隐藏真实独立卡片。若要统一最终复核检查顺序,属于额外行为决策,先记录并确认,不在本单默认顺手修改,也不把已有提前返回当作本次验收通过。 ## 不做 - 不改 Server、Web、数据库、AI 匹配、人工映射数据和下单流程。 - 不清理历史探测数据和已有映射(如有映射到截短值的情况,另行只读统计后再议)。 - 不用 OCR/VLM,不保存原始控件树或整屏截图。 ## 风险 - 前缀只证明文字关系,不能独立证明同一 SKU。相邻卡片“黑色 / 黑色加绒”,以及同一有标签的可点击外层容器内两个独立规格,都必须保持独立;空标签、只有价格、缺少完整标签等不能利用空前缀合并。 - `optionBlock` 为颜色和尺码共用,修改不能误并带体重说明的尺码、数字尺码或不同款式;先以当前颜色故障为目标,不扩展为通用规格模糊归一化。 - 不使用泛化子树排除掩盖错选;若第一步解析修复足以恢复采购,就保留原确认算法。 - 历史已映射到截短值的任务,在新解析只返回完整值后可能找不到精确目标。本单不得将其静默改为完整值;保留明确失败,不改任务快照、人工映射或历史探测结果,后续另做只读统计及授权处理。 - 演练任务按 PDD 商品建立,不自动绑定原 SYB 明细,且档案匹配可能跳过 spec_probe。真机验收应分别确认探测去重和 purchase 选中路径确实覆盖;不能把“演练完成”当成两者均已测试。正式任务探测成功后可能继续下单,装机/真机验证需授权;正式创建订单需单独授权,永久不付款。 ## 验收标准 - [x] 合成树:外层块和图片为完整标签、文字为截短版且均 selected;解析仅得到1个完整规格,无影子值;探测与普通采集都不新增截短重复项。 - [x] 解析去重后,目标颜色在现有 `isExactSpecSelected` 中确认成功,并能继续精确选择目标尺码及完成演练复核。若无需确认层改动,明确记录“确认算法保持不变”。 - [x] 相邻“黑色 / 黑色加绒”保持两个值;共同外层容器(包括带标签、可点击的容器)内部两个独立规格也不合并;不跨维度。 - [x] 合并后保持各卡片真实 selected/checked 状态;仅其他卡片选中、以及目标与其他卡片同时选中,都有测试。即时确认应继续识别独立卡片冲突;最终复核已有提前返回差异单独记录,不将其声称为本次已修复,也不新增排除逻辑掩盖它。 - [x] “完整标签+附加说明/徽标”的原合并场景、短名商品、体重说明尺码、数字尺码及 #332/task 535 场景回归;尾部价格清理使用现有方法,不更改原始完整目标语义。 - [x] 空标签、纯价格、无完整标签证据、非前缀不同文案不误并;历史截短目标不能通过模糊定位被悄悄改选完整目标。 - [x] 构建包含 #361 修复,保留其等待/跳过预滑/尺码建议拒绝及诊断代码;明确提交基线和 APK 版本,不从缺少 #361 的 main 单独构建覆盖新版。 - [ ] 用户另行授权后,正常故障样例商品能选中预期完整颜色和尺码并通过确认;同时分别验证探测候选不再有影子值。保护性失败不算正常商品修复成功。 - [x] 测试结果、未验证项、提交哈希、APK 版本回写工单,待用户验收;不提前关闭。 ## 验证方式 - 先做解析合成树和即时/最终确认基线测试,证明根因及反例,再实施最小解析修复;自动化测试不得创建真实订单。 - Android 单元测试覆盖采集器、执行器及 #361 回归,运行 `scripts/verify.ps1 -Component android` 或等效测试/构建,真实记录未通过项。 - 真机验证需另行授权;演练和正式任务的阶段/安全边界按本单风险章节执行。不修改线上商品、规则或历史任务来强行走测试分支。 - #361 诊断是否实际写入属于独立核查,不因其尚待核实而省略 #362 的结构化回归测试;如真机无法读取必要证据,应明确保留未验证项,不宣称修复验收通过。 ## 文档影响 - 业务规则:补充「选项显示文字可能被 PDD 截短;同一选项块内以完整标签为准」。 - 如诊断或排障方式有变化,同步 Troubleshooting。先改线上 Wiki 并回读 revision,再执行一次 sync 与 sync --check。 ## 附:#361 验收相关的观察(不属本单范围) 0.9.68 安装后,设备上的 `databases/goauto_diagnostics.db` 修改时间仍为 2026-09-12,表中最新记录是 0.9.59 写入的;#1789 在 17:15 的规格探测按 #361 本应写入点击诊断,实际没有任何新记录。可能是诊断表升级失败或写入被 `SafeAgentDiagnosticRecorder` 吞掉,请在 #361 中排查。 ## 备注 - 本单由 Claude Code 依据只读查询、代码阅读和设备现场只读分析整理;建单使用 Gitea REST API(当前会话无 Gitea MCP)。现场界面结构和导出的诊断库副本已删除,工单不含收货人、地址、订单号等个人信息。 - 2026-10-06 Codex 按用户授权将审核结论整合入正文;当前无 Gitea MCP 工具,回退 Gitea REST API,凭据仅从本机私有配置读取。本次未修改代码、数据、Wiki,未实施/安装/发布。 ## 实施与交付(2026-10-06) ### 授权变化与依赖处理 - 用户在实施中追加:「做完 #362 后,构建 APK 后安装到 192.168.0.9:37755」。 - 为保留手机现有 #361,已在 **#362 工单分支**集成 origin/fix/361-spec-probe-click 的 75ab256(含实现 a49dc69),合并提交 35119ea;没有合并 main,没有改变 #361 的诊断逻辑,更不表示 #361 整体验收通过。前文 v2.1 的“暂不装机”是追加授权前边界,以本节为准。 - 工单分支 fix/362-truncated-spec-card 已推送;worktree D:/OPC/goauto-worktrees/issue-362 未合并,按规则保留。主目录用户改动未动。 ### 最小实现 - 9a4495a:完整外层/图片标签 + 截短字幕的同卡去重,复用尾价语法。新增兼容只认同卡内一个完整图片叶节点和一个截短文字叶节点、合理边界/上下排列;非空严格前缀,已出现的尾价数值一致。无价格不替代结构证据;嵌套独立卡片或不明结构不放宽。 - 1f5bab8:验收反例发现共同容器“黑色”可被原前缀分支用于吞并“黑色加绒”子卡片。补充同一解析函数的结构边界:含不同名称、可点击、有自身子节点的卡片时,外层容器既不成为规格值,也不能成为合并块,避免其聚合选中状态污染其他卡片。原同名包装/徽标分支通过回归。 - 正式确认层没有修改:isExactSpecSelected、selectionProof、verifyExactSpecSelection 与 #361 一致;下单、地址、支付、AI、Server/Web、接口及数据库无本单改动。旧的即时/最终多选冲突顺序差异通过测试固定为基线,不声称修复。 - 新增 TruncatedSpecCardTest 20 项合成测试(不是真机树):正常去重、价格与空值边界、独立/嵌套/共同容器、selected/checked、徽标与尺码保留、共享采集输出、颜色确认后实际模拟点击尺码并完成演练、旧截短目标仍精确失败。只采用人工构造的示例数据。 ### 验证 - 最终 #362 解析实现基线 1f5bab8:完整 testDebugUnitTest **49 套 / 483 项,0 失败、0 错误、0 跳过**,耗时 4m40s。 - 集成 #361 并升级版本后的 35119ea:重新执行采集器、采购执行器、截短卡片、旧去重、规格归一化、尺码建议点击保护、诊断/迁移、无障碍策略、当前页采集回归,**11 套 / 232 项,0 失败、0 错误、0 跳过**;assembleDebug 成功。此处是集成后的受影响范围回归,不将之前完整测试冒充集成版本全量测试。 - git diff --check 通过。harness check --strict 仍有 main 原有错误:docs/evidence/pdd-home-35727-summary.md 未登记 Wiki 镜像;该文件与 origin/main blob 均为 41f74c4a467062e109d61ad1ac7f52b12755455e。本单不修改无关登记。 - Wiki 业务规则已在线更新并回读 revision 81cec55e15c968efcc2cb4c4e5babcd17a40a3f4,记录解析边界与授权后的集成版本;最终镜像检查及文档提交见完工评论。后续真实反例补齐、用户追加安装授权造成文档事实变化,因此在这些变化点更新镜像;没有手改镜像反推 Wiki。同步保留线上已有 #361 架构/排障说明,不宣称其诊断问题已解决。 ### APK 与安装 - Agent **0.9.69 / versionCode 82**,同时包含 #361 与 #362;不从缺少 #361 的 main-only 构建覆盖手机。 - APK:D:/OPC/goauto-worktrees/issue-362/android/app/build/outputs/apk/debug/app-debug.apk - SHA-256:B58A351BD21D4E61C8C1AB7942D26D5C2E555B726B3C1937EEDD9D087392F7BA - 安装前两次只读核对设备 ONLINE 且无 current_task_id;使用 adb install -r,返回 Success,保留应用数据。设备回读 versionName=0.9.69、versionCode=82、lastUpdateTime=2026-10-06 18:01:02。 - 未启动采购、未重试任务、未修改地址、未创建订单、未付款。真机目标颜色/尺码结果以及 #361 诊断实际写入仍未验证,工单保持打开待验收。
Author
Owner

v2 复核意见(2026-10-06,Claude Code,用户确认补充)

依据:SynapBus #goauto 消息 #65/#66,对照 origin/main 64f0e49 代码复核。

结论:v2 方案合理,可实施

已核实的要点:

  • verifyExactSpecSelection 先在 PurchaseRehearsalExecutor.kt:763 接受「唯一目标已选中」,再在 :766 检查冲突,与 isExactSpecSelected(:692 先查冲突)顺序不同。v2 只记录这一差异、不顺手修改,做法正确。
  • 代表节点由 safeOptionRank 选出,可能是图片,而文字节点是它的兄弟节点、不在其子树内。取消原方案中「排除点击节点子树内选中项」的做法是对的。
  • 先只修解析去重、不改确认逻辑:影子选项消失后 :692 自然不再误触发,改动最小。
  • 保留原有「成员以外层开头」的合并分支,不改成双向前缀,避免破坏徽标、附加说明等旧场景。

补充 1:把「单一卡片结构证据」写成可测试的条件

截短成员只有同时满足以下三条才能并入外层块,否则保持独立:

  1. 前缀:成员与外层完整标签都按现有方法去掉尾部价格并压缩空白后,两者均非空,且成员是外层的严格前缀。
  2. 价格一致:两边都带尾部价格时,价格必须相同(例:影子「…有口袋 ¥18.8」与外层「…有口袋不起球 ¥18.8」);一边有价格、另一边没有时不作为合并依据。
  3. 无中间子块:成员与外层块之间(以及外层块内部)不存在另一个带标签、可点击的子块。这一条用于防止「共同容器内两个独立规格」被误并,例如容器标签为「黑色加绒」、内部嵌套「黑色」卡片时,仅凭前缀会误合并。

以上三条各需正例和反例测试,并加入验收标准第 3、6 条的覆盖范围。

补充 2:尽早明确 #361 的依赖

v2 的写法是「#361 未合并时先确认集成方式」,这会让本单卡住。#1789 已用 #361 版本(0.9.68/81)成功下单,建议:

  • 用户先验收 #361 并合并进 main,本单再从最新 origin/main 拉分支;
  • #361 诊断未写入的问题继续在 #361 跟进,或另建工单,不阻塞合并。

此项需用户决定;用户决定前本单的解析修复与合成树测试可以先行开展,但不装机、不发布。

其他

验收项共 9 条,偏多,但都在本单范围内,没有越界,可以接受。

## v2 复核意见(2026-10-06,Claude Code,用户确认补充) 依据:SynapBus `#goauto` 消息 #65/#66,对照 origin/main `64f0e49` 代码复核。 ### 结论:v2 方案合理,可实施 已核实的要点: - `verifyExactSpecSelection` 先在 `PurchaseRehearsalExecutor.kt:763` 接受「唯一目标已选中」,再在 `:766` 检查冲突,与 `isExactSpecSelected`(`:692` 先查冲突)顺序不同。v2 只记录这一差异、不顺手修改,做法正确。 - 代表节点由 `safeOptionRank` 选出,可能是图片,而文字节点是它的兄弟节点、不在其子树内。取消原方案中「排除点击节点子树内选中项」的做法是对的。 - 先只修解析去重、不改确认逻辑:影子选项消失后 `:692` 自然不再误触发,改动最小。 - 保留原有「成员以外层开头」的合并分支,不改成双向前缀,避免破坏徽标、附加说明等旧场景。 ### 补充 1:把「单一卡片结构证据」写成可测试的条件 截短成员只有同时满足以下三条才能并入外层块,否则保持独立: 1. **前缀**:成员与外层完整标签都按现有方法去掉尾部价格并压缩空白后,两者均非空,且成员是外层的**严格前缀**。 2. **价格一致**:两边都带尾部价格时,价格必须相同(例:影子「…有口袋 ¥18.8」与外层「…有口袋不起球 ¥18.8」);一边有价格、另一边没有时不作为合并依据。 3. **无中间子块**:成员与外层块之间(以及外层块内部)不存在另一个带标签、可点击的子块。这一条用于防止「共同容器内两个独立规格」被误并,例如容器标签为「黑色加绒」、内部嵌套「黑色」卡片时,仅凭前缀会误合并。 以上三条各需正例和反例测试,并加入验收标准第 3、6 条的覆盖范围。 ### 补充 2:尽早明确 #361 的依赖 v2 的写法是「#361 未合并时先确认集成方式」,这会让本单卡住。#1789 已用 #361 版本(0.9.68/81)成功下单,建议: - 用户先验收 #361 并合并进 main,本单再从最新 origin/main 拉分支; - #361 诊断未写入的问题继续在 #361 跟进,或另建工单,不阻塞合并。 此项需用户决定;用户决定前本单的解析修复与合成树测试可以先行开展,但不装机、不发布。 ### 其他 验收项共 9 条,偏多,但都在本单范围内,没有越界,可以接受。
Author
Owner

完工回写:代码完成、已安装,待真机验收

  • 工单正文已整合 v2.1 两点及最终结构边界,实际方案、验证与授权变化见正文。
  • 分支 fix/362-truncated-spec-card 已推送至 279cb55,工作区干净;未合并 main。
  • 解析修复:9a4495a、1f5bab8;保留 #361 的集成及版本提交:35119ea;最终 Wiki 镜像提交:279cb55。采购执行器相对 #361 无改动。
  • #362 完整回归 483 项通过;集成后 232 项受影响回归及 assembleDebug 通过。0.9.69 / 82 已覆盖安装到用户指定设备,回读版本与 APK 元数据一致。
  • Wiki 最终在线 revision 81cec55e15c968efcc2cb4c4e5babcd17a40a3f4 已回读;最终 harness sync / sync --check 全部通过。check --strict 的 main 既有未登记镜像错误已在正文如实记录,不混入修复。
  • 未执行真实采购、修改地址、创建订单、支付或线上发布。正常故障商品的真机选中效果,以及 #361 诊断实际写入,仍未验收。工单保持 open / 待验收,不提前关闭。
## 完工回写:代码完成、已安装,待真机验收 - 工单正文已整合 v2.1 两点及最终结构边界,实际方案、验证与授权变化见正文。 - 分支 fix/362-truncated-spec-card 已推送至 279cb55,工作区干净;未合并 main。 - 解析修复:9a4495a、1f5bab8;保留 #361 的集成及版本提交:35119ea;最终 Wiki 镜像提交:279cb55。采购执行器相对 #361 无改动。 - #362 完整回归 483 项通过;集成后 232 项受影响回归及 assembleDebug 通过。0.9.69 / 82 已覆盖安装到用户指定设备,回读版本与 APK 元数据一致。 - Wiki 最终在线 revision 81cec55e15c968efcc2cb4c4e5babcd17a40a3f4 已回读;最终 harness sync / sync --check 全部通过。check --strict 的 main 既有未登记镜像错误已在正文如实记录,不混入修复。 - 未执行真实采购、修改地址、创建订单、支付或线上发布。正常故障商品的真机选中效果,以及 #361 诊断实际写入,仍未验收。工单保持 open / 待验收,不提前关闭。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#362