feat(iace): cross-domain precision overhaul + component review + schema reconcile
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>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
-- Migration 152: reconcile iace_projects with the current Go schema.
|
||||
-- ==========================================================================
|
||||
-- The iace tables were created ad-hoc (no CREATE migration) and drifted across
|
||||
-- environments. The consolidation copied an OLDER iace_projects into the local
|
||||
-- `compliance` schema: it carried legacy columns (intended_use,
|
||||
-- limits_description, reasonably_foreseeable_misuse, completeness_pct,
|
||||
-- can_export) and lacked the columns the current store layer reads/writes
|
||||
-- (store_projects.go CreateProject/GetProject): customer_name, description,
|
||||
-- narrative_text, ce_marking_target, completeness_score, risk_summary,
|
||||
-- triggered_regulations, archived_at.
|
||||
--
|
||||
-- This migration brings the table to the code's expectation. Idempotent and
|
||||
-- guarded so it is a no-op where the table already matches. Legacy columns are
|
||||
-- only made nullable (not dropped) so any historical data survives while the
|
||||
-- current INSERT (which omits them) no longer fails on NOT NULL.
|
||||
-- ==========================================================================
|
||||
|
||||
ALTER TABLE iace_projects
|
||||
ADD COLUMN IF NOT EXISTS parent_project_id uuid,
|
||||
ADD COLUMN IF NOT EXISTS customer_name text,
|
||||
ADD COLUMN IF NOT EXISTS description text,
|
||||
ADD COLUMN IF NOT EXISTS narrative_text text,
|
||||
ADD COLUMN IF NOT EXISTS ce_marking_target text,
|
||||
ADD COLUMN IF NOT EXISTS completeness_score double precision,
|
||||
ADD COLUMN IF NOT EXISTS risk_summary jsonb,
|
||||
ADD COLUMN IF NOT EXISTS triggered_regulations jsonb,
|
||||
ADD COLUMN IF NOT EXISTS archived_at timestamptz;
|
||||
|
||||
-- Relax legacy NOT NULL columns the current code never writes, so INSERTs that
|
||||
-- omit them succeed. DROP NOT NULL is a no-op when already nullable; the guard
|
||||
-- skips columns that do not exist in this schema variant.
|
||||
DO $$
|
||||
DECLARE
|
||||
col text;
|
||||
BEGIN
|
||||
FOREACH col IN ARRAY ARRAY[
|
||||
'intended_use', 'limits_description', 'reasonably_foreseeable_misuse',
|
||||
'completeness_pct', 'can_export'
|
||||
] LOOP
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'iace_projects' AND column_name = col
|
||||
) THEN
|
||||
EXECUTE format('ALTER TABLE iace_projects ALTER COLUMN %I DROP NOT NULL', col);
|
||||
END IF;
|
||||
END LOOP;
|
||||
END $$;
|
||||
@@ -0,0 +1,70 @@
|
||||
-- Migration 153: reconcile iace_components / iace_hazards / iace_mitigations
|
||||
-- with the current Go schema.
|
||||
-- ==========================================================================
|
||||
-- Same drift as 152 (iace_projects): the consolidation copied an OLDER table
|
||||
-- generation with different column names (title vs name, component_ids vs
|
||||
-- component_id, safety_relevant vs is_safety_relevant). This adds every column
|
||||
-- the current store layer reads/writes and relaxes legacy NOT NULL columns the
|
||||
-- code no longer fills, so INSERTs succeed. Idempotent + guarded.
|
||||
-- ==========================================================================
|
||||
|
||||
-- iace_components
|
||||
ALTER TABLE iace_components
|
||||
ADD COLUMN IF NOT EXISTS is_safety_relevant boolean NOT NULL DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS is_networked boolean NOT NULL DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS sort_order integer NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS parent_id uuid,
|
||||
ADD COLUMN IF NOT EXISTS description text,
|
||||
ADD COLUMN IF NOT EXISTS version text,
|
||||
ADD COLUMN IF NOT EXISTS metadata jsonb;
|
||||
|
||||
-- iace_hazards
|
||||
ALTER TABLE iace_hazards
|
||||
ADD COLUMN IF NOT EXISTS component_id uuid,
|
||||
ADD COLUMN IF NOT EXISTS library_hazard_id uuid,
|
||||
ADD COLUMN IF NOT EXISTS name text,
|
||||
ADD COLUMN IF NOT EXISTS scenario text,
|
||||
ADD COLUMN IF NOT EXISTS sub_category text,
|
||||
ADD COLUMN IF NOT EXISTS machine_module text,
|
||||
ADD COLUMN IF NOT EXISTS function text,
|
||||
ADD COLUMN IF NOT EXISTS lifecycle_phase text,
|
||||
ADD COLUMN IF NOT EXISTS hazardous_zone text,
|
||||
ADD COLUMN IF NOT EXISTS trigger_event text,
|
||||
ADD COLUMN IF NOT EXISTS affected_person text,
|
||||
ADD COLUMN IF NOT EXISTS possible_harm text,
|
||||
ADD COLUMN IF NOT EXISTS review_status text;
|
||||
|
||||
-- iace_mitigations
|
||||
ALTER TABLE iace_mitigations
|
||||
ADD COLUMN IF NOT EXISTS name text,
|
||||
ADD COLUMN IF NOT EXISTS verification_method text,
|
||||
ADD COLUMN IF NOT EXISTS verification_result text,
|
||||
ADD COLUMN IF NOT EXISTS verified_at timestamptz,
|
||||
ADD COLUMN IF NOT EXISTS verified_by uuid,
|
||||
ADD COLUMN IF NOT EXISTS is_relevant boolean NOT NULL DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS is_customer_standard boolean NOT NULL DEFAULT false;
|
||||
|
||||
-- Relax legacy NOT NULL columns the current code never writes.
|
||||
DO $$
|
||||
DECLARE
|
||||
rec record;
|
||||
BEGIN
|
||||
FOR rec IN SELECT * FROM (VALUES
|
||||
('iace_components','tenant_id'),('iace_components','safety_relevant'),('iace_components','tags'),
|
||||
('iace_hazards','tenant_id'),('iace_hazards','title'),('iace_hazards','component_ids'),
|
||||
('iace_hazards','library_id'),('iace_hazards','severity'),('iace_hazards','exposure'),
|
||||
('iace_hazards','probability'),('iace_hazards','avoidance'),('iace_hazards','risk_score'),
|
||||
('iace_hazards','risk_level'),
|
||||
('iace_mitigations','tenant_id'),('iace_mitigations','project_id'),('iace_mitigations','title'),
|
||||
('iace_mitigations','sub_type'),('iace_mitigations','control_ids'),
|
||||
('iace_mitigations','verification_evidence')
|
||||
) AS t(tbl, col)
|
||||
LOOP
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = rec.tbl AND column_name = rec.col
|
||||
) THEN
|
||||
EXECUTE format('ALTER TABLE %I ALTER COLUMN %I DROP NOT NULL', rec.tbl, rec.col);
|
||||
END IF;
|
||||
END LOOP;
|
||||
END $$;
|
||||
@@ -0,0 +1,12 @@
|
||||
-- Migration 154: add the mitigation UNIQUE/CHECK that the current code relies on.
|
||||
-- The reconciled (consolidated) iace_mitigations lacked the constraints from
|
||||
-- migrations 029/030, so CreateMitigation's `ON CONFLICT (hazard_id, name)`
|
||||
-- failed (SQLSTATE 42P10) and no mitigations were created. Idempotent.
|
||||
|
||||
ALTER TABLE iace_mitigations
|
||||
DROP CONSTRAINT IF EXISTS iace_mitigations_hazard_name_uniq;
|
||||
ALTER TABLE iace_mitigations
|
||||
ADD CONSTRAINT iace_mitigations_hazard_name_uniq UNIQUE (hazard_id, name);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_iace_mitigations_relevant
|
||||
ON iace_mitigations(hazard_id) WHERE is_relevant = TRUE;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Migration 155: component presence status for expert review.
|
||||
-- vorhanden | nicht_vorhanden (engine negation verdict) | geloescht (soft-delete).
|
||||
-- Only `vorhanden` components feed pattern matching.
|
||||
ALTER TABLE iace_components
|
||||
ADD COLUMN IF NOT EXISTS presence_status text NOT NULL DEFAULT 'vorhanden';
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Migration 156: ce_marked on iace_components.
|
||||
-- Marks a bought component that carries its own CE / Declaration of Conformity
|
||||
-- (finished robot, actuator, drive, safety PLC ...). SAFE semantics: hazards are
|
||||
-- NOT suppressed (integration/application hazards remain the integrator's job);
|
||||
-- ce_marked only drives provenance + evidence hints in the UI, and flags that a
|
||||
-- CE + safety-relevant component still needs its integrated safety function
|
||||
-- (PL/SIL) validated.
|
||||
ALTER TABLE iace_components
|
||||
ADD COLUMN IF NOT EXISTS ce_marked boolean NOT NULL DEFAULT false;
|
||||
Reference in New Issue
Block a user