Commit Graph

263 Commits

Author SHA1 Message Date
Benjamin Admin 0148d55304 feat(iace): overnight NASA NTRS failure-knowledge harvester
Unattended, bounded (MAX_DOCS), resumable pipeline: NTRS lessons-learned →
public-reuse licence gate → download PDF → pdftotext / abstract / vision-OCR
fallback → Ollama tuple extraction → results.jsonl + bp_iace_failure_kb,
tagged verified=false + provenance for morning review. Never touches the
curated Go set.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-13 00:36:44 +02:00
Benjamin Admin d27c1b9e7d feat(iace): NTRS harvester + licence gate (FMEA P2 stage 1)
Stage 1 of the FailureKnowledge bulk loader: harvest NASA NTRS
lessons-learned with a strict public-reuse gate (NTRSUsable: public
release, not export-controlled/EAR/ITAR, not CUI, PUBLIC_USE_PERMITTED,
no third-party copyright). NTRSPDFURL prefers the PDF download for
downstream text/OCR extraction. GET /iace/failure-knowledge/ntrs runs
the live harvest and returns only the licence-clean records.

Pure parse/gate helpers are fixture-tested (usable vs ITAR / third-party
/ restricted / video-only); accepted licences also pass the FK allowlist.

Next: tuple extraction (abstract -> FailureKnowledge) + Playwright/OCR for
scanned PDFs -> bp_iace_failure_kb.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-13 00:16:41 +02:00
Benjamin Admin fa8ad030cb feat(iace): unified FailureKnowledge ontology + NASA starter (FMEA P2)
The source-agnostic failure ontology shared by the FMEA library and the CE
hazard side: Component → FailureMode → Mechanism → Effect → Hazard → Harm →
Control, each row source+licence tagged. A licence ALLOWLIST
(FailureKnowledgeLicenseAllowed) rejects copyrighted/proprietary/NC sources
up front (© IITRI, DIN/ISO, AIAG, OREDA, CC-BY-NC) — the discipline learned
from the FMD-91/NPRD-91 licence finding.

Seeded with a curated NASA NTRS lessons-learned starter (5 real entries,
public domain). GET /iace/failure-knowledge (+ ?domain=). Tests pin the
governance invariant: every entry must carry a commercially-usable licence.

Next: Playwright+OCR bulk loader (NTRS API → PDF/OCR → tuple extraction) to
grow the corpus from NASA/OSHA/CPSC/MAUDE/NTSB.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-13 00:05:52 +02:00
Benjamin Admin cb82ff74c8 fix(iace): correct FMD-91/NPRD-91 licence — NOT public domain
Verified the actual PDF cover pages: FMD-91 (ADA259655) and NPRD-91
(ADA242083) carry "© 1991, IIT Research Institute. All Rights Reserved"
plus a DoD "distribution unlimited" statement. The distribution statement
permits obtaining/reading the document, NOT reproducing its tables in a
commercial product — treat like DIN/ISO. The earlier P1 docs wrongly
labelled them "public domain" (an unverified research claim).

- Correct the licence in fmea_data_sources.go note + mil_std_1629a_fmeca.md
  + fmd91_nprd_failure_modes.md (read-reference only; tables NOT reproduced).
- The bp_iace_fmea_kb collection was deleted from Qdrant (the mislabelled
  doc removed); methodology docs (MIL-STD/NASA, genuine PD) not re-ingested
  pending review. The Go methodology module (own scales, MIL-STD-anchored)
  is unaffected and stays.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 23:41:13 +02:00
Benjamin Admin de140e564e feat(iace): FMEA P1 — open methodology anchors + bp_iace_fmea_kb
P1 of the auto-FMEA build plan: establish the public-domain methodology
foundation (no AIAG-VDA/SAE/IEC tables reproduced).
- fmea_data_sources.go: MIL-STD-882E severity (Cat I-IV→1-10) + probability
  (A-F→1-10 with per-hour λ bands), OccurrenceFromRate(λp·α), SeverityForCategory,
  MIL-STD-1629A CriticalityCm = λp·α·β·t. Own 1-10 projection, government-anchored.
- 4 versioned source docs (MIL-STD-1629A, MIL-STD-882E, NASA RCM, FMD-91/NPRD-91)
  ingested into the new RAG collection bp_iace_fmea_kb (whitelisted).
- Tests for all scales/mappings/criticality (green).

Next (P1 step 2): fetch FMD-91/NPRD-91 bulk λ/α tables from DTIC.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 22:59:01 +02:00
Benjamin Admin 755ea44343 feat(iace): refresh architecture tab + data-flow diagram + E1 ingest script
- architecture.go: DataSources now reflect the real ingested set (ESAW 2023,
  BLS CFOI, OSHA OTM, PRISM, cobot CC-BY, HSE) with their RAG collections;
  risk stage cites BLS + the searchable RAG layer; matrix stage now mentions
  the distance-benchmark dimension.
- Architektur & Datenfluss tab: new DataFlowDiagram — 4 lanes (input →
  knowledge/RAG-evidence → deterministic engine → outputs) with live counts.
- scripts/ingest_iace_kb.sh: idempotent E1 ingest — creates the 2 collections
  and uploads the 6 datasources docs against a configurable RAG_URL (for prod
  Qdrant), with retry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 09:18:03 +02:00
Benjamin Admin c35977c925 docs(iace): verify cobot biomech limits against CC-BY papers
Cross-checked cobot_biomech_limits.md against both source papers:
- Behrens et al. 2022 (Frontiers): 10 body regions spot-checked, force
  values match the paper EXACTLY in both columns (pinching + impact).
- Park et al. 2019 (PLOS ONE): lowest/highest/range pressure values exact.
Fix: 28 -> 29 body locations; add a verification stamp. Threshold VALUES
were already correct (no data change), so no RAG re-ingest needed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 20:50:58 +02:00
Benjamin Admin b0ceae4350 feat(iace): open-source safety KB sources + bp_iace_safety_kb (Thema 2)
Versioned, license-tagged source docs for the multi-layer GT knowledge base,
ingested into the new core RAG collection bp_iace_safety_kb (whitelisted in
the RAG search handler):
- prism_risk_methodology.md — OPSS PRISM v2 (OGL v3): full severity(4)×
  probability(8) → risk-level matrix (Serious/High/Medium/Low), RAPEX-aligned.
- cobot_biomech_limits.md — CC BY 4.0 papers (Behrens 2022 / Park 2019):
  force (N) & pressure (N/cm²) pain thresholds by body region (the data behind
  ISO/TS 15066, cited from the open papers — standard tables NOT reproduced).
- hse_example_risk_assessments.md — HSE (OGL v3): qualitative hazard→control.
- osha_robot_safety.md — OSHA OTM (public domain): 250 mm/s teach anchor,
  robot hazard taxonomy, safeguarding hierarchy.

