docs: import wiki at afc651f75a3a

ila
2026-08-07 16:37:02 +08:00
parent fd13b0824b
commit b0f09e6bc1
+156
@@ -0,0 +1,156 @@
<!-- docs-wiki-sync:docs/tasks/T-256.md@afc651f75a3abc2676bb13aa8a80f6aa6a25a72e -->
> 同步来源:[`docs/tasks/T-256.md`](/chengma/mroubao/src/commit/afc651f75a3abc2676bb13aa8a80f6aa6a25a72e/docs/tasks/T-256.md) · commit `afc651f75a3a`
---
id: T-256
title: 允许少于目标数量的有效候选进入人工确认
phase: 2
deps:
- T-254
status: DOING
created: 2026-07-30
context_ref: c488e50
work_branch: null
write_paths:
- docs/tasks/T-256.md
- docs/current-state.md
- android-buyer/app/src/main/**
- android-buyer/app/src/test/**
---
## 问题 / 背景
T-254 的真机计数分布显示,第一轮已经采集到一个**完全合格**的候选:
| 轮次 | resolved | target | skuMismatch | identityFail | reason |
| --- | --- | --- | --- | --- | --- |
| 1 | **1** | 2 | 0 | 0 | `CANDIDATE_TARGET_NOT_FOUND` |
| 2 | 0 | 1 | 0 | 0 | `CANDIDATE_ALL_EXCLUDED` |
| 3 | 0 | 2 | 0 | 0 | `CANDIDATE_ALL_EXCLUDED` |
该候选颜色尺码硬约束匹配、商品身份解析成功、双证据齐全,但因为
`PROCUREMENT_CANDIDATE_TARGET = 2` 而未达标,整条任务判 `FAILED`,候选没有提交给
Admin,人工连看都看不到。
这与需求直接冲突。`docs/02-requirements.md`:
- 范围决策:「以 SKU 颜色/尺码硬约束过滤后回传 **`0..5` 个**」
- F-005 验收:「匹配项按评估结果排序且最多 5 个,**不足时不得凑数**」
- 业务规则 20:「回传可以少于 5 个,禁止用弱匹配凑数」
需求要求的是「不足就少回传」,当前实现做成了「不足就整体失败」。这是本轮采购任务
失败的**直接可修复原因**,且不依赖 T-255 对卡片识别率的根治。
## 关联需求与交互
- 功能:F-005、F-006、F-007
- 用户故事:US-004、US-005
- 交互:沿用 Admin 任务详情候选确认区,不新增页面
- 架构/API:候选回传与人工确认接口契约不变,**后端零改动**
## 行为契约
### 一、候选采集完成判定
`candidateCollectionReady` 改为:**存在至少一个 `hasResolvedIdentity` 为真的候选**
即视为可提交,不再要求达到 `PROCUREMENT_CANDIDATE_TARGET`。
`PROCUREMENT_CANDIDATE_TARGET = 2` 保留原值和原语义,继续用于计算每轮
`roundCandidateTarget`(即「本轮还想再找几个」),但**不再作为成败门槛**。
### 二、三轮预算继续走满
有效候选数未达 `PROCUREMENT_CANDIDATE_TARGET` 时,剩余轮次**仍然要执行**,尽量补足。
不得因为已经拿到 1 个就提前终止搜索——目标仍是尽量凑够 2 个,只是凑不够不算失败。
三轮跑完后:
- 累计有效候选 **≥ 1** → 提交候选并进入 `WAITING_CONFIRMATION`,不写 `FAIL` 终态
- 累计有效候选 **= 0** → 维持 T-251 现有的 `CANDIDATE_SEARCH_EXHAUSTED` 可重试 `FAIL`
### 三、不得凑数
只有 `hasResolvedIdentity` 为真、且经过既有颜色/尺码硬约束校验的候选才能计入。
`identityFailureCode` 非空的观测候选**不计入**这个「至少一个」的判定,其既有的
观测保存行为不变。
### 四、诊断可见性
提交时的 search run 必须如实记录本次实际提交的候选数与 `PROCUREMENT_CANDIDATE_TARGET`
的差距,让 Admin 知道这是「不足目标但可用」而不是「完整 Top N」。沿用 T-254 的
诊断 message 机制,不新增字段格式。
## 方案
1. `MainActivity.candidateCollectionReady`:判定改为「至少一个 EXACT 或 FALLBACK 的
已解析候选」。
2. 三轮流程收尾处:区分「有候选 → 提交并进人工确认」与「零候选 → 沿用 T-251 终态」
两条路径。
3. `roundCandidateTarget` 的计算方式不变,继续按缺口驱动后续轮次。
4. 单测覆盖:1 个候选跑完三轮后提交且不写 FAIL、2 个候选行为与现状一致、0 个候选仍
写 `CANDIDATE_SEARCH_EXHAUSTED`、仅有未解析身份的观测候选不满足「至少一个」。
## 验收要点
- [ ] 三轮累计恰好 1 个有效候选时,任务进入 `WAITING_CONFIRMATION` 且不写 `FAIL` 终态。
- [ ] 三轮累计 0 个有效候选时,仍写 T-251 的 `CANDIDATE_SEARCH_EXHAUSTED` 可重试终态。
- [ ] 累计 ≥ 2 个时行为与本任务前完全一致,有回归测试证明。
- [ ] 只有身份未解析的观测候选时不满足提交条件。
- [ ] 已拿到 1 个候选后,剩余轮次仍然执行,不提前终止。
- [ ] `PROCUREMENT_CANDIDATE_TARGET` 常量值未改变。
- [ ] Android Debug/Release 单测、`lintDebug`、`assembleDebug`、`assembleRelease` 全部通过。
- [ ] 真机复跑 T-254 那条任务:第一轮那个候选出现在 Admin 候选确认区,可查看双证据。
## 边界
- 不改 `PROCUREMENT_CANDIDATE_TARGET` 的值,也不改 `MAX_CANDIDATES_PER_PROBE`。
- 不改 `PinduoduoImageCandidateRoundPolicy` 的三轮预算、timeout、attempts、scrolls。
- 不改卡片识别、签名算法或跨轮去重——那是 T-255 的范围。
- 不改 T-254 的诊断计数与失败码语义。
- 不降低颜色、尺码、身份解析、双证据任何一项校验,不允许用弱匹配或未解析身份的
观测候选充数。
- 不改后端代码、数据库、API 契约或 Admin 模板。
- 不新增第四轮搜索。
- 不启动真实下单,不进入订单提交或支付。
## 执行记录
开工后记录修改、命令、结果、环境、决策和 blocker。
### 2026-07-30
- 状态:开工时由 `TODO` 改为 `DOING`;按用户要求未改为 `DONE`。
- 修改:
- `MainActivity.candidateCollectionReady` 现在只判断累计候选中是否存在
`hasResolvedIdentity` 为真的候选;身份未解析的诊断观测不会满足提交条件。
- 新增 `CandidateCollectionPolicy`,将“已有有效候选”“已达到原目标”和“达到第三轮
后允许以不足目标数量收尾”分开判定;`PROCUREMENT_CANDIDATE_TARGET = 2`、三轮门禁、
`roundCandidateTarget`、`MAX_CANDIDATES_PER_PROBE` 和图片候选预算未修改。
- 首轮只有一个有效候选时继续执行第二、第三轮;第三轮结束后将累计有效候选排入
`WAITING_CONFIRMATION`,不写 `CANDIDATE_SEARCH_EXHAUSTED`。零个有效候选仍沿用
`CANDIDATE_SEARCH_EXHAUSTED` 终态。
- 三轮耗尽后的进程恢复也先检查持久化候选;已有有效候选回到正常提交路径,只有零个
有效候选才执行既有耗尽失败恢复。
- 成功收尾和恢复收尾的 `recordCandidateSearchAttempt` 均复用 T-254 诊断 message,
并把本次实际累计提交候选数写入 `resolved`、目标写入 `target`,以显示不足目标但可用。
- 新增 `CandidateCollectionPolicyTest`,覆盖 1 个候选三轮收尾、2 个候选原目标行为、
0 个候选失败路径和仅身份未解析观测不满足提交条件。
- 未修改 `backend-api`、API 契约、数据库、候选识别/去重、SKU 硬约束、三轮预算、下单
提交或支付逻辑;未运行 `init.ps1`,未执行 git commit 或 push。
- 命令与结果(工作目录:`D:\chengma\cmroubao\android-buyer`,PowerShell):
- `.\gradlew.bat testDebugUnitTest --no-daemon`:`BUILD SUCCESSFUL`。
- `.\gradlew.bat testReleaseUnitTest --no-daemon`:`BUILD SUCCESSFUL`。
- `.\gradlew.bat lintDebug --no-daemon`:`BUILD SUCCESSFUL`。
- `.\gradlew.bat assembleDebug --no-daemon`:`BUILD SUCCESSFUL`。
- `.\gradlew.bat assembleRelease --no-daemon`:`BUILD SUCCESSFUL`。
- 五项命令均退出码 0。
- 环境:Windows PowerShell;JDK 17.0.13;Android SDK 34;Gradle Wrapper 8.2。构建输出
的 SDK XML 版本提示为既有环境警告,不影响五项目标成功。
- 决策:保留既有“达到 2 个 exact 或 2 个 fallback 可提前收尾”的行为;只有累计有效候选
少于目标时才要求继续到第三轮。第三轮结束时只要有至少一个已解析身份候选即可提交,
以满足不足时少回传而非整体失败的需求。
- 未验证 / blocker:无法连接并操作测试真机,也无法在 Admin 任务详情确认候选双证据,
因此 T-256 的最终 on-device verification 未完成;需要在测试设备上复跑 T-254 任务并
确认一个有效候选进入人工确认区。该 blocker 不影响本次五项 Android Gradle 验证。
- 最终复核:补充进程恢复门禁后,以上五条 Gradle 命令均重新执行并再次以退出码 0
`BUILD SUCCESSFUL` 结束。