afb3f83f30
Engine precision (stop foreign-machine patterns leaking into a project):
- Wire project.MachineType into the engine machine-type gate (empty input no
longer fires every machine class — press/cnc/excavator/crane/medical...).
- Capability-domain gating extended by 7 domains (outdoor, ventilation,
machining, bulk, palletizer, playground, fitness) so domain-specific hazards
only fire when the narrative names that domain; emitted via keyword_dictionary.
- Relevance backstop moved into iace (single gating contract, testable), and its
dominant false-anchor class removed (a long pattern word no longer matches a
short common token; prepositions/leitung added to the generic stoplist).
- New guard tests: TestCrossDomainPrecision (full pipeline, 0 foreign per GT) and
TestPatternReachability now asserts 0 dead patterns. Both GTs keep coverage 1.0.
Reachability fix: the 51 dead patterns required electrical/pneumatic/hydraulic
tags nothing produced — renamed to the canonical electrical_energy/
pneumatic_pressure/hydraulic_pressure/hydraulic_part.
Component review (negation is best-effort + expert-correctable):
- Parser surfaces negated components (ComponentMatch.Negated) instead of dropping
them; negated contribute no tags/energy → no phantom hazards.
- presence_status (vorhanden|nicht_vorhanden|geloescht) + ce_marked on components;
only `vorhanden` feed matching. CE+safety-relevant flags the PL/SIL obligation.
- Force re-seed preserves the expert's component decisions instead of wiping them.
- Tag-based component→hazard assignment (was: all on the first component).
- Negation-aware narrative parsing ("keine Pneumatik" no longer extracts it).
Local-dev DB: ai-sdk sets search_path=compliance,core,public; reconcile migrations
152-156 bring the consolidated local iace tables to the current schema + add the
presence_status/ce_marked columns. Machine-type vocabulary endpoint for the form.
[migration-approved]
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
44 lines
1.3 KiB
Go
44 lines
1.3 KiB
Go
package handlers
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/breakpilot/ai-compliance-sdk/internal/iace"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
func TestPickComponentForPattern(t *testing.T) {
|
|
motor := uuid.New()
|
|
hubwerk := uuid.New()
|
|
def := uuid.New()
|
|
parseComps := []iace.ComponentMatch{
|
|
{NameDE: "Elektromotor (Drehstrom)", Tags: []string{"electrical", "rotating_part"}},
|
|
{NameDE: "Hubwerk", Tags: []string{"moving_part", "crush_point"}},
|
|
}
|
|
compByName := map[string]uuid.UUID{
|
|
iace.NormalizeDEPublic("Elektromotor (Drehstrom)"): motor,
|
|
iace.NormalizeDEPublic("Hubwerk"): hubwerk,
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
matchedTags []string
|
|
zone string
|
|
want uuid.UUID
|
|
}{
|
|
{"electrical tag → motor", []string{"electrical"}, "", motor},
|
|
{"crush tags → hubwerk", []string{"crush_point", "moving_part"}, "", hubwerk},
|
|
{"no overlap → default", []string{"unknown_tag"}, "", def},
|
|
{"zone fallback → hubwerk", nil, "Gefahr am Hubwerk-Bereich", hubwerk},
|
|
{"nothing → default", nil, "", def},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := pickComponentForPattern(tt.matchedTags, tt.zone, parseComps, compByName, def)
|
|
if got != tt.want {
|
|
t.Errorf("got %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|