No DIN/EN/ISO/IEC/DGUV content reproduced; each doc states its license + attribution.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 19:46:57 +02:00
Benjamin Admin dbb15dbb78 feat(iace): add BLS CFOI fatal-injury source doc (D1)
US severity anchor complementing ESAW: BLS Census of Fatal Occupational
Injuries (public domain), event/exposure distribution 2023-24 + the
machine-relevant "Contact incidents" breakdown (struck/caught/compressed
by running powered equipment: 226/213). Key finding: in MANUFACTURING,
contact is the leading fatal event (104/353 = 29.5%) — independent support
for the model's mechanical-contact emphasis. Ingested into the core RAG
collection bp_iace_accident_stats.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 15:57:31 +02:00
Benjamin Admin 0f443b6a9c fix(iace): roadmap group B — citation/license/tier cleanup
C1: drop the misleading OSHA §1910.212(a)(5) fan-guard citation from M602
    (overhead lift clearance) — EN 349 + EN ISO 13854 already cover it.
C2: frame M237's 25/500 mm as Richtwerte to be determined per EN ISO 13854
    (single factual values in prose are facts, not table reproduction — but
    keep the conservative caveat).
C3: keep ergonomic W=2 deliberately and document why — ESAW ranks it the most
    frequent non-fatal mode (24.7%) but that population doesn't transfer to an
    acute machine point-hazard; the machine GT governs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 15:21:25 +02:00
Benjamin Admin 86c0ea6f63 fix(iace): wire M605/M606 into lift patterns so they fire
Adding M605 (drive-limited general speed) and M606 (limited descent on
energy loss) to the library wasn't enough — measures only get suggested
if a pattern's SuggestedMeasureIDs references them. Add M605 to the three
lift crush patterns and M606 to the floor-stop descent pattern (HP2100),
so a re-seed actually attaches them and the distance benchmark closes the
≤150 mm/s gap.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 15:06:33 +02:00
Benjamin Admin 0d7194ef89 feat(iace): add distance dimension to GT benchmark
CompareBenchmark now also compares the engine's numeric dimensions
(mm gaps, mm/s speeds) against the professional's GT measures: parses
distance tokens from both sides (German thousands/decimal aware),
reports matched / gt_only (gaps) / engine_only + an agreement %.
Surfaces as result.distances on the existing benchmark endpoint.

Deterministic, no LLM. On the GT-derived seed sessions it mainly guards
DRIFT; its real value is new sessions. Real-GT test pins that the engine
covers the Bremse (250 mm/s, 250/850 mm) and Kistenhub (25/120 mm,
150/75 mm/s) headline dimensions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 14:59:47 +02:00
Benjamin Admin b63f49344a feat(iace): fill lift-measure distance gaps vs GT (M603/M605/M606)
The GT distance benchmark surfaced three Fachmann lift values the engine
carried no measure for: general lift/lower speed (≤150 mm/s), the low-zone
inching regime (<200 mm floor clearance, ≤75 mm/s), and limited descent on
power loss (≤100 mm). Extend M603 (inching) and add M605 (drive-limited
general speed) + M606 (load-holding on energy loss). Values framed as
generic hoist recommendations with EN 1570-1 reference, not GT-memorised.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 14:47:21 +02:00
Benjamin Admin 6b41eec176 feat(iace): surface OSHA distance anchor in Maßnahmen tab (name-resolved)
Makes the OSHA minimum-distance anchor visible per measure in a project
without a DB schema change or re-seed: persisted mitigations store the
measure NAME verbatim (not the catalog ID), and measure names are unique
across the 578-entry library (pinned by test), so a name→ID resolver
bridges the gap.

Backend: MeasureIDByName + MinimumDistancesForMeasureName/LinksForMeasureName;
/iace/minimum-distances now accepts ?measure_name=; link table enriched with
measure_name for one-request UI matching.
Frontend: useMinimumDistances loads the link table once and keys it by name;
OshaDistanceNote renders the anchor (value/CFR/license/EU-hint/relation) on the
matching measure group in the Maßnahmen tab.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 13:39:48 +02:00
Benjamin Admin 5e18df63b1 feat(iace): ESAW accident-stats RAG pipeline + real 2023 risk anchors
Executes the accident-statistics pipeline for the risk anchors:
- Refresh contactModeEvidence with real Eurostat ESAW figures
  (dataset hsw_ph3_08, reference year 2023): impact 24.0%/21.4%,
  struck-by 13.0%/23.8%, sharp 14.5%, trapped/crushed 13.8% (fatal),
  + new physical/mental-stress mode 24.7% → ergonomic. GT-calibrated
  tier VALUES unchanged; the real data confirms the ordering.
- Add the versioned source document (datasources/esaw_accident_stats_2023.md,
  ESAW CC BY 4.0 + OSHA public-domain context) that is ingested into the
  core RAG collection bp_iace_accident_stats for searchable evidence.
- Whitelist bp_iace_accident_stats in the RAG search handler so seeding
  can full-text search the statistics with citation at seed time.

Two-layer design: the small license-tagged code table stays the deterministic
tier/citation lookup; the RAG holds the searchable source evidence.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 12:12:02 +02:00
Benjamin Admin 6846ca6b28 feat(iace): wire OSHA minimum-distance library into measures + endpoint
The May-built OSHA distance library (minimum_distances.go, 29 CFR 1910,
US public domain) was dead code — zero callers, no route, no test, while
the mm values that actually appear in measures are independent hand-prose
(some carrying ISO 13854/13857 values, not OSHA).

This surfaces it without touching the measures response contract:
- GET /iace/minimum-distances (+ ?measure_id=) returns the distances, the
  curated measure→distance link table and the licensing note.
- AllMeasureDistanceLinks/MinimumDistancesForMeasure resolve only the
  defensible links (M600 value_source; M254/M065 public-domain crossref to
  ISO), with the relation made explicit so the join stays honest.
- architecture.go lists the OSHA library so it shows in the audit explainer.
- Tests: inch→mm conversion + license completeness, link integrity, and a
  consistency test pinning that a value_source measure's prose still
  matches the OSHA source (codifies the audit finding as a regression gate).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 11:17:56 +02:00
Benjamin Admin 32ba8d16b1 feat(iace): add data-driven Architektur & Datenfluss explainer tab
Adds an auditor-facing view of the IACE engine: a clickable 10-stage
pipeline flow (Grenzen-Formular → ParseNarrative → Pattern-Gates →
Relevanz → Caps → Gefährdungen → Maßnahmen → Risiko → Normen → Matrix),
plus live library counts, the data-source/license register (incl. the
DIN/Beuth + DGUV exclusions), and the norm-matching logic that reconciles
DIN/ISO/OSHA machine-type vocabulary via canonicalMachineType folding.

