From 1ffdb99650b0450aef369f5fd8059473375153c0 Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Tue, 9 Jun 2026 16:44:29 +0200 Subject: [PATCH] fix(iace): narrative extractor ignored most Grenzen fields (field-name mismatch) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit extractNarrativeFromMetadata looked for field names that don't exist in the real limits-form schema (interfaces_description, control_system_description, energy_sources, space_limits, foreseeable_misuse), so it effectively read only general_description + intended_purpose. The electrical/mechanical/pneumatic/ software interface fields — each a hazard source — were silently dropped, which is why electrical hazard coverage was 9% for the Kistenhubgeraet. Now reads the actual schema fields incl. electrical_interfaces / mechanical_interfaces / pneumatic_hydraulic_interfaces / software_interfaces / energy_supply / spatial_limits / foreseeable_misuses, plus array fields (operating_modes, person_groups, industry_sectors). Legacy names kept. Co-Authored-By: Claude Opus 4.7 --- .../api/handlers/iace_handler_init_helpers.go | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/ai-compliance-sdk/internal/api/handlers/iace_handler_init_helpers.go b/ai-compliance-sdk/internal/api/handlers/iace_handler_init_helpers.go index a0651935..2f87f5a0 100644 --- a/ai-compliance-sdk/internal/api/handlers/iace_handler_init_helpers.go +++ b/ai-compliance-sdk/internal/api/handlers/iace_handler_init_helpers.go @@ -26,23 +26,51 @@ func extractNarrativeFromMetadata(metadata json.RawMessage) string { return "" } + // Every limits-form field that carries machine-describing text. Field + // names MUST match the real form schema — the per-interface fields + // (electrical/mechanical/pneumatic/software) each contribute hazards, not + // just the prose. Legacy names are kept so older projects still parse. textFields := []string{ - "general_description", "intended_purpose", "foreseeable_misuse", - "space_limits", "time_limits", "environmental_conditions", - "energy_sources", "materials_processed", "operating_modes", - "maintenance_requirements", "personnel_requirements", - "interfaces_description", "control_system_description", - "safety_functions_description", + // description / purpose + "general_description", "machine_designation", "intended_purpose", + "foreseeable_misuse", "foreseeable_misuses", "variants", "area_of_use", + // limits + "space_limits", "spatial_limits", "time_limits", "temporal_limits", + "environmental_conditions", "operating_conditions", + // energy + materials + "energy_sources", "energy_supply", "materials_processed", + // interfaces (the previously-ignored ones) + "interfaces_description", "control_system_description", "safety_functions_description", + "electrical_interfaces", "mechanical_interfaces", + "pneumatic_hydraulic_interfaces", "software_interfaces", + // people / maintenance + "maintenance_requirements", "personnel_requirements", "qualification_requirements", } - var result string + arrayFields := []string{"operating_modes", "person_groups", "industry_sectors"} + + var sb strings.Builder for _, field := range textFields { if v, ok := limits[field]; ok { - if s, ok := v.(string); ok && s != "" { - result += s + "\n\n" + if s, ok := v.(string); ok && strings.TrimSpace(s) != "" { + sb.WriteString(s) + sb.WriteString("\n\n") } } } - return result + for _, field := range arrayFields { + if v, ok := limits[field]; ok { + if arr, ok := v.([]interface{}); ok { + for _, e := range arr { + if s, ok := e.(string); ok && s != "" { + sb.WriteString(s) + sb.WriteString(", ") + } + } + sb.WriteString("\n\n") + } + } + } + return sb.String() } // acceptableMeasureCategories returns the set of measure HazardCategory values