fix(iace): Delta + FMEA — derive component tags from names when library_id missing
Build + Deploy / build-admin-compliance (push) Successful in 2m7s
Build + Deploy / build-backend-compliance (push) Successful in 3m42s
Build + Deploy / build-ai-sdk (push) Successful in 48s
Build + Deploy / build-developer-portal (push) Successful in 1m8s
Build + Deploy / build-tts (push) Successful in 1m38s
Build + Deploy / build-document-crawler (push) Successful in 1m0s
Build + Deploy / build-dsms-gateway (push) Successful in 29s
Build + Deploy / build-dsms-node (push) Successful in 19s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 15s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m36s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Successful in 51s
CI / test-python-backend (push) Successful in 37s
CI / test-python-document-crawler (push) Successful in 25s
CI / test-python-dsms-gateway (push) Successful in 21s
CI / validate-canonical-controls (push) Successful in 15s
Build + Deploy / trigger-orca (push) Successful in 3m28s

Auto-created components have no library_id. Delta analysis and FMEA now
derive pattern-engine-compatible tags from component names (e.g. "Roboter"
→ cobot/robot_arm, "SPS" → controller/plc, "Scanner" → sensor).

Also: new E2E test file iace-extensions.spec.ts (FMEA, Knowledge Graph,
Delta API, Failure Modes API).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-12 08:26:15 +02:00
parent df15f6f098
commit 6586d2cb5e
3 changed files with 282 additions and 25 deletions
@@ -108,15 +108,45 @@ export function useOperationalStates(projectId: string) {
setDeltaLoading(true)
setDeltaResult(null)
try {
// Build a MatchInput from the project's current components + proposed states
// Build MatchInput from project's components — derive tags from names/types
const compRes = await fetch(`/api/sdk/v1/iace/projects/${projectId}/components`)
let componentIds: string[] = []
let componentTags: string[] = []
let energyIds: string[] = []
if (compRes.ok) {
const cj = await compRes.json()
const comps = cj.components || cj || []
componentIds = comps.map((c: { library_id?: string }) => c.library_id).filter(Boolean)
energyIds = comps.flatMap((c: { energy_source_ids?: string[] }) => c.energy_source_ids || [])
const comps = (cj.components || cj || []) as Array<{ library_id?: string; component_type?: string; name?: string; energy_source_ids?: string[] }>
// Use library_ids if available, otherwise derive tags from component names/types
const libIds = comps.map((c) => c.library_id).filter(Boolean) as string[]
if (libIds.length > 0) {
componentTags = libIds
} else {
// Derive tags from component names for pattern matching
const tagMap: Record<string, string[]> = {
sensor: ['sensor', 'has_sensor'], software: ['software', 'has_software'],
firmware: ['firmware', 'has_firmware'], ai_model: ['has_ai', 'ai_model'],
hmi: ['hmi', 'display'], electrical: ['electric_motor', 'electric_drive'],
network: ['networked', 'ethernet'], actuator: ['actuator', 'hydraulic'],
mechanical: ['moving_mechanical_parts'], hydraulic: ['hydraulic'],
}
const nameKeywords: Record<string, string[]> = {
roboter: ['cobot', 'robot_arm'], motor: ['electric_motor', 'electric_drive'],
scanner: ['sensor', 'safety_scanner'], sps: ['controller', 'plc'],
steuerung: ['controller', 'plc'], greifer: ['actuator', 'gripper'],
schutzzaun: ['safety_fence'], lichtgitter: ['light_curtain'],
kamera: ['camera', 'sensor'], ventil: ['valve', 'pneumatic'],
}
const tags = new Set<string>()
for (const c of comps) {
const typeTags = tagMap[c.component_type || ''] || ['moving_mechanical_parts']
typeTags.forEach((t) => tags.add(t))
const nameLower = (c.name || '').toLowerCase()
for (const [kw, kwTags] of Object.entries(nameKeywords)) {
if (nameLower.includes(kw)) kwTags.forEach((t) => tags.add(t))
}
}
componentTags = [...tags]
}
energyIds = comps.flatMap((c) => c.energy_source_ids || [])
}
const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}/delta-analysis`, {
@@ -124,13 +154,15 @@ export function useOperationalStates(projectId: string) {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
current: {
component_library_ids: componentIds,
component_library_ids: componentTags,
energy_source_ids: energyIds,
custom_tags: componentTags,
operational_states: metadataRef.current.operational_states || [],
},
proposed: {
component_library_ids: componentIds,
component_library_ids: componentTags,
energy_source_ids: energyIds,
custom_tags: componentTags,
operational_states: states,
},
}),