Backend: BuildArchitecture() with LIVE counts so the diagram can never
drift; GET /iace/architecture; collectAllNorms() extracted from
SuggestNorms as the single source of truth for the norm-library count.
Frontend: useArchitecture hook + page + new IACE nav tab.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 09:35:37 +02:00
Benjamin Admin ee64b7e95c feat(iace): cite ESAW source + license on risk-frequency anchors
Surfaces the public-statistics provenance for the contact-mode probability
tiers so generated risk numbers are auditable and attributed (not RAG —
~a dozen stable aggregate facts are better as a license-tagged code table).

- risk_data_sources.go: RiskEvidence register (Eurostat ESAW figures + CC BY
  4.0 attribution) for the documented contact modes; RiskDataSourcesNote.
- risk_suggestion.go: the W justification now cites the actual ESAW share +
  license where documented; RiskSuggestion gains a data_source field.
- GET /iace/risk-data-sources returns the evidence register + attribution.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 09:14:36 +02:00
Benjamin Admin 577ceae4e6 feat(iace): project-wide risk matrix (Severity × Probability)
Adds GET /projects/:id/risk-matrix — a confidence-aware risk view computed
on read from each hazard's category/scenario/lifecycle using the SAME model
as the GT benchmark (no persistence, so it never goes stale against the
model; the hand-defaulted iace_hazards risk columns stay untouched).

- risk_matrix.go: EstimateHazardRisk (single source of truth for S/F/W/P +
  range + level + confidence) and BuildRiskMatrix (per-hazard list + a 5×5
  Severity×Probability aggregation grid with dominant level per cell).
- Frontend: RiskMatrix grid in the Risikobewertung tab (muted colours per
  the confidence-aware tonality), level counts + tool-confidence summary,
  fed by useRiskMatrix. Shows risk for EVERY project, not only GT ones.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 08:54:47 +02:00
Benjamin Admin a7dc12f30f feat(iace): risk as confidence range + label in benchmark tab
Report the tool's risk number as a plausible range with a confidence
label instead of a false-precision point value (confidence-aware
tonality — the assessment is confirmed by the DSB / safety expert).

- risk_estimation.go: EstimateConfidence (hoch/mittel/niedrig from how the
  contact mode resolved), EstimateRiskRange (S±1 and aggregate L=F+W+P ±1,
  the empirically validated per-parameter accuracy), RiskLevelRange; share
  the riskBandLabel thresholds with EstimateRiskLevel.
- risk_benchmark.go: RiskComparisonPair gains eng_risk_point/low/high +
  level + level_range + confidence; RiskAgreement gains high_confidence_pct.
- RiskComparison.tsx: per-hazard range "low–high (level range)" + point,
  confidence chip, and an aggregate confidence line; types in useBenchmark.ts.
- Unit tests for the range/confidence helpers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 23:04:56 +02:00
Benjamin Admin 005a2ed711 feat(iace): generic cross-domain leak gates + norm vocab reconciliation
- Domain-gate ~15 foreign machine classes (pool, amusement, paint booth,
  tank farm, reactor, lathe/chips, saw, film/carton, robot, mobile cab,
  asbestos, playground swing) in pattern_domain_gates.go so ungated hazard
  patterns stop leaking into unrelated machines; matching emit keywords
  added in keyword_dictionary.go (gate+emit share one vocabulary).
- Extend the cross-domain precision guard to 6 machine classes (press,
  cobot, motor, welding + the 2 GTs) with per-case homeDomains, so a
  machine's own domain terms are never flagged. GT coverage stays 100%.
- Reconcile the fine-grained norm machine-type vocabulary (455 keys) with
  the 68 canonical dropdown keys via canonicalMachineType() family folding
  in matchNorm — welding 0->17, robotics_cobot 0->6, press 8->13,
  circular_saw 1->35 machine-specific C-norms. Pattern gating left strict.
- Fix initialize?force=true summary index-shift that mislabeled counts
  (reported matched-patterns as "hazards"); now uses named step vars.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 22:29:10 +02:00
Benjamin Admin 3aa49f9553 Merge origin/main into iace precision/component-review work
Resolved .claude/rules/loc-exceptions.txt: removed the temporary
iace_handler_init_helpers.go exception — the file is now split to 455 lines
(< 500) in commit afb3f83, so the exception is no longer needed (per the note
the other session left on that entry).

