本正文为最终版(2026-08-28 重写)。 此前的历史评论仅作决策过程记录;如与本正文冲突,一律以本正文为准。原方案「新增续做采购能力」已作废——经复核,AgentRetry 已存在且完全满足需求,本工单缩减为状态与入口改造。
AgentRetry
30d8238
AgentRetry 已经存在(purchase/retry.go:143),其注释明确:
purchase/retry.go:143
delegates creation and idempotency to BatchRetry so Admin and Agent use exactly the same archive, device and irreversible-boundary checks
具体能力:
retry.go:154-156
taskId
requestId
BatchRetry
retry.go:67-69
Retryable
purchase/agent_history.go:41
admin_query.go:68
因此不新增第二套创建能力与第二套幂等链路。 此前正文中「为设备 token 新开创建路径」的判断有误,据此产生的风险分析一并作废。
retryable
replacementMappingStatus
continuePurchaseEligible
continuePurchaseDisabledReason
replacement_item
mapping_status
retryEligibility
matched
POST /purchase-tasks/{taskId}/retry
mappingTargetsValid
matching
manual_required
reason
Create
go test ./app/goauto/purchase/... ./app/goauto/access/...
cd android && .\gradlew.bat :app:testDebugUnitTest :app:assembleDebug
Business-Rules-and-Glossary
Android-Agent-API-Contract
sync
sync --check
待实施(前置 #129、#130、#131)。
用户于 2026-08-28 确认整体流程为「替换 → 自动匹配规格 → Agent 刷新看状态 → 继续采购」,AI 匹配按 B 方案处理。本评论据此调整续做的判据与提示。
正文第 2 项的三条校验中,第三条「该任务原 pdd_product_id 存在一条生效的替换记录」细化为:
pdd_product_id
mapping_status = matched
其余两条(任务为 failed、失败码属商品失效类)不变。
failed
mapping_status = matching
规格匹配中,请稍后重试
mapping_status = manual_required
规格待匹配,请在 Admin 处理
服务端仍保留 BatchRetry 自身的映射校验作为最终防线(mappingTargetsValid),但 Agent 侧的按钮显隐与预期提示以 mapping_status 为准,避免用户点了才知道不行。
「继续采购」按钮仅在 mapping_status = matched 时出现;其余状态显示 #130 定义的一行状态文字,不显示按钮。
因此正文第 9 项「客户端按任务状态与是否存在替换记录判断」调整为「按任务状态与 mapping_status 判断」。
matched 可能来自两种途径:
两者对本工单等价,续做逻辑不区分来源。但自动确认的映射必须已完整记录 source / confidence / reason(由 #131 保证),以便日后追溯买错规格的责任链路。
正文其余内容(复用 BatchRetry、Agent 只传任务号与 requestId、不在手机端做规格决策、幂等与仅成功一次、设备 token 反向测试、按钮区分约束)保持不变。
目标合理,但当前代码已有 AgentRetry → BatchRetry:
AgentRetry → BatchRetry
因此不建议新增第二套“续做采购”创建能力和第二套幂等链路。建议本工单调整为:
matching/manual_required
shopee_product
retryable/reason
若 Claude Code 认为必须保留独立 endpoint,请明确说明现有 AgentRetry 不能满足的具体差异,并保证它只是薄包装,所有现有门禁一条不少。
为避免两份设计证据漂移,#132 复用 #130 的同一合并原型:
prototypes/130/v1/index.html
prototypes/130/v1/annotated-continue-purchase.png
#132 落图结论:只有分项 matched 且服务端 continuePurchaseEligible=true 时,既有重试入口显示为“继续采购”;matching/manual_required 无按钮;matched 但设备忙等既有资格失败时只显示服务端原因。未新增按钮、页面或采购任务创建路径,点击语义仍为既有 AgentRetry → BatchRetry。
continuePurchaseEligible=true
用户于 2026-08-28 明确确认:与 #130 共用的合并轻量原型 v1 审核通过。
1413CB75799B5D16FA756E9BD8687D98DB4A105E794DCDB4E53A00C4E5E22825
该版本不再原位覆盖。#132 仍以后置依赖 #129、#131、#130 完成为实施前提;真机点击“继续采购”可能创建正式采购任务,执行该项真机验收前仍需单独取得授权,永久禁止支付。
已按最终正文实施并推送:
0f522d5 feat(#132): continue purchase after replacement
main
696e0bc
92385c4
33d4bb1
4862d48
POST /api/agent/v1/purchase-tasks/{taskId}/retry
AgentRetry → BatchRetry → Create
prototypes/130/v1
go test ./app/goauto/purchase ./app/goauto/access
.\scripts\verify.ps1 -Component all
python dev_scripts/harness.py check --strict
python dev_scripts/harness.py sync
6e33660c976c4412c3c456d04d5b4784a3043732
81d3fec214250f9b5be4b049e65ca6a1144e52ad
adb devices -l
用户于 2026-08-29 明确确认本工单通过验收。验收结论已记录,现关闭工单。没有新的长期事实变化,本次不重复同步 Wiki。
No dependencies set.
The note is not visible to the blocked user.
所属与来源
当前事实(提交
30d8238复核)AgentRetry已经存在(purchase/retry.go:143),其注释明确:具体能力:
retry.go:154-156);taskId与requestId,不接受任何业务参数;BatchRetry,与 Admin 走完全相同的档案、设备与不可逆边界校验;retry.go:67-69);Retryable字段(purchase/agent_history.go:41、admin_query.go:68)。因此不新增第二套创建能力与第二套幂等链路。 此前正文中「为设备 token 新开创建路径」的判断有误,据此产生的风险分析一并作废。
目标
BatchRetry),无需改动。非目标
AgentRetry→BatchRetry。BatchRetry的既有校验、价格护栏、规则解析与不可逆边界。实施方案
一、服务端
retryable。replacementMappingStatus、continuePurchaseEligible、continuePurchaseDisabledReason。replacementMappingStatus必须读取该任务对应虾皮商品的replacement_item,不得使用 #129 主表的全局mapping_status——一个 PDD 商品可被多个虾皮商品共用,各自结果可能不同。continuePurchaseEligible的判定为:既有全部retryEligibility通过 且 分项状态为matched。retryEligibility的任何一条。POST /purchase-tasks/{taskId}/retry(AgentRetry);Admin 仍调用BatchRetry。BatchRetry的mappingTargetsValid、最新任务判定、不可逆边界、订单事实、设备在线/空闲/能力、价格与规则校验,继续作为最终事实:即使replacementMappingStatus为matched,若映射后来被人工清空、PDD 重新采集导致候选变化、价格缺失或设备变忙,最终仍由 Retry 校验拒绝。二、Agent 端
matched且continuePurchaseEligible为真 → 既有重试入口显示为**「继续采购」**。matching/manual_required→ 只显示 #130 定义的一行状态文字,不显示按钮。continuePurchaseDisabledReason,不在客户端拼接原因。retryable/reason模式,UI 不做任何状态组合推断。安全边界
BatchRetry/Create。验收标准
BatchRetry/Create路径(以调用链检查与测试佐证)。replacement_item状态,未使用主表全局状态。matched且既有retryEligibility全通过时显示「继续采购」,点击成功创建新任务。matching/manual_required时不显示按钮,只显示状态文字。matched但映射已被人工清空、matched但 PDD 重新采集导致候选变化、matched但设备忙、已有更新的任务、已越过不可逆边界、价格缺失。requestId重复请求不产生第二条任务;不同requestId重复点击受既有「最新任务」规则拦截。验证方式
go test ./app/goauto/purchase/... ./app/goauto/access/...cd android && .\gradlew.bat :app:testDebugUnitTest :app:assembleDebug依赖、并行与风险
matched但最终 Retry 被拒,用户会看到「点了没成功」。缓解:服务端返回明确的continuePurchaseDisabledReason,并在最终拒绝时给出可读原因。AgentRetry不受影响。文档影响
Business-Rules-and-Glossary:失败采购任务续做的条件与规格前置要求。Android-Agent-API-Contract:采购任务详情接口新增的三个字段;明确未新增创建接口。sync与一轮sync --check,把页面与 revision 写回本工单。状态
待实施(前置 #129、#130、#131)。
修订:续做的前置条件改为依据
mapping_status用户于 2026-08-28 确认整体流程为「替换 → 自动匹配规格 → Agent 刷新看状态 → 继续采购」,AI 匹配按 B 方案处理。本评论据此调整续做的判据与提示。
一、第三条校验细化
正文第 2 项的三条校验中,第三条「该任务原
pdd_product_id存在一条生效的替换记录」细化为:mapping_status = matched(#129 新增字段)。其余两条(任务为
failed、失败码属商品失效类)不变。二、按状态返回,不在服务端重复判断映射
mapping_status = matching→ 拒绝并提示规格匹配中,请稍后重试;mapping_status = manual_required→ 拒绝并提示规格待匹配,请在 Admin 处理;mapping_status = matched→ 受理,走既有BatchRetry单任务路径。服务端仍保留
BatchRetry自身的映射校验作为最终防线(mappingTargetsValid),但 Agent 侧的按钮显隐与预期提示以mapping_status为准,避免用户点了才知道不行。三、按钮显示条件
「继续采购」按钮仅在
mapping_status = matched时出现;其余状态显示 #130 定义的一行状态文字,不显示按钮。因此正文第 9 项「客户端按任务状态与是否存在替换记录判断」调整为「按任务状态与
mapping_status判断」。四、B 方案带来的边界说明
matched可能来自两种途径:matched。两者对本工单等价,续做逻辑不区分来源。但自动确认的映射必须已完整记录 source / confidence / reason(由 #131 保证),以便日后追溯买错规格的责任链路。
五、验收标准调整
mapping_status为matching或manual_required时续做被拒绝,提示分别可读且不同。mapping_status = matched时续做成功,新任务的商品与映射来源与 Admin 重试一致。matched时出现。正文其余内容(复用
BatchRetry、Agent 只传任务号与 requestId、不在手机端做规格决策、幂等与仅成功一次、设备 token 反向测试、按钮区分约束)保持不变。Codex 全栈复核:建议缩减为复用现有 AgentRetry 的状态与入口改造
目标合理,但当前代码已有
AgentRetry → BatchRetry:因此不建议新增第二套“续做采购”创建能力和第二套幂等链路。建议本工单调整为:
retryable。replacementMappingStatus、continuePurchaseEligible、continuePurchaseDisabledReason。matched且现有 retryEligibility 全部通过时,Agent 将现有“重试采购”入口显示为“继续采购”。POST /purchase-tasks/{taskId}/retry,Admin 仍调用 BatchRetry。matching/manual_required只显示状态,不显示按钮。mappingTargetsValid、最新任务、不可逆边界、订单事实、设备在线/空闲/能力、价格和规则校验继续作为最终事实。必须修正的判据
mapping_status不能决定某条采购任务是否可继续;必须读取该任务对应shopee_product的 replacement item 状态,或由服务端直接返回 eligibility。retryable/reason模式。验收补充
若 Claude Code 认为必须保留独立 endpoint,请明确说明现有 AgentRetry 不能满足的具体差异,并保证它只是薄包装,所有现有门禁一条不少。
PDD 商品替换(四):失败采购任务在 Admin 与 Agent 均可续做to PDD 商品替换(四):续做入口改造(复用既有 AgentRetry,不新增创建路径)合并轻量原型 v1(与 #130 共用,草稿待确认)
为避免两份设计证据漂移,#132 复用 #130 的同一合并原型:
prototypes/130/v1/index.htmlprototypes/130/v1/annotated-continue-purchase.png#132 落图结论:只有分项
matched且服务端continuePurchaseEligible=true时,既有重试入口显示为“继续采购”;matching/manual_required无按钮;matched但设备忙等既有资格失败时只显示服务端原因。未新增按钮、页面或采购任务创建路径,点击语义仍为既有 AgentRetry → BatchRetry。原型验收通过
用户于 2026-08-28 明确确认:与 #130 共用的合并轻量原型 v1 审核通过。
prototypes/130/v1/index.html、prototypes/130/v1/annotated-continue-purchase.png1413CB75799B5D16FA756E9BD8687D98DB4A105E794DCDB4E53A00C4E5E22825该版本不再原位覆盖。#132 仍以后置依赖 #129、#131、#130 完成为实施前提;真机点击“继续采购”可能创建正式采购任务,执行该项真机验收前仍需单独取得授权,永久禁止支付。
实施完成,提交待验收
已按最终正文实施并推送:
0f522d5 feat(#132): continue purchase after replacementmain696e0bc、#13192385c4、#13033d4bb1、#1344862d48实现结论
POST /api/agent/v1/purchase-tasks/{taskId}/retry,调用链保持AgentRetry → BatchRetry → Create。continuePurchaseEligible、continuePurchaseDisabledReason;replacementMappingStatus继续来自该任务对应虾皮商品的 replacement item,不读主表总体状态。matched才计算继续资格。资格复用既有任务终态、最新任务、不可逆边界、当前档案、PDD 候选、价格、设备在线/空闲与能力门禁。continuePurchaseEligible显示既有入口并改文案为“继续采购”;matching/manual_required 无按钮,matched 但不可用显示服务端原因。prototypes/130/v1未修改、未纳入提交。自动化验证
go test ./app/goauto/purchase ./app/goauto/access:通过.\scripts\verify.ps1 -Component all:通过python dev_scripts/harness.py check --strict:通过python dev_scripts/harness.py sync+sync --check:通过长期文档
6e33660c976c4412c3c456d04d5b4784a304373281d3fec214250f9b5be4b049e65ca6a1144e52ad尚未验证 / 安全边界
adb devices -l没有连接设备,APK 未安装,PKG110 入口显示未做真机检查。用户于 2026-08-29 明确确认本工单通过验收。验收结论已记录,现关闭工单。没有新的长期事实变化,本次不重复同步 Wiki。