本正文为最终版(2026-08-31 四次修订)。 已按全栈复核补充存量盘点、固定根因位置、确定尺码价格处理、收紧清洗规则、修正服务端部分接受语义与自相矛盾的验收项;本次补充 SKU Complete 基准、contains 现存假阳性漏洞,并更正文档影响。历史评论仅作决策记录,如与本正文冲突以本正文为准。
Complete
contains
来源:用户于 2026-08-31 反馈 CG-28 采购失败并提示“再次执行仍未能精确选择商品规格”,人工检查 PDD 规格面板存在“黑色、XL”。用户确认按分析建议建工单,并于同日确认按复核建议修订本工单。
目的:修复 PDD 规格采集把尺码价格混入规格名称,导致采购严格匹配失败。
failed
PURCHASE_SPEC_NOT_MATCHED
黑色 / XL
黑色 / XL【建议121-140斤】 ¥15.78
ai_match
717778239538
酒红色 / 卡其色 / 黑色
priceCent
XL【建议121-140斤】 ¥15.78
size
100cm ¥3.69
PddProductDetailCollector.kt
label
VisibleSpecValue.text
dimension.Role != "color"
PriceCent
SubmitResult → persistResult → resultProductSpecs → applyResultToProduct
dimension.Values
persistResult
id := valueIDs[key][value]
0
DimensionValueID = 0
Complete: len(input.Specs) == len(valueIDs)
len(valueIDs)
isExactSpecSelected
verifySummary
selectedSummary?.contains(target)
XL
已选 黑色 2XL
true
2XL
只剥离位于字符串尾部的价格片段:¥ 或 ¥ 加数字(可含一至两位小数),及其前置空白。
¥
¥
4XL 160-170斤
黑色+白色【纯棉两件装】 简约亲肤
3XL
4XL
missing
completed_partial
manual_required
ResultRequest
SKU.specs
color
colorPrices
CollectionSKUValue
spec_dimension_invalid:<dimensionKey>
completed
现存缺陷(本工单必须修复,不只是复核方式不足):PurchaseRehearsalExecutor.kt 的 isExactSpecSelected 与 verifySummary 都以 selectedSummary?.contains(target) 作为独立成立条件,存在双向问题:
PurchaseRehearsalExecutor.kt
XL【建议121-140斤】
false
固定要求:
selected
checked
样本中规格显示 XL【建议121-140斤】 ¥15.78 时,采集结果为 XL【建议121-140斤】,不含任何货币符号或价格数字。
100cm ¥3.69 形态清洗为 100cm。
100cm
中括号说明保持原样,4XL 160-170斤 类文本不被截断。
XL、2XL、3XL、4XL 相互独立,未被归一。
清洗后为空或与同维度重复时判为无法清洗,不写入。
同维度出现无法清洗的值时该维度整体不更新,计入 missing 且任务为 completed_partial;不产生部分接受的残缺档案。
污染值不会覆盖已有可用档案。
尺码价格被丢弃,未新增字段,服务端 priceCent 只允许颜色的校验未放宽。
AI 输入候选为清洁值,AI 仍只能返回候选集合中的精确值。
采购端仍严格匹配,未引入包含或模糊点击。
输出受影响 PDD 商品 ID 只读清单并回写工单;未执行自动批量清洗。
重新采集 717778239538 后尺码候选不含货币符号,相邻尺码互相独立;具体保留文本以真机页面为准。
已确认虾皮侧目标尺码文本,并说明清洗后能否精确匹配。
通过既有重试路径生成新任务;未修改 CG-28 快照,未手工改库。
未执行订单创建和支付;继续验证创建待付款订单需另行人工授权。
Server 对安全尾价执行与 Android 相同的规范化后再校验;旧 Agent 提交安全尾价样本也不会写入污染值。
被拒维度与其关联 SKU 原子丢弃;color 被拒时 colorPrices 和相关 SKU 同步丢弃,未产生残缺或重复 SKU。
不再产生 DimensionValueID = 0 的 CollectionSKUValue 记录。
维度被丢弃时 SKU 的 Complete 以被拒前的维度总数为准,残缺 SKU 不会被误标为完整。
服务端发现污染时强制 completed_partial,并记录稳定 missing 值 spec_dimension_invalid:<dimensionKey>。
候选为 XL【建议121-140斤】 且“已选”摘要仅为 XL 时,可通过明确选中态或无歧义 token 安全复核。
目标 XL、摘要为 已选 黑色 2XL 时必须判定为未选中,现存 contains 假阳性漏洞已消除。
isExactSpecSelected 与 verifySummary 均已修改,两处都不再把裸 contains 作为独立成立条件。
Android-Agent-API-Contract 已记录新增稳定 missing 值,并完成一轮 sync 与一轮 sync --check。
Android-Agent-API-Contract
sync
sync --check
Android、Server 相关单元/契约测试与 APK 构建通过。
同一面板同时存在 XL、2XL、3XL 时分别解析为不同 token,不误判歧义,也不发生子串误认。
同一面板存在两个不同候选但主 token 均为 XL 时,短摘要 XL 必须判定证据不足并安全失败。
token 唯一性只使用本次执行同一规格面板的新鲜候选集合;旧档案、旧快照和页面变化前候选不能作为证据。
摘要 token 仅参与点击后复核,规格定位和点击仍对完整规范化候选执行严格相等。
XL/2XL/3XL
有长期文档影响。 §7.2 引入稳定 missing 值 spec_dimension_invalid:<dimensionKey>,属于 Agent 与服务端共享契约的新增稳定代码,必须:
python dev_scripts/harness.py sync
其余部分恢复既有“采集规格是稳定业务值、采购只精确匹配”的已确认规则,不改变 API 字段形状、状态机或安全边界。
待实施。
开始实施 #160(基线:5d85625)。
范围按四次修订最终正文执行:
安全边界:不修改 CG-28 历史快照,不自动清洗数据库,不创建订单,不支付;真机只验证到订单创建前,若需要继续创建待付款订单将另行等待授权。
实施中只读盘点纠正(2026-08-31,本机 goauto 当前数据库):
范围处理:
完整15个只读商品清单仍会在最终待验收回写中提供,并明确标注 size/color 分类。
当前会话未暴露 Gitea MCP 工具,按仓库规则回退项目根目录安全配置与 Gitea API 完成本评论。
¥ / ¥ + 数字价格
spec_dimension_invalid:size
dimension_value_id=0
selectedSummary.contains(target)
5caf37fa5589e63e128e2beb9bc6acc24edf1cb1
提交并已推送:67f3a74 fix(goauto): safely normalize specification values (#160)
67f3a74 fix(goauto): safely normalize specification values (#160)
SpecValueNormalizerTest
PddProductDetailCollectorTest
PurchaseRehearsalExecutorTest
.\gradlew.bat testDebugUnitTest assembleDebug
go test ./app/goauto/task
go test ./...
go build ./...
python dev_scripts/harness.py check --strict
python dev_scripts/harness.py sync --check
git diff --check
.\scripts\verify.ps1 -Component all
web/src/views/goauto/purchase-tasks/index.vue
757123298435
¥5.69
925227518638
378666384863
339317949086
474624457919
607234829804
625761667128
712088396137
7828258098
960129324986
966564660291
99584550593
992229024823
993585204674
parse_status=success
manually_confirmed=false
No dependencies set.
The note is not visible to the blocked user.
原始需求摘要
来源:用户于 2026-08-31 反馈 CG-28 采购失败并提示“再次执行仍未能精确选择商品规格”,人工检查 PDD 规格面板存在“黑色、XL”。用户确认按分析建议建工单,并于同日确认按复核建议修订本工单。
目的:修复 PDD 规格采集把尺码价格混入规格名称,导致采购严格匹配失败。
已核实事实(2026-08-31 复核数据库与代码)
failed、错误码PURCHASE_SPEC_NOT_MATCHED、目标黑色 / XL、固化映射黑色 / XL【建议121-140斤】 ¥15.78、来源ai_match。717778239538(内部 ID 6425)当前档案:颜色酒红色 / 卡其色 / 黑色干净且价格保存在priceCent;尺码 7 个值全部形如XL【建议121-140斤】 ¥15.78。size维度的 PDD 商品共 1744 个,其中 15 个的尺码值含货币符号。污染样式包含XL【建议121-140斤】 ¥15.78和100cm ¥3.69两类,共同点是价格位于文本尾部。PddProductDetailCollector.kt解析阶段把节点label(必要时取首个非空后代 label)整体作为VisibleSpecValue.text。颜色已有独立价格提取写入priceCent,尺码没有对等的名称与价格分离。dimension.Role != "color"时PriceCent必须为空,因此尺码价格当前无处存放。SubmitResult → persistResult → resultProductSpecs → applyResultToProduct全链路把dimension.Values原样写入档案,没有价格文本检查。只改 Android 而服务端不设防,旧版 Agent 仍会写入污染值。persistResult写 SKU 时存在零值外键风险:id := valueIDs[key][value],维度被丢弃后取零值0,会写出DimensionValueID = 0的孤儿行或触发外键失败。Complete: len(input.Specs) == len(valueIDs)依赖维度总数:丢弃一个维度会使len(valueIDs)减一,导致原本不完整的 SKU 被误标为完整。contains假阳性漏洞:isExactSpecSelected与verifySummary均使用selectedSummary?.contains(target)。目标为XL而摘要为已选 黑色 2XL时,contains返回true,会把2XL已选误判为XL已选,直接违反“不猜测、不选相近候选”的安全边界。目标
contains子串误判。非目标
priceCent只允许颜色的校验。固定实施方案
一、清洗规则(最小安全集)
只剥离位于字符串尾部的价格片段:
¥或¥加数字(可含一至两位小数),及其前置空白。4XL 160-170斤、黑色+白色【纯棉两件装】 简约亲肤这类文本中,括号或体重区间是规格身份的一部分;过度剥离会导致4XL 160-170斤与其他值冲突,制造新的匹配失败甚至误选。二、Android 采集
XL/2XL/3XL/4XL必须保持互相独立,禁止使用简单 contains 截断。三、服务端防线
missing,任务标记completed_partial。四、存量盘点
717778239538无法覆盖其余 14 个,下次采购会以同样方式失败。五、采购端
XL不会匹配2XL、3XL,且不依赖包含匹配。六、CG-28 恢复路径
manual_required,由 Admin 重新入队或人工选择规格;不绕过既有门禁。七、双端规范化、关联数据与最终复核补充
1. 固定 Android 与 Server 的规范化顺序
2. 固定被拒维度与关联结果的原子处理
ResultRequest完整性校验之前。SKU.specs删除该键后保留残缺组合,也不得因此制造重复 SKU。color,还必须同时丢弃colorPrices以及所有引用该颜色维度的 SKU。persistResult的零值外键必须被消除:不得再出现DimensionValueID = 0的CollectionSKUValue;任一 SKU 引用到已被丢弃维度时整条 SKU 丢弃,而不是写入零值关联。Complete: len(input.Specs) == len(valueIDs)会因维度被丢弃而缩小分母,把原本不完整的 SKU 误标为完整。实施必须使用原始维度总数计算Complete,或在维度被拒时直接判定相关 SKU 不完整,禁止出现“维度变少反而更完整”的结果。completed_partial,并追加稳定 missing 项:spec_dimension_invalid:<dimensionKey>。服务端不得接受客户端仍声明completed的完整状态。3. 固定长候选与短“已选摘要”的安全复核
现存缺陷(本工单必须修复,不只是复核方式不足):
PurchaseRehearsalExecutor.kt的isExactSpecSelected与verifySummary都以selectedSummary?.contains(target)作为独立成立条件,存在双向问题:XL、摘要已选 黑色 2XL时contains返回true,会把2XL已选误判为XL已选。这直接违反“不猜测、不选相近候选”的永久边界,必须修复。XL【建议121-140斤】,而摘要只显示XL时contains返回false,会阻断 CG-28 的正常恢复。固定要求:
isExactSpecSelected与verifySummary必须同时修改,不得只改其一;两者不得再把裸contains作为独立成立条件。contains、前缀或相近文本寻找和点击候选。selected/checked状态,并对该选项应用同一尾价规范化后与目标严格相等,作为选中证据。XL/2XL/3XL等歧义。不能退化为任意子串包含。XL【建议121-140斤】、摘要仅为XL”的成功样本,以及XL不得匹配2XL/3XL、存在重复主规格 token 时必须失败的反例。4. 固定主规格 token 的唯一性边界
数据、接口与迁移
spec_dimension_invalid:<dimensionKey>属于共享契约新增,按“文档影响”一节处理。设计证据
验收标准
样本中规格显示
XL【建议121-140斤】 ¥15.78时,采集结果为XL【建议121-140斤】,不含任何货币符号或价格数字。100cm ¥3.69形态清洗为100cm。中括号说明保持原样,
4XL 160-170斤类文本不被截断。XL、2XL、3XL、4XL相互独立,未被归一。清洗后为空或与同维度重复时判为无法清洗,不写入。
同维度出现无法清洗的值时该维度整体不更新,计入
missing且任务为completed_partial;不产生部分接受的残缺档案。污染值不会覆盖已有可用档案。
尺码价格被丢弃,未新增字段,服务端
priceCent只允许颜色的校验未放宽。AI 输入候选为清洁值,AI 仍只能返回候选集合中的精确值。
采购端仍严格匹配,未引入包含或模糊点击。
输出受影响 PDD 商品 ID 只读清单并回写工单;未执行自动批量清洗。
重新采集
717778239538后尺码候选不含货币符号,相邻尺码互相独立;具体保留文本以真机页面为准。已确认虾皮侧目标尺码文本,并说明清洗后能否精确匹配。
通过既有重试路径生成新任务;未修改 CG-28 快照,未手工改库。
未执行订单创建和支付;继续验证创建待付款订单需另行人工授权。
Server 对安全尾价执行与 Android 相同的规范化后再校验;旧 Agent 提交安全尾价样本也不会写入污染值。
被拒维度与其关联 SKU 原子丢弃;
color被拒时colorPrices和相关 SKU 同步丢弃,未产生残缺或重复 SKU。不再产生
DimensionValueID = 0的CollectionSKUValue记录。维度被丢弃时 SKU 的
Complete以被拒前的维度总数为准,残缺 SKU 不会被误标为完整。服务端发现污染时强制
completed_partial,并记录稳定 missing 值spec_dimension_invalid:<dimensionKey>。候选为
XL【建议121-140斤】且“已选”摘要仅为XL时,可通过明确选中态或无歧义 token 安全复核。目标
XL、摘要为已选 黑色 2XL时必须判定为未选中,现存contains假阳性漏洞已消除。isExactSpecSelected与verifySummary均已修改,两处都不再把裸contains作为独立成立条件。Android-Agent-API-Contract已记录新增稳定 missing 值,并完成一轮sync与一轮sync --check。Android、Server 相关单元/契约测试与 APK 构建通过。
同一面板同时存在 XL、2XL、3XL 时分别解析为不同 token,不误判歧义,也不发生子串误认。
同一面板存在两个不同候选但主 token 均为 XL 时,短摘要 XL 必须判定证据不足并安全失败。
token 唯一性只使用本次执行同一规格面板的新鲜候选集合;旧档案、旧快照和页面变化前候选不能作为证据。
摘要 token 仅参与点击后复核,规格定位和点击仍对完整规范化候选执行严格相等。
风险与回归
XL/2XL/3XL、100cm数字尺码和动态规格行。contains时若退化为更宽松的 token 匹配,会重新引入相近候选误选:token 必须严格相等且证明同维度无歧义。文档影响
有长期文档影响。 §7.2 引入稳定 missing 值
spec_dimension_invalid:<dimensionKey>,属于 Agent 与服务端共享契约的新增稳定代码,必须:Android-Agent-API-Contract,说明该 missing 值的含义、产生条件与客户端处理方式;python dev_scripts/harness.py sync和一次sync --check,提交本地镜像并把页面与 revision 回写本工单。其余部分恢复既有“采集规格是稳定业务值、采购只精确匹配”的已确认规则,不改变 API 字段形状、状态机或安全边界。
状态
待实施。
开始实施 #160(基线:5d85625)。
范围按四次修订最终正文执行:
安全边界:不修改 CG-28 历史快照,不自动清洗数据库,不创建订单,不支付;真机只验证到订单创建前,若需要继续创建待付款订单将另行等待授权。
实施中只读盘点纠正(2026-08-31,本机 goauto 当前数据库):
范围处理:
完整15个只读商品清单仍会在最终待验收回写中提供,并明确标注 size/color 分类。
实施完成,等待验收(2026-08-31)
实现
size剥离位于文本尾部的¥ / ¥ + 数字价格;保留括号建议、体重区间等规格身份文本,同时保留原始文本用于碰撞检查。completed_partial,并加入spec_dimension_invalid:size。dimension_value_id=0。selectedSummary.contains(target)模糊命中。优先使用当前节点 selected/checked 精确证据;仅在当前候选中主 token 唯一且摘要为独立完整 token 时允许短摘要回退,2XL不再误判为XL。Android-Agent-API-Contract,在线回读 revision:5caf37fa5589e63e128e2beb9bc6acc24edf1cb1;已执行一次sync和一次sync --check。提交并已推送:
67f3a74 fix(goauto): safely normalize specification values (#160)自动化验证
SpecValueNormalizerTest、PddProductDetailCollectorTest、PurchaseRehearsalExecutorTest通过。.\gradlew.bat testDebugUnitTest assembleDebug,BUILD SUCCESSFUL。go test ./app/goauto/task通过。go test ./...、go build ./...通过。python dev_scripts/harness.py check --strict通过。python dev_scripts/harness.py sync --check通过。git diff --check通过。.\scripts\verify.ps1 -Component all中 Server 通过;Web lint 被未修改的web/src/views/goauto/purchase-tasks/index.vue既有 24 个 Tab/空格错误阻断。#160 未修改 Web,按范围未顺手修复。只读存量盘点
717778239538:7 个(S–4XL 尾价)。757123298435:3 个套餐尺码值(尾部¥5.69)。925227518638:7 个(100cm–160cm 尾价)。378666384863、1563/339317949086、3310/474624457919、5118/607234829804625761667128、6365/712088396137、6957/7828258098、8061/960129324986966564660291、8466/99584550593、8471/992229024823、8472/993585204674黑色 / XL,parse_status=success,manually_confirmed=false。明确边界 / 待人工验收
717778239538,并验证采购规格黑色 / XL不再被2XL或长标签摘要误判。