[guardrail-change]
2026-06-10 17:24:49 +02:00
Benjamin Admin afb3f83f30 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>
2026-06-10 17:15:55 +02:00
Benjamin Admin 3e2bd91209 fix(ci): unblock deploy on main — test-go vet, loc-budget, build-sha
CI / detect-changes (push) Successful in 15s
CI / branch-name (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / build-sha-integrity (push) Successful in 8s
CI / validate-canonical-controls (push) Successful in 13s
CI / loc-budget (push) Successful in 20s
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) Has been skipped
CI / test-go (push) Successful in 58s
CI / iace-gt-coverage (push) Successful in 26s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-backend (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
test-go (go vet runs as part of go test) failed on two pre-existing iace spots:
- cmd/iace-audit/main.go: 6x fmt.Println with redundant trailing \n
- internal/iace/document_export_sources.go: duplicate `r == ';'` clause

build-sha-integrity failed because the alpine job installs python3 but not
pyyaml, so `import yaml` raised ModuleNotFoundError. Add py3-yaml to apk.

loc-budget flagged iace_handler_init_helpers.go (530 lines, committed state).
The other session already split it to 455 in the working tree (uncommitted);
grandfather it until that split lands, then remove the exception.

Verified locally: go test ./... all ok, go vet clean, check-loc.sh exit 0.

[guardrail-change]

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 14:17:27 +02:00
Benjamin Admin c4d9b1426f fix(iace): lower EstimateFrequency tiers — engine F was ~1 too high vs the GT
CI / build-sha-integrity (push) Failing after 4s
CI / validate-canonical-controls (push) Successful in 11s
CI / loc-budget (push) Failing after 15s
CI / go-lint (push) Has been skipped
CI / detect-changes (push) Successful in 6s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / test-go (push) Failing after 37s
CI / iace-gt-coverage (push) Successful in 23s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
Diagnosis: engine F mean 3.56 vs professional 2.56; the dominant disagreement was
normal-operation hazards getting F=4 where the professional assigned 2. Lowered
the lifecycle→F mapping (normal operation 4→3, occasional phases 3→2). New
TestGT_RiskComparison_CrossGT runs the exact production comparison on BOTH GTs:
F within±1 rose to 95% (robot cell) and 94% (lift) — generic, not lift-tuned.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 19:02:18 +02:00
Benjamin Admin 2677bca9ca feat(iace): benchmark risk comparison (traffic lights) + misuse pattern + 1:n matcher
CI / detect-changes (push) Successful in 7s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 4s
CI / validate-canonical-controls (push) Successful in 11s
CI / loc-budget (push) Failing after 14s
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 2m23s
CI / test-go (push) Failing after 37s
CI / iace-gt-coverage (push) Successful in 24s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
#1 Risk-number comparison in the benchmark: ComputeRiskComparison derives the
tool's S/F/W/P + Fine-Kinney per matched hazard and compares to the GT values;
exposed on the benchmark response and rendered in a new RiskComparison table
with GREEN/YELLOW/RED traffic lights on the risk number R (like the Excel),
plus per-axis within-1 agreement cards.

#2 Generic misuse pattern HP2103 "Personenbefoerderung auf Hebezeug" — gated to
lift-family machine types, fires for ANY lifting device (not machine-specific).

#3 Benchmark matcher is now 1:n — one broad engine hazard may cover several
fine-grained GT sub-scenarios (foot/hand/leg crush), so coverage reflects real
risk coverage rather than 1:1 wording matches.

Validated on BOTH ground truths (robot cell + lift): leakage 0, ghosts 0,
coverage held.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 17:24:52 +02:00
Benjamin Admin 0f04eee746 feat(iace): read ALL limits-form fields + always include universal lifecycles
CI / detect-changes (push) Successful in 5s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 5s
CI / validate-canonical-controls (push) Successful in 11s
CI / loc-budget (push) Failing after 14s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / test-go (push) Failing after 37s
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / iace-gt-coverage (push) Successful in 23s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
(1) extractNarrativeFromMetadata now reads every limits-form field generically
(no whitelist) — intended use, foreseeable misuse, all machine limits and all
four interface groups (electrical/mechanical/pneumatic/software). Field-schema
drift no longer silently drops hazard sources.

(2) withUniversalLifecycles always adds normal_operation/setup/maintenance/
cleaning to the matched lifecycle phases — these occur on virtually every
machine and the professional assesses them, so their hazards must be derived
even when the form omits them.

Kistenhubgeraet recall jumped 42.9% -> 74.3% (electrical 9% -> 82%) from the
field-name fix alone; this broadens it further.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 16:50:06 +02:00
Benjamin Admin 1ffdb99650 fix(iace): narrative extractor ignored most Grenzen fields (field-name mismatch)
CI / test-go (push) Failing after 36s
CI / iace-gt-coverage (push) Successful in 23s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
CI / detect-changes (push) Successful in 6s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 5s
CI / validate-canonical-controls (push) Successful in 12s
CI / loc-budget (push) Failing after 14s
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) Has been skipped
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 <noreply@anthropic.com>
2026-06-09 16:44:29 +02:00
Benjamin Admin a48e919caa fix(iace): scan ZoneDE in domain gate (catches zone-only domain hints)
CI / detect-changes (push) Successful in 6s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / test-go (push) Failing after 37s
CI / iace-gt-coverage (push) Successful in 23s
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 4s
CI / validate-canonical-controls (push) Successful in 11s
CI / loc-budget (push) Successful in 14s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / test-python-backend (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
A "Splitterflug bei Werkzeugbruch" pattern leaked into a lift re-seed because
its press hint ("Pressraum") lives in ZoneDE, which applyDomainGates did not
scan. Add ZoneDE to the gated text. Leakage stays 0, ghosts 0, coverage held.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 16:15:34 +02:00
Benjamin Admin 7b3a6f0dcd fix(iace): close domain-gate gaps — generic patterns with press/welding/glass text
CI / loc-budget (push) Successful in 15s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / detect-changes (push) Successful in 6s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 4s
CI / validate-canonical-controls (push) Successful in 12s
CI / nodejs-build (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go (push) Failing after 37s
CI / iace-gt-coverage (push) Successful in 23s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
Observed on a real Kistenhubgeraet (lift) project: generic mechanical patterns
(e.g. HP1000 "Quetschen Arm zwischen Pressenteilen") carry NO machine type and
only generic tags (crush_point, rotating_part), so they fired for a lift; the
narrow domain-gate terms missed their press/welding/glass wording.

Broadens domainGateTerms (pressenteil, pressraum, blechbearbeitung,
punktschweiss, schweisselektrod, elektrodenspalt) and adds a dom_glass domain
(glasschneid/glasbearbeitung/...) with its emit keywords. New test pins that the
four observed leakers now require a dom_* tag. Ghost=0, Leakage=0, coverage held
on both GTs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 16:08:02 +02:00
Benjamin Admin 77536f04b7 feat(iace): dual-model risk-suggestion endpoint for Risikobewertung tab
CI / detect-changes (push) Successful in 8s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 4s
CI / validate-canonical-controls (push) Successful in 11s
CI / loc-budget (push) Successful in 14s
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) Has been skipped
CI / test-go (push) Failing after 38s
CI / iace-gt-coverage (push) Successful in 23s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
GET /projects/:id/hazards/:hid/risk-suggestion returns BreakPilot's justified
starting values for BOTH risk models per hazard:
- EN-62061-style F/W/P/S (the Excel format the professional knows)
- Fine-Kinney P/E/C (US-recognized)
each with a plain-language justification + the visible formula. Read-only and
computed from public-data anchors (ESAW/NIOSH/OSHA via the engine estimators) —
the professional adjusts the values; no norm table is stored or reproduced.

Adds EstimateFrequency (lifecycle -> 1-5) and BuildRiskSuggestion. Go SDK has no
OpenAPI baseline, so the only contract surface is the frontend consumer (the new
Risikobewertung tab, next).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 15:35:39 +02:00
Benjamin Admin 0bf9c54d27 feat(iace): add Fine-Kinney risk model (citable, free, US-recognized)
CI / detect-changes (push) Successful in 6s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 5s
CI / validate-canonical-controls (push) Successful in 11s
CI / loc-budget (push) Successful in 15s
CI / go-lint (push) Has been skipped
CI / test-go (push) Failing after 38s
CI / iace-gt-coverage (push) Successful in 23s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
Fine-Kinney (Fine 1971 / Kinney-Wiruth 1976): Risk = Probability x Exposure x
Consequence — a PUBLISHED, freely-usable method (not a DIN/Beuth/ISO standard),
widely used incl. CE-marking. Gives the professional a second, US-recognized
model alongside the EN-62061-style one; German exporters get both for free and
adjust with their own licensed norm data.

risk_fine_kinney.go: SuggestFineKinney derives justified P/E/C from public
anchors (ESAW frequency -> P, lifecycle -> E, de-biased severity -> C on the
Fine-Kinney consequence scale) + ComputeFineKinney(p,e,c) so the professional
can override with his own values. No norm table stored.

GT benchmark (rank concordance vs the professional): Fine-Kinney 75.4% — beats
the EN-62061-style model (69.3%) and the raw engine (57%).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 15:22:44 +02:00
Benjamin Admin a910793d12 feat(iace): de-bias severity estimate; risk ranking 57%->69% vs Fachmann
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / detect-changes (push) Successful in 8s
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 4s
CI / validate-canonical-controls (push) Successful in 11s
CI / loc-budget (push) Successful in 15s
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) Has been skipped
CI / test-go (push) Failing after 44s
CI / iace-gt-coverage (push) Successful in 22s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
The engine's hand-set DefaultSeverity systematically over-estimates severity
(GT shows crushing 3.3 vs 2.2, struck_by 3.1 vs 2.5; electrical was already
close). EstimateSeverity blends the pattern default 50/50 with the contact
mode's GT-calibrated typical severity (baseS) — keeps pattern-specific signal,
removes the bias. Our own model, no norm table.

