fix(bom): preserve resolved primary material
This commit is contained in:
parent
cdb807dcd5
commit
62017377ad
@ -218,7 +218,7 @@ const supportedV3AnalysisResult = {
|
||||
schemaVersion: 'competitor-analysis/v3' as const,
|
||||
structureBlueprint: {
|
||||
schemaVersion: 'structure-blueprint/v2' as const,
|
||||
knowledgeVersion: 'structure-priors/2026-08-13-v8',
|
||||
knowledgeVersion: 'structure-priors/2026-08-13-v9',
|
||||
supportStatus: 'SUPPORTED' as const,
|
||||
categorySkill: {
|
||||
selectedSkillId: 'TABLE',
|
||||
|
||||
@ -276,7 +276,7 @@ async function runStructurePipeline(orderedFixture: readonly ProductionWardrobe2
|
||||
}
|
||||
const checkpoint = readBomCostingStructureCheckpoint({
|
||||
kind: 'BOM_COSTING_STRUCTURE_CHECKPOINT',
|
||||
version: 12,
|
||||
version: 13,
|
||||
sourceSkuIds: snapshot.skus.map((sku) => sku.externalSkuId),
|
||||
blueprint,
|
||||
completedGroupKeys: [],
|
||||
|
||||
@ -222,7 +222,7 @@ describe('BOM v7 结构蓝图与 SKU 展开', () => {
|
||||
const group = blueprint.structureGroups[0]!
|
||||
const checkpointValue = {
|
||||
kind: 'BOM_COSTING_STRUCTURE_CHECKPOINT',
|
||||
version: 12,
|
||||
version: 13,
|
||||
sourceSkuIds: ['sku-1'],
|
||||
blueprint,
|
||||
completedGroupKeys: [group.structureGroupKey],
|
||||
@ -307,7 +307,7 @@ describe('BOM v7 结构蓝图与 SKU 展开', () => {
|
||||
)
|
||||
const checkpoint = readBomCostingStructureCheckpoint({
|
||||
kind: 'BOM_COSTING_STRUCTURE_CHECKPOINT',
|
||||
version: 12,
|
||||
version: 13,
|
||||
sourceSkuIds: snapshot.skus.map((sku) => sku.externalSkuId),
|
||||
blueprint,
|
||||
completedGroupKeys: blueprint.structureGroups.map((group) => group.structureGroupKey),
|
||||
|
||||
@ -140,9 +140,9 @@ const instantiationPayloadSchema = z.object({
|
||||
|
||||
const BOM_COSTING_STRUCTURE_CHECKPOINT_KIND =
|
||||
'BOM_COSTING_STRUCTURE_CHECKPOINT' as const
|
||||
// Version 12 invalidates instantiated lines created before trusted slot
|
||||
// dimensions were preserved and axis-labelled evidence was parsed safely.
|
||||
const BOM_COSTING_STRUCTURE_CHECKPOINT_VERSION = 12 as const
|
||||
// Version 13 invalidates instantiated lines created before unrecognized
|
||||
// component material prose stopped shadowing a resolved primary cost family.
|
||||
const BOM_COSTING_STRUCTURE_CHECKPOINT_VERSION = 13 as const
|
||||
|
||||
const bomCostingStructureCheckpointSchema = z.object({
|
||||
kind: z.literal(BOM_COSTING_STRUCTURE_CHECKPOINT_KIND),
|
||||
@ -954,6 +954,16 @@ function materialDescriptorFromRecords(records: readonly MaterialFactRecord[]) {
|
||||
)))
|
||||
}
|
||||
|
||||
function materialDescriptorLayer(records: readonly MaterialFactRecord[]) {
|
||||
const descriptors = records.flatMap((record) => (
|
||||
materialDescriptorsFromText(materialFactText(record))
|
||||
))
|
||||
return {
|
||||
hasRecognizedDescriptor: descriptors.length > 0,
|
||||
descriptor: selectSingleMaterialDescriptor(descriptors),
|
||||
}
|
||||
}
|
||||
|
||||
function explicitPrimaryMaterialDescriptor(input: {
|
||||
materialRecords: readonly MaterialFactRecord[]
|
||||
skuId: string
|
||||
@ -987,14 +997,16 @@ function resolvePhysicalMaterialDescriptor(input: {
|
||||
))
|
||||
// A conflict at a higher-priority scope remains unknown; a broader fact or
|
||||
// model guess must not silently choose one side of that conflict.
|
||||
if (skuSlotRecords.length > 0) return materialDescriptorFromRecords(skuSlotRecords)
|
||||
const skuSlotMaterial = materialDescriptorLayer(skuSlotRecords)
|
||||
if (skuSlotMaterial.hasRecognizedDescriptor) return skuSlotMaterial.descriptor
|
||||
|
||||
const productExactSlotRecords = slotRecords.filter((record) => (
|
||||
!isBomCostingSkuSpecificFact(record, input.sku.externalSkuId) &&
|
||||
materialFactNamesConcreteComponent(record)
|
||||
))
|
||||
if (productExactSlotRecords.length > 0) {
|
||||
return materialDescriptorFromRecords(productExactSlotRecords)
|
||||
const productExactSlotMaterial = materialDescriptorLayer(productExactSlotRecords)
|
||||
if (productExactSlotMaterial.hasRecognizedDescriptor) {
|
||||
return productExactSlotMaterial.descriptor
|
||||
}
|
||||
|
||||
if (canInheritPrimaryMaterial(input.slot)) {
|
||||
@ -1008,7 +1020,8 @@ function resolvePhysicalMaterialDescriptor(input: {
|
||||
if (costFamilyDescriptor) return costFamilyDescriptor
|
||||
}
|
||||
|
||||
if (slotRecords.length > 0) return materialDescriptorFromRecords(slotRecords)
|
||||
const genericSlotMaterial = materialDescriptorLayer(slotRecords)
|
||||
if (genericSlotMaterial.hasRecognizedDescriptor) return genericSlotMaterial.descriptor
|
||||
if (!canInheritPrimaryMaterial(input.slot)) return undefined
|
||||
return explicitPrimaryMaterialDescriptor({
|
||||
materialRecords: allMaterialRecords,
|
||||
|
||||
@ -26,7 +26,7 @@ import {
|
||||
} from './category-skills'
|
||||
|
||||
export const BOM_COSTING_STRUCTURE_KNOWLEDGE_VERSION =
|
||||
'structure-priors/2026-08-13-v8' as const
|
||||
'structure-priors/2026-08-13-v9' as const
|
||||
|
||||
export type BomCostingStructureSlotPrior = BomCostingCategoryComponentDefinition
|
||||
|
||||
|
||||
@ -148,6 +148,15 @@ export function buildProductionMobileRattanStorageRack2FactSnapshot(): BomCostin
|
||||
basis: 'SOURCE_TEXT',
|
||||
confidence: 0.99,
|
||||
},
|
||||
{
|
||||
factId: 'IMG_0039_fact_004',
|
||||
kind: 'MATERIAL',
|
||||
subject: '上层托盘及置物箱框体',
|
||||
value: '表面呈棕色木纹外观',
|
||||
relatedSkuIds: [],
|
||||
basis: 'IMAGE',
|
||||
confidence: 0.9,
|
||||
},
|
||||
{
|
||||
factId: 'IMG_0039_fact_008',
|
||||
kind: 'STRUCTURE',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user