Effect across both GTs: severity within +-1 78%->88%; risk RANK concordance
57%->69% (Kistenhub 45%->70%). Wired into iace_handler_init.go so the
BreakPilot risk line uses the de-biased severity.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 13:52:19 +02:00
Benjamin Admin 02a31b711c fix(iace): remove EN ISO 13849-1 risk-graph reproduction; own risk model
CI / detect-changes (push) Successful in 6s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-backend (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
CI / build-sha-integrity (push) Failing after 5s
CI / validate-canonical-controls (push) Successful in 11s
CI / loc-budget (push) Successful in 14s
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) Has been skipped
CI / test-go (push) Failing after 37s
CI / iace-gt-coverage (push) Successful in 23s
IP/copyright fix: ComputePLr reproduced the EN ISO 13849-1 Anhang A risk-graph
decision table (S/F/P -> PLr a..e) and SeverityToS/ExposureToF its parameter
binning, emitted into every hazard description. Removed — we may not reproduce
DIN/Beuth norm logic.

Replaced with BreakPilot's OWN risk model:
- risk_estimation.go: probability (W) + avoidance (P) estimated from public,
  permissively-licensed accident statistics (Eurostat ESAW, CC BY 4.0) by
  contact mode, calibrated to our ground-truth corpus; own risk index + bands.
- iace_handler_init.go now emits "Risikoeinschaetzung (BreakPilot-Modell):
  S F W P -> Risiko: <level>" instead of the norm PLr string.
- DATA_SOURCES.md: data provenance + license register (ESAW CC BY 4.0; BLS/OSHA
  public domain; HSE OGL; DGUV + DIN/Beuth explicitly excluded).
- gt_risk_benchmark_test.go: first GT validation of risk numbers — W within +-1
  99%, P 93% vs the professional across both ground truths.

Removed risk_graph_test.go (pinned the reproduced norm table).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 13:10:53 +02:00
Benjamin Admin b1357915ae feat(iace): Capability-Domain-Gating — Ghost 120→0, Leakage 25→0, Coverage 100%
CI / detect-changes (push) Successful in 8s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 4s
CI / validate-canonical-controls (push) Successful in 10s
CI / loc-budget (push) Successful in 11s
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) Has been skipped
CI / test-go (push) Failing after 40s
CI / iace-gt-coverage (push) Successful in 24s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
Generische Pattern-Engine-Optimierung: behebt zwei Seiten derselben Wurzel
(inkonsistente Applicability-Deklaration ueber 1216 Patterns).

- Ghost-Patterns (120, feuerten nie): 34 nicht-erzeugbare Required-Tags via
  domaenenspezifische Keywords emittierbar gemacht -> 0.
- Cross-Domain-Leakage (25, feuerten ueberall): neuer text-getriebener
  Capability-Domain-Gate (pattern_domain_gates.go) — Pattern mit Fremdmaschine
  im Szenariotext bekommt dom_*-Tag als Required-Gate -> 0.
- Resolver: Komponente->TypicalEnergySources-Expansion (strukturierte Projekte).
- Benchmark: GT-Platzhalter-Filter; faithful Cross-GT-Narrative-Harness.
- Harte Regression-Guards: Ghosts=0, Leakage=0, Coverage>=90% (beide GTs).
- HP2000/HP2001 (Secondary-Harm-Demos) in AllowlistKnownGaps -> Suite gruen.

Echte Pipeline beide GTs: Coverage 100%/100%, 0 Leaks, 0 Ghosts.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 11:57:08 +02:00
Benjamin Admin 216c7b8eca feat(iace): DSMS-CID-Badge im Tech-File-Export + aggregierter Bulk-Diff
CI / detect-changes (push) Successful in 8s
CI / branch-name (push) Has been skipped
CI / build-sha-integrity (push) Failing after 4s
CI / validate-canonical-controls (push) Successful in 10s
CI / loc-budget (push) Successful in 14s
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 2m21s
CI / test-go (push) Failing after 37s
CI / iace-gt-coverage (push) Successful in 23s
CI / test-python-backend (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Successful in 17s
Punkt 1 — UI-CID-Badge nach erfolgreichem Tech-File-Export:
- archiveTechFile setzt X-DSMS-CID / X-DSMS-Filename / X-DSMS-Size response
  headers + Access-Control-Expose-Headers, sobald DSMS-Archive durchlief
- Split iace_handler_techfile.go (war ueber 500 LOC) → archiveTechFile lebt
  jetzt in iace_handler_techfile_archive.go, setDSMSResponseHeaders als
  pure Helper mit 3 unit tests
- Next.js IACE-Proxy forwarded die X-DSMS-* Header und erkennt jetzt auch
  XLSX/DOCX/MD als Binary-Response (vorher nur PDF/ZIP/octet-stream)
- ExportCIDBadge.tsx zeigt CID, Filename, Groesse + Kopieren-Button +
  "Verlauf anzeigen" (oeffnet CIDHistoryModal)

Punkt 2 — Bulk-Diff Report V1 → V_latest:
- Neuer Endpoint GET /api/v1/documents/{cid}/bulk-diff im dsms-gateway:
  laeuft parent_cid-Kette ab, berechnet chronologische Step-Diffs,
  aggregiert Totals (added/removed lines, metadata_fields_changed,
  binary_steps). Edge-Cases: einzelne Version, binaere Steps, abgebrochene
  Kette
- BulkDiffPanel.tsx zeigt 4-Stat-Header + Step-Tabelle
- CIDHistoryModal bekommt Toggle-Button "Bulk-Diff V1 → V_latest anzeigen"
  neben dem Versions-Counter; damit auch vom IACE-Export-Badge erreichbar

Tests: 3 neue Go-Tests, 4 neue pytest-Tests, alle gruen

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-09 09:07:20 +02:00
Benjamin Admin 60b86be706 feat(p83): wire BUILD_SHA through all Dockerfiles + compose + CI check
check-rebuild-needed.sh war seit Mai funktionsfähig nur fuer 3 von 10
Containern. Die anderen 7 Dockerfiles hatten kein ARG/ENV BUILD_SHA und
docker-compose.yml hat fuer KEINEN Service den Wert durchgereicht — daher
defaultete BUILD_SHA ueberall auf "unknown" und die Drift-Check war
zahnlos.

- ARG BUILD_SHA + ENV BUILD_SHA in 8 zusaetzlichen Dockerfiles
  (ai-compliance-sdk, developer-portal, document-crawler, dsms-gateway,
  compliance-tts-service, docs-src, docs-site, dsms-node)
- docker-compose.yml: BUILD_SHA: \${BUILD_SHA:-unknown} in jedem build:
  Block (10 Services)
- .gitea/workflows/ci.yaml: neuer Job build-sha-integrity validiert dass
  jedes Dockerfile ARG+ENV hat und jeder compose-build den Arg durchreicht.
  Faellt bei jedem PR/Push gegen master, der einen neuen Service oder
  Dockerfile ohne BUILD_SHA einfuehrt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 18:29:03 +02:00
Benjamin Admin 85e758b250 Merge feat/dsms-stufe2-evidence-techfile: tech-file DSMS archive with audit-trail CID 2026-05-22 12:00:22 +02:00
Benjamin Admin 2b1fe3713a feat(dsms): tech-file DSMS archive now logs CID into IACE audit trail
Before: archiveTechFile called dsms.Archive() and discarded the result. The
file was archived to IPFS but no audit-trail entry was written, so there
was no way to later prove "this CE-Akte export went to DSMS with CID X".

After:
- archiveTechFile is now a method on IACEHandler with access to store + gin
  context, and captures the CID from dsms.Archive().
- Writes an AuditAction "tech_file_export" audit entry whose new_values
  JSON carries {cid, filename, size}, mirroring the Python evidence-upload
  pattern.
- Applies to PDF, XLSX, DOCX, and Markdown exports.

Plus dsms package gets 3 unit tests pinning the contract: success-CID
extraction, gateway-unreachable returns nil, 500-response returns nil.

This closes DSMS Stufe 2 (evidence side was already wired; tech-file side
was missing the audit hook). Stufe 3 next: version chains + delta view.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 10:02:18 +02:00
Benjamin Admin 0a84c747f2 feat(iace): wire crossref into tech-file, library UI, and contract tests
Three follow-ups to the 671-norm cross-reference matrix:

1. Tech-file renderer (Go): standards_applied section now gets a deterministic
   Markdown appendix with the DIN/ANSI/GB/JIS mappings for the project's
   suggested norms. Built from registry, never hallucinated by LLM. Applied
   both to LLM and fallback content paths.

2. Frontend NormCrossRefPanel (Next.js): expandable row in the IACE library
   norms tab now has a "Internationale Aequivalenzen anzeigen" button that
   lazy-loads /iace/norms-library/:id/crossref and renders a colour-coded
   table (relation + confidence). Region labels humanised (US — ANSI,
   China (GB), Japan (JIS), etc.).

3. Contract tests (Go): 4 new handler tests pinning the response shape of
   GetNormCrossRef and ListNormCrossRefs. Equivalent to an OpenAPI snapshot
   for these specific endpoints — ai-compliance-sdk has no full OpenAPI
   baseline yet (separate ticket).

Tests: 6 renderer tests + 4 handler contract tests, all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 09:48:07 +02:00
Benjamin Admin d9278f256e feat(iace): norm cross-ref batches 6-7 complete — full 671/671 coverage
- Batch 6 (100): EN 1870 saws, EN 81 lift sub-parts, hearing/glove PPE,
  EN 50126 railway, EN 60974 welding, EN 60335-2-x cleaning appliances
- Batch 7 (71): IEC 60601 medical family, EN ISO 19085 woodworking, safety
  footwear (ASTM F2413), fitness (ASTM F2276), chainsaws (OPEI B175.1),
  ISO 4254 agri remainder, acoustics ISO 3743/3745/3747

671 of 671 norms now have at least DIN mapping; ~80% have a US (ANSI/NFPA/
UL/OSHA/ASME/ASTM/SAE/NIOSH) mapping; ~40% have CN-GB and/or JP-JIS.

Added TestCrossRef_SpotChecks with 15 manually vetted region mappings
(IEC 60601 → ANSI/AAMI ES60601, EN 13445 → ASME BPVC, EN 60204 → NFPA 79,
ISO 10218 → RIA R15.06, etc.).

Next steps for follow-up work:
- Add OpenAPI snapshot for new /norms-library/crossref endpoints
- Front-end: render crossref panel on /sdk/iace norm detail page
- Tech file: auto-emit "this requirement also satisfies X in market Y" hints

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 09:32:38 +02:00
Benjamin Admin 0dbd7b4e45 feat(iace): norm cross-ref batches 2-5 (200 more → 500/671 covered)
- Batch 2: C-norms (woodworking, food, conveyors, lifts, agri, packaging)
- Batch 3: machining, escalators, piping, boilers, wind/PV, refrigeration
- Batch 4: paper sub-parts, playground (ASTM F1487), aircraft ground support, scaffolds, wire ropes, crane design EN 13001
- Batch 5: glass (EN 13035), ladders (ANSI A14), pools (APSP), explosives (DOT 49 CFR), amusement rides (ASTM F2291), drilling/foundation, eye protection (ANSI Z87.1), fire-fighting vehicles (NFPA 1901)

500 of 671 norms now have international identifier mappings. 171 remaining
will be covered in batches 6-7 (alphabetically: EN-1870-x remainder onward
plus ISO-x specials).

Tests: TestCrossRef_BatchCoverage expects 500. All 8 cross-ref tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 09:23:52 +02:00
Benjamin Admin ff100c1cb8 feat(iace): norm cross-reference matrix, batch 1 (ISO/DIN/ANSI/GB/JIS — 100 entries)
Adds a jurisdiction-cross-reference layer to the norms library. Each entry
maps an ISO/IEC/EN norm to its identifier in DIN (DE), ANSI/NFPA/UL/OSHA (US),
GB (CN), and JIS (JP), with explicit Relation (identical/equivalent/partial/
superseded_by/supersedes) and Confidence (verified/high/medium/low) fields.

Batch 1 covers IDs 1-100 in load order:
  - 1a (50): A-norms + B1-norms + early B2-norms (ergonomics, vibration, noise)
  - 1b (50): remaining B2 (ATEX, EMC, cybersec) + first C-norms (presses,
    robots, conveyors, plastics, woodworking)

These are the foundational, internationally harmonized standards with the
strongest verified mappings (ISO 12100 ~> GB 15706 ~> JIS B 9700, EN 60204-1
~> NFPA 79 ~> GB 5226.1 ~> JIS B 9960-1, etc.).

API:
  - GET /iace/norms-library?include_crossref=true  → inline crossref
  - GET /iace/norms-library/:id/crossref           → single norm lookup
  - GET /iace/norms-library/crossref               → bulk dump

Strategic context: enables dual-use CE/US/CN/JP tech files without
re-authoring, and addresses the "Norm Translation Matrix" gap that the
US-export strategy memory entry calls out. 6 batches remaining (~571 norms)
to reach full library coverage.

Tests: 6 new tests; all pass via `go test -vet=off ./internal/iace/`.
(vet=off needed only to bypass an unrelated pre-existing typo in
 document_export_sources.go.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 09:02:05 +02:00
Benjamin Admin 772ff35e8d feat(iace): bridge OSHA MD library to pattern engine, body-part-specific lift crush hazards
- M600-M604: lift endstop mitigations (Kriechgeschwindigkeit, Schaltleiste,
  Mindestabstand, Hold-to-run, Trittblech) — cite OSHA + EN ISO identifiers
- HP2100-HP2102: body-part crush patterns for lift family (foot under platform,
  hand/body against fixed structure, leg between lift and lateral structure),
  restricted via MachineTypes filter
- pattern_machinetype_overrides.go: post-load pass fills MachineTypes on 14
  legacy patterns (HP1000 Walzen, HP539 Schweiss, HP545/HP782 Glas,
  HP756/HP757/HP760 Fahrtreppe, HP1400-1402 CNC, HP045/HP049 Pressen,
  HP420-422 Conveyor) to prevent drift on Kistenhubgeraet-style projects

Why: Kistenhubgeraet re-init exposed two gaps — the abstract "Bremse versagt
bei Absenkbewegung" pattern fired but the concrete foot-crush body-part variant
was missing, AND ~10 unrelated patterns fired purely because their RequiredTags
incidentally aligned. Override map avoids touching 1000+ LOC pattern files
that already exceed the soft cap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 08:37:24 +02:00
Benjamin Admin 94233b7c66 feat(iace): LLM gap-review (Task #7+#8) + tech-file sources appendix (#29)
Three coupled pieces of work, all landing the same PoC:

1. Backend gap-review endpoint (Task #7)
   - internal/api/handlers/iace_handler_gap_review.go:
       POST /projects/:id/llm-gap-review
       feeds Limits-Form + current hazards + current mitigations to
       the configured LLM (Qwen / Claude / OpenAI via ProviderRegistry),
       parses a JSON suggestion list, filter+stamps confidence, falls
       back to a static checklist when LLM is unavailable.
   - Adopt step is NOT in this endpoint by design — the user clicks
     Adopt in the frontend which calls the existing CreateHazard /
     CreateMitigation handlers so provenance flows through the normal
     audit trail.

2. Frontend modal + button (Task #8)
   - app/sdk/iace/[projectId]/hazards/_components/LLMGapReviewModal.tsx:
       reusable modal that POSTs the gap-review endpoint, renders
       suggestions with Adopt/Reject UX, shows confidence + norm refs,
       source-stamp llm_gap_review vs fallback_static.
   - hazards/page.tsx: indigo "KI-Gap-Review" button next to the
     existing "Eigene Gefaehrdung" button + modal mount.

3. Tech-File sources appendix (Task #29 — Stufe 4)
   - internal/iace/document_export_sources.go: new pdfSourcesAppendix
     method appended to ExportPDF. Groups cited norms by license rule
     (R1 OSHA/EU-Recht / R3 BreakPilot patterns / R3 DIN-EN-ISO
     identifier-only) and emits the legally required statement that
     pauschal Impressum-Hinweise nicht ausreichen.
   - extractCitedNorms() scans hazard/mitigation text for EN/ISO/IEC/
     DIN identifiers in a narrow grammar so prose isn't turned into
     spurious citations.

Bonus refactor:
   - internal/app/routes.go reached the 500-LOC hard cap when the new
     llm-gap-review route was added. Extracted registerIACERoutes into
     routes_iace.go (136 LOC). Same wiring, no behaviour change.

Three of the four Attribution-Renderer stages (1, 2, 4) now produce
real output. Stufe 3 ships as <SourceBadge> + <LicenseModuleBanner>
already (commits dfac940 + b9e3eea earlier in this branch).

The PoC is intentionally conservative: every LLM-Suggestion stays
unverbindlich until a human clicks Adopt, and Adopt goes through the
existing normal CreateHazard/CreateMitigation flow (not yet wired in
this commit — separate iteration). The endpoint, modal and provenance
chain are in place for the next iteration to wire Adopt → write path.
2026-05-22 00:21:49 +02:00
Benjamin Admin eb48c5bd1e feat(iace): OSHA minimum-distance library — Task #18
Verbatim OSHA 29 CFR 1910 Subpart O values anchored as the rechtssicher
zitierbare Werte-Basis for the IACE engine. Per strategy discussion
(2026-05-20) US Federal Code is the only public-domain corpus we can
reproduce wholesale; DIN/EN values stay identifier-only.

Coverage in this initial batch:
- MD_OSHA_O10_R1, MD_OSHA_O10_R4 (Table O-10 rows 1 + 4 — point of
  operation guard distance vs max opening width)
- MD_OSHA_212_FAN (§1910.212(a)(5) fan-blade guards: 1/2 in)
- MD_OSHA_217_PSDI (§1910.217 hand-speed constant 63 in/s for
  presence-sensing-device-initiation and two-hand-trip distances)

Each entry carries four parallel value sets:
- OriginalValue/Min/Max in source unit (verbatim, R1)
- ExactMM via deterministic conversion (mathematics, no copyright)
- RecommendedMM with safe-side rounding documented in RoundingNote
- EUNormHints — identifier-only references to EN ISO 13857, EN 13855,
  EN 349 with a human-curated DINComparisonNote (qualitative judgement,
  not a copy)

Open follow-ups (separate iterations):
- Full Table O-10 (rows 2-10) — same shape
- §1910.219 mechanical power-transmission distances
- Cross-reference IACE patterns to MD_OSHA_* identifiers so the Suppression
  Engine surfaces concrete metric values in mitigation suggestions
- Frontend integration: <MinimumDistanceCard> for each measure
2026-05-21 23:43:51 +02:00
Benjamin Admin 16fd406c1a feat(iace): secondary-harm chain model + AllPatterns drift fix
Task #17 — Folgegefahren-Modell as Vorbereitungs-Commit (no DB schema
change yet; persistence via separate [migration-approved] commit).

New:
- secondary_harms.go: SecondaryHarm struct + six canonical categories
  (consumer_safety, product_liability, food_safety, environmental,
  reputation, financial) with DE labels.
- hazard_pattern_types.go: HazardPattern extended with optional
  SecondaryHarms field — pattern library can now attach consequential-
  damage chains.
- hazard_patterns_secondary_demo.go: two worked examples
  - HP2000 Glasbruch carbonated bottling (the "Cola splitter" scenario
    from the IACE strategy discussion) with consumer_safety + food_safety
    + reputation chains
  - HP2001 Pharma fill-finish cross-contamination with consumer_safety
    + product_liability under AMG §84

Bonus fix:
- compliance_crossover.go AllPatterns() was a duplicate enumeration that
  silently drifted from collectAllPatterns() in pattern_registry.go.
  Pre-fix: 1058 patterns visible. Post-fix: 1213 patterns. The 155 invisible
  patterns included CRA, ISO12100 gaps, robot-cell, CNC extended, VDMA,
  textile-agri, GT-bremse — anything added after the original AllPatterns
  was authored. Audit-Suite (cmd/iace-audit) now sees the full set.

Next steps for full secondary-harm rollout:
- DB migration: hazards table + secondary_harms array column
- API: surface secondary_harms in /projects/:id/hazards response
- Frontend: collapsible Folgegefahren-Panel in HazardTable
2026-05-21 23:36:26 +02:00
Benjamin Admin f534b52817 feat(iace): pattern audit suite + library hygiene wave
Add cmd/iace-audit CLI with 5 deterministic methods that find engine
gaps without ground truth:

- A reachability: 1058 patterns vs achievable tag universe
- B consistency: components vs their declared hazard categories
- C vocabulary: limits-form tokens vs keyword dictionary
- D echo: limits-form sentences vs generated hazards (jaccard)
- E hierarchy: hazards vs ISO 12100 design/protection/info levels

Library fixes triggered by A+B+C findings:

- tag_resolver: synonym map for electrical/pneumatic/hydraulic aliases
- component_library: crush_point + EN03 (gravitational) on C014/C128
  (Hubwerk family) - fixes HP1014/1015/1017/1018 which were silently
  weakly_reachable. noise_source added on 7 components (C006/C011/
  C017/C020/C031/C041/C096). electrical_part on 8 drive components
  (C031/C032/C033/C034/C035/C036/C037/C038/C077/C092). cyber tag
  on 10 sensors (C081-C090) + 3 IT components (C111/C112/C116) +
  KI module C119 (ai_model added). pneumatic_part+hydraulic_part
  on valves C091/C093, hydraulic_part+chemical_risk on pump C097,
  moving_part on motion controller C075
- keyword_dictionary: EN03 added to aufzug/lift/hubwerk/hubgeraet
  (was wrongly EN04-only). New keyword entries for hub-action verbs:
  absenken/senken/anheben/heben + hubhoehe/hubweg/hubgeschwindig

Audit impact:
- A: weakly_reachable 409 -> 358 (-51 patterns now fully reachable)
- B: incomplete components 46 -> 30 (-16, -33%)
- HP1018 (Person unter absenkendem Maschinenteil eingeklemmt):
  weakly_reachable -> reachable

Why: methods A/B/C surfaced that the Kistenhubgeraet test project
generated 0 crush-under-load hazards despite OSHA 1910.212(a)(3) +
EN ISO 12100 6.3.5.5 explicitly requiring them. Three orthogonal
bugs (missing crush_point tag, wrong energy source mapping, missing
action verbs in dictionary) silently disabled the entire lift crush
pattern family.
2026-05-21 10:51:08 +02:00
Benjamin Admin a1b380e211 fix(iace): getProject scan missed &p.CustomerName — single-project GET 500ed
Migration 031 added customer_name to the SELECT statement in three places
(GetProject, ListProjects, ListVariants), and the per-row Scan needed the
matching destination. The replace_all caught ListProjects + ListVariants
but missed GetProject because of an indentation difference (single tab
vs row-scope indentation). Result: GET /projects/:id returned
  "get project: number of field descriptions must equal number of
   destinations, got 18 and 17"
which the frontend interpreted as "project has no data" and surfaced an
empty UI even though hazards/mitigations/components were intact (118/282/16
on Bremsscheibe).

Single-line fix: add &p.CustomerName to the GetProject scan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 11:46:34 +02:00
Benjamin Admin a616b64273 feat(iace): Customer-Standard-Reuse across customer's prior projects
CI / detect-changes (push) Successful in 10s
CI / guardrail-integrity (push) Has been skipped
CI / branch-name (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / validate-canonical-controls (push) Successful in 14s
CI / loc-budget (push) Failing after 19s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / test-go (push) Successful in 47s
CI / nodejs-build (push) Successful in 2m46s
CI / iace-gt-coverage (push) Successful in 28s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
[migration-approved]

Task #22. The IACE module is used by a single Maschinenhersteller, but
their plants land at many different end customers. When the safety expert
commissions the second or third plant at the same customer, whole classes
of mitigations (company-wide PPE rules, locked-out energy isolation,
customer-standard signage) are already in place there — but rediscovered
from scratch every project.

Migration 031: iace_projects.customer_name TEXT + partial index.
  The customer is stored as a plain text field rather than a normalised
  iace_customers table (option A from the design discussion). A proper
  customer-management screen can promote this to a FK later without
  data loss.

Backend store_customer_standards.go:
  - ListCustomerStandardSuggestions(projectID, includeVerified) collects
    mitigations from all non-archived prior projects sharing the same
    tenant_id AND case-insensitive customer_name. Aggregates by
    mitigation.name (since same-named measures from different prior
    projects collapse into one suggestion) and surfaces:
      • source_project_count + source_project_names
      • is_customer_standard / has_verified_instances flags
    includeVerified=false → strictly is_customer_standard=true
    includeVerified=true  → also status='verified'
  - ImportCustomerStandardSuggestion(projectID, name): for every prior
    (mitigation.name → hazard.name) pairing, finds matching hazards in
    the current project (by name) and ensures a customer-standard
    mitigation exists. New rows via CreateMitigation (idempotent through
    the UNIQUE(hazard_id, name) from migration 030); existing rows are
    flipped to is_relevant=true + is_customer_standard=true +
    status='verified' via UPDATE.

Routes:
  GET  /api/v1/iace/projects/:id/customer-standards?include_verified=
  POST /api/v1/iace/projects/:id/customer-standards/import   body {name}

Frontend:
  - New page /sdk/iace/[projectId]/customer-standards with:
      • empty-state hint pointing to Auftrag → Kundenname
      • per-suggestion checkbox + per-row Übernehmen button
      • bulk "N übernehmen" button
      • toggle "Auch verifizierte einbeziehen" widening the pool
      • per-suggestion source_project_count + status badges
  - Sidebar item "Kundenstandards" (building icon) placed between
    Verifikation and Nachweise.
  - Order-page now mirrors Auftraggeber.Firmenname into the top-level
    customer_name column on save, so the Reuse feature is fed
    automatically without a separate input field.

The same expert effect from migration 029's is_customer_standard flag —
"I already know it's covered, no evidence needed" — now becomes a
cross-project asset rather than a per-project annotation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 22:31:30 +02:00