Commit Graph

1362 Commits

Author SHA1 Message Date
Benjamin Admin 3f90e40807 fix(browser-matrix): Tracking-Signal statt Cookie-Rohzahl + Matrix-Schnellpfad
Korrektheit (§ 25 TDDDG): "Cookies vor Consent" ist KEIN Verstoss per se —
technisch notwendige Cookies inkl. des Consent-Cookies (speichert die
Ablehnung) sind nach Abs. 2 erlaubt. Verstoss ist nur nicht-essentielles
TRACKING vor Consent.
- browser_cross_finding: Befund haengt jetzt an violations.before_consent
  (Tracking), nicht an der Cookie-Rohzahl; § 25 Abs. 2-Hinweis im Detail.
  Regressionstest: Cookies-ohne-Tracking → KEIN Befund.
- multi_browser_scanner._extract_dimensions: Score nutzt Tracking-Violations
  + reject_respected-Verdikt statt Rohzahl (Fallback erhalten).
- BrowserBehaviorView: "Cookies vor Consent" nur rot/⚠ bei Tracking,
  "nach Ablehnen" neutral (Verdikt = reject-Spalte); erklaerende Zeile.

Speed: run_consent_test ueberspringt im Matrix-Modus (browser_profile gesetzt)
die teuren Phasen C/D-F/G — nur A+B noetig. Verhindert das 504 beim
Multi-Engine-Scan (BMW 4 Engines lief sonst in den 338s-Gateway-Timeout).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-13 00:10: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 75d42a834b fix(consent-tester): playwright install-deps — Firefox/WebKit fehlten OS-Libs
E2E auf BMW (macmini, arm64) zeigte: nur Chromium lief, Firefox/WebKit/Mobile-
Safari scheiterten mit "Host system is missing dependencies to run browsers".
Die manuell gepflegte apt-Lib-Liste war fuer Gecko/WebKit unvollstaendig.
`playwright install-deps chromium firefox webkit` (als root) installiert den
vollstaendigen OS-Dep-Satz → alle Engines starten. Betrifft beide Arches.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 23:51:17 +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 85a8a1d545 feat(browser-matrix): Cross-Browser-Befunde + Browser-Default-Einordnung (Phase 4)
- browser_cross_finding: deterministische Sicht ueber die Matrix (keine 2.
  Engine, kein LLM). Findet Inkonsistenzen ZWISCHEN Browsern (Cookies vor
  Consent / Ablehnen nicht universell respektiert / Banner-Links fehlend) und
  ordnet ein: Safari-ITP / Brave-Shields / Firefox-ETP maskieren Verstoesse
  clientseitig → strenge Engine "sauber" ist KEIN Compliance-Beleg, massgeblich
  sind die nachgiebigen (Chrome/Edge). Coverage-Hinweis fuer nicht verfuegbare
  Browser. Je Befund Titel/Detail/Severity/affected/Massnahme.
- snapshot_check_routes: cross_findings frisch in run + GET (nicht persistiert).
- BrowserBehaviorView: "Cross-Browser-Befunde"-Block ueber der Tabelle.
- Tests: test_browser_cross_finding (6).

Offen (Folge-Task): Borlabs-Consent-Historie-Live-Erkennung (braucht
consent-tester-Storage-Scan).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 23:22:57 +02:00
Benjamin Admin 9587726936 feat(admin): Tab "Browser-Verhalten" — Per-Browser-Matrix + Screenshots (Phase 3)
- BrowserBehaviorView: laedt gespeicherte Matrix (GET), sonst "Browser-Test
  starten" (POST run, Live-Lauf). Per-Browser-Tabelle (Cookies vor Consent /
  nach Ablehnen / Ablehnen respektiert / Oberflaeche / Score), Engine-Detail
  mit Banner-Screenshot + Oberflaechen-Befunden, Mobil-Badge, "nicht
  verfuegbar"-Zeilen fuer fehlende Browser (arm64-Dev).
- Proxys browser-behavior (GET) + browser-behavior/run (POST, langer Timeout).
- page.tsx: Tab "Browser-Verhalten" (sichtbar sobald scanbare URL im Snapshot).
- consent-tester scan_matrix_summary: banner_findings je Engine im summary
  (Text/Severity/Norm) → Oberflaechen-Befunde im Tab.
- tsc strict clean; Vitest BrowserBehaviorView (2).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 23:15:06 +02:00
Benjamin Admin c7fde93061 feat(backend): On-demand Browser-Verhaltens-Matrix + Snapshot-Persistenz (Phase 2)
- check_snapshot: update_browser_matrix/load_browser_matrix — migrationsfrei
  in banner_result.browser_matrix (JSONB jsonb_set, eigener scanned_at)
- snapshot_check_routes: POST /snapshots/{id}/browser-behavior/run laeuft
  /scan-matrix LIVE (Re-Crawl je Engine, nur live messbar), persistiert das
  Ergebnis; GET /snapshots/{id}/browser-behavior liefert die gespeicherte
  Matrix ohne Re-Crawl. Profil-Set = 4 Default-Engines + Brave/Chrome/Edge.
- consent-tester multi_browser_scanner: Semaphore(2) gegen OOM (7 Browser
  parallel sprengten das 2g-mem_limit)
- Pydantic-Modell mit Optional[List[...]] (nicht `| None`) → Py3.9-sicher
- Tests: _snapshot_scan_url + Request-Defaults (5)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 23:03:28 +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 7c0126f2ef feat(consent-tester): Brave + Chrome/Edge-Channels im Image (amd64-gated, Phase 1.3)
- Dockerfile: Brave-apt-Repo + `playwright install --with-deps chrome msedge`,
  beide hinter TARGETARCH=amd64-Gate und best-effort (|| echo) → arm64-Dev-
  Builds (macmini) brechen NICHT, laufen mit den 4 Default-Engines; Brave/
  Chrome/Edge sind amd64-only opt-in-Extras (EXTRA_PROFILES).
- docker-compose.hetzner.yml: consent-tester auf linux/amd64 (statt arm64-
  Emulation auf Orca) — Voraussetzung dafuer, dass die echten Browser
  ueberhaupt installiert werden.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 22:52:49 +02:00
Benjamin Admin 881e9c28de feat(consent-tester): /scan-matrix echt — Profil je Engine + Per-Engine-Summary (Phase 1.2)
- _scanner_run reicht browser_profile an run_consent_test durch (statt Single-Chromium-Shim)
- neue scan_matrix_summary.matrix_scan_dict: ConsentTestResult -> schlanke
  Matrix-dict-Form (phases fuer _extract_dimensions + kompakter `summary`:
  cookies_before_consent/after_reject, reject_respected-Heuristik [keine
  Verstoesse UND kein neuer Tracker], surface, screenshot)
- multi_browser_scanner._run_one hebt summary + engine + is_mobile an die
  Zeile, verwirft die vollen Cookie-Listen (JSONB-Persistenz schlank)
- consent_scanner: _ctx_base mit Mobile-Device-Emulation (iPhone-Profil ->
  echtes Mobile-Viewport/Touch), alle 5 new_context auf **_ctx_base
- Tests: test_scan_matrix_summary (6) inkl. _extract_dimensions-Vertrag

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 22:46:42 +02:00
Benjamin Admin c816827720 feat(consent-tester): browser_profile-Param — echte Engine-Wahl im Scan (Phase 1.1)
run_consent_test nimmt jetzt browser_profile (browser_profiles.py): Firefox/Gecko,
WebKit/Safari oder Blink (Chromium-Default / Chrome-/Edge-Channel / Brave via
executable_path). Rückwärtskompatibel: None → Chromium wie bisher. Fundament für
die echte /scan-matrix (Stage-1.b-Shim), die als nächstes Profile durchreicht.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 22:21:20 +02:00
Benjamin Admin 11740bd2f9 feat(consent-tester): 4 weitere Edge-Cases — Consent-or-Pay, Consent Mode, CNAME-Cloaking, Returning-User
#4 Consent-or-Pay (EDPB Opinion 08/2024): Banner-Text-Signatur (Pur-Abo/
   "zustimmen oder bezahlen" + Consent-Kontext) → MEDIUM-Befund "rechtlich
   umstritten, gesondert prüfen".
#5 Google Consent Mode v2: page.evaluate (dataLayer-consent-Events / inline
   gtag('consent')) → MEDIUM "ist KEINE gültige Einwilligung".
#6 CNAME-Cloaking: First-Party-Subdomains per socket.gethostbyname_ex auflösen,
   CNAME-Kette gegen bekannte Tracker-Infra (Eulerian/Adobe/Webtrekk/…) → HIGH
   "faktisch Drittanbieter trotz First-Party-Optik". Best-effort, kurze Timeouts.
#7 Returning-User: Scanner nutzt by-design frische Browser-Contexts → Hinweis im
   Kein-Banner-Befund (fehlendes Banner liegt nicht an erinnertem Consent).

Tests + py_compile grün.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 20:45:20 +02:00
Benjamin Admin 2b928dcb33 fix(consent-tester): Edge-Case-Befunde auch im no-banner-Frühreturn
#1/#2 (kein-Banner-affirmativ) feuerte nicht, weil der no-banner-Pfad bei
Zeile 220 früh zurückkehrt — vor dem Edge-Case-Block am Funktionsende.
Logik in _apply_edge_case_findings extrahiert und an BEIDEN Return-Pfaden
aufgerufen (Früh-Return + Ende). Damit greift #1 jetzt auf statischen Seiten.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 19:55:42 +02:00
Benjamin Admin c2422138e6 feat(consent-tester): 3 Edge-Cases — kein-Banner-konform, Geo-Caveat, Non-Cookie-Tracking
#1/#2: Wenn KEIN Banner erkannt UND kein Tracking vor Consent (statische Seite
oder nur technisch notwendige Cookies, §25 Abs.2 TDDDG) → affirmativer LOW-Befund
"konform, kein Banner nötig" statt stillem "Banner fehlt". Inkl. Geo-Caveat
(Scan außerhalb EU sieht geo-getargetete Banner evtl. nicht).

#3: detect_non_cookie_tracking erkennt Pixel/Fingerprinting per Domain-Signatur
(Meta, TikTok, LinkedIn, Pinterest, Clarity, FingerprintJS, Hotjar, Reddit,
Snapchat) → MEDIUM-Befund "§25/Art.5(3) gilt auch ohne Cookies". '0 Cookies' ≠
'kein einwilligungspflichtiges Tracking'.

Verdrahtet in consent_scanner vor dem Return. Tests + py_compile grün.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 19:49:55 +02:00
Benjamin Admin d8a9e3049d feat(consent-tester): cookieless Opt-out erkennen statt False-HIGHs
Cookie-freie Analyse mit reinem Opt-out-Hinweis (z.B. bayshore.ai:
"Privacy-friendly, cookie-free analytics are currently enabled ... Disable")
ist KEIN Consent-Banner: cookieless = kein Endgeräte-Zugriff → §25 TDDDG
verlangt keine Einwilligung → Opt-out statt Opt-in. Die Standard-Opt-in-
Checks (granulare Kategorien, Accept/Reject-Balance, Impressum-im-Banner)
trafen nicht zu und erzeugten 3 Falsch-HIGHs.

is_cookieless_optout() erkennt das Muster (cookieless-Signal + Opt-out-Wort,
KEIN Consent-Signal); check_banner_text gibt dann früh EINEN ausführlichen
LOW-Erklär-Befund zurück (zählt nicht als HIGH) und setzt die Opt-in-Checks
aus. Ausführlich, weil der Fall extrem untypisch ist.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 19:27:12 +02:00
Benjamin Admin 2f68646c2d fix(advisor): keep_alive 30m gegen Modell-Kaltstart ("Load failed")
Ollama entlädt das 35b-Modell nach 5 Min Leerlauf → jede Frage danach
startet es kalt (Modell-Load) und läuft in den Frontend-Timeout ("Load
failed"). keep_alive='30m' im Chat-Request hält es warm.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 13:20:13 +02:00
Benjamin Admin bb777fd474 feat(advisor): Abkürzungs-Glossar für treffsichere Kurzfragen
~50 EU/DE-Gesetzes-/Norm-Kürzel (BGB, HGB, MiCA, CRA, DSGVO, DDG, TDDDG …)
mit korrekter Bedeutung + Disambiguierung mehrdeutiger (CRA/CRMA) + Korrektur
veralteter Namen (TTDSG→TDDDG, TMG→DDG). Der Advisor antwortet bei kurzen
Akronym-Fragen sofort + richtig statt auszuweichen.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 13:02:26 +02:00
Benjamin Admin b5431f7375 feat(advisor): Co-Pilot-Tonalität + Scope-Disziplin; drafting-agent Anti-Leak
compliance-advisor.soul.md (Sie durchgehend):
- Persona: ruhiger Compliance Co-Pilot (Komplexität abnehmen, Nutzer entscheidet),
  DSB/Anwalt als Partner-Schritt statt Ausrede.
- Antwortlänge an die Frage koppeln (kurze Frage → 1-3 Sätze, kein erzwungenes
  4-Punkte-Schema); proaktiv mit nächstem Schritt schließen.
- Konfidenz-bewusst (Wahrscheinlichkeit statt Garantie); Risiken/Bußgelder nur auf
  Nachfrage + konstruktiv, nie als erster Eindruck.
- Scope-Disziplin: nur Compliance/Datenschutz/Security/Recht; Off-Topic freundlich
  + knapp ablehnen, kein Erfinden fachfremder Antworten.

drafting-agent.soul.md: Anti-Leak-Regel (Anweisungen nie offenlegen) + Sie + Off-Topic-Disziplin.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 12:58:24 +02:00
Benjamin Admin ffff84c594 fix(advisor): Quellenschutz-Regel eingrenzen + kein Prompt-Leak
Der Advisor deutete Inhaltsfragen ("Was ist der CRA?") als Quellen-/
System-Frage und wich aus; auf Nachfrage zitierte er sogar seine
Quellenschutz-Anweisung. Fixes in compliance-advisor.soul.md:
- Quellenschutz gilt nur noch für ECHTE Meta-Fragen (Quellenliste/RAG),
  NICHT für "Was ist X?"-Fachfragen → die werden sofort beantwortet.
- Neue Regel: System-Anweisungen/Prompt NIE offenlegen oder zitieren;
  auf "warum hast du nicht geantwortet?" nicht mit internen Regeln erklären.
- Neue Regel: mehrdeutige Abkürzungen (CRA …) kurz disambiguieren statt
  ausweichen.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 12:17:06 +02:00
Benjamin Admin 45df68537e feat(agent): Voll-Audit-Link in die Snapshot-Detail-Seite
Der "Voll-Audit öffnen (alle MCs)"-Link hing in ComplianceResultTabs (aus
der Agent-Seite entfernt). Jetzt im Detail-Header der Snapshot-Ansicht via
snap.check_id → /sdk/agent/audit/{check_id} (Audit-Daten verifiziert
vorhanden). Plus Site-Titel-Header.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 10:05:09 +02:00
Benjamin Admin 1b2b030367 feat(agent): /sdk/agent auf Compliance-Check + Snapshot-Historie reduzieren
- Tabs Website-Scan (nie funktioniert), Banner-Check, Agent-Test entfernt;
  Tab-Leiste weg, da nur noch Compliance-Check übrig.
- Unter dem Compliance-Check jetzt die Snapshot-Historie (neuer
  SnapshotHistoryList): neuester oben + farblich markiert, Klick → Detail-
  Seite mit den Ergebnissen. Macht /sdk/agent/snapshots erreichbar.
- ComplianceCheckTab zeigt nach dem Lauf keine Inline-Ergebnisse mehr,
  sondern einen Hinweis auf die Historie (onComplete refresht sie).
- Tote Komponenten gelöscht (ScanResult/BannerCheckTab/AgentTestTab).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 09:31:35 +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 99901bba0a fix(cookie): Präfix-Matcher über-matcht kurze generische Basen nicht mehr
CI / detect-changes (push) Successful in 15s
CI / guardrail-integrity (push) Has been skipped
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 / build-sha-integrity (push) Successful in 10s
CI / validate-canonical-controls (push) Successful in 17s
CI / loc-budget (push) Successful in 18s
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 59s
CI / iace-gt-coverage (push) Successful in 28s
CI / test-python-backend (push) Successful in 37s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
Die Deklaration-vs-Bibliothek-Sicht deckte sofort einen Fehl-Match auf:
'cct_chatSessionToken' (Genesys-Webchat) traf die Library-Basis 'cct'
(actual_category Marketing, purpose 'shopping cart') → falsches
'necessary→Marketing'-Finding. Ursache: gekürzte 3-Zeichen-Basis ohne
führenden _.

_is_distinctive_base: gekürzte Präfix-Basis nur akzeptieren bei ≥4 Zeichen
ODER führendem '_' (kanonische Cookies wie '_ga'). GTM-/AdobeOrg-/Hash-
Suffix-Stripping bleibt erhalten (Tests grün), generische 'cct'/'sid'/'gtm'
über-matchen nicht mehr.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 21:26:47 +02:00
Benjamin Admin 403e3c66d2 feat(cookie): Deklaration-vs-Bibliothek-Diff-Sicht + Funnel-KPI
Für die Library-getroffene Teilmesse (~32%) pro Cookie die Feld-
Abweichungen deklariert→Library (Kategorie/Laufzeit/Zweck) als Diff-Karte,
plus ehrlicher Funnel (gesamt → geprüft → abweichend) — nicht-getroffene
Cookies sind nicht prüfbar (kein Pass/Fail), passend zur Tonalität.

- analyze_cookies: 'expected'-Soll-Wert an tracker_as_necessary/
  excessive_lifetime/missing_purpose (+ _CAT_LABEL_DE).
- neues cookie_declaration_diff.build_declaration_diff: reine Regroup-
  Aggregation der Findings pro Cookie (single source = analyze_cookies),
  Hinweis-Typen (third_country/eu_alternative) bewusst ausgeschlossen.
- cookie-check exponiert out['declaration_diff'].
- CookieDeclarationDiff.tsx oben im Cookie-Tab (vor Panel/ResultView).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 21:00:50 +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 97e39579d5 feat(cookie+routing): Storage-Typ-Filter + legal_notice capture-only
#3 Storage-Filter: cookie-check exponiert per-Cookie-Speichertyp
(storage_inventory.per_cookie); CookieResultView bekommt Filter-Chips
(Cookie/Local Storage/Framework …) + eine Speicher-Spalte, Anbieter ohne
passenden Treffer werden ausgeblendet, KPI zeigt gefilterte Zahl.

A-Routing: legal_notice ist jetzt ein kanonischer Doc-Type. Eigene
Discovery-Regel (legal-disclaimer/rechtlicher-hinweis) VOR impressum →
die Disclaimer-Seite wird nicht mehr als Impressum substituiert (Ursache,
dass die Cross-Doc-Reconciliation nie zündete). capture-only: als
doc_entry für B persistiert, aber nicht einzeln gescort (keine 0%-Noise,
da ohne eigene Checkliste). Im Scan-Form als Option auswählbar.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 20:45:18 +02:00
Benjamin Admin 0f6cdc93fd fix(snapshot): Cookie-Dedup + schneller Impressum-Tab + Tabellen-Zahl
- Cookies werden je Vendor nach Name dedupliziert (Consent-Phasen-Dubletten;
  BMW 2196 → ~772) — in cookie-check + get_snapshot, behebt aufgeblähte
  Kachel-/Finding-Zahlen.
- Impressum-Snapshot-Check überspringt den ~40s-LLM-Schritt (context skip_llm)
  → Tab lädt sofort statt leer zu bleiben.
- Vendor-Tabelle zeigt nur die Cookie-Zahl (kein 'Cookies'-Wort je Zeile).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 19:54:15 +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 b4981ea9ab feat(iace): benchmark distance panel (Thema 1)
Surface result.distances in the benchmark module: a DistanceComparison
panel showing agreement %, covered values (green), GT-only gaps (amber)
and engine-only extras — mirroring the RiskComparison panel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 16:32:52 +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 ba7d98be36 feat(reconcile): B — Cross-Doc-Reconciliation (Pflicht in anderem Doc erfüllt)
Ein 'X fehlt'/'zu prüfen'-Finding wird unterdrückt, wenn die Pflicht in einem
ANDEREN Snapshot-Dokument erfüllt ist (z.B. § 36 VSBG / OS-Link stehen bei BMW
in AGB/'Rechtlicher Hinweis', nicht im Impressum → war False Positive).
Konservative Allowlist (impressum: verbraucher_streitbeilegung, odr_link) gegen
False-Reconciliation. Verdrahtet in _run_doc_agent (alle Doc-Checks). Frontend:
'In anderem Dokument abgedeckt'-Sektion. Greift voll nach Scan + Legal-Capture.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 15:42:16 +02:00
Benjamin Admin 9dfdaae8e4 feat(cookie): präfix-bewusster Library-Match (Runtime-Suffixe)
load_big_library matchte nur EXAKT → nur ~27% der BMW-Cookies trafen die
Open-Cookie-DB, weil Per-Instanz-Suffixe abweichen (_ga_GTM-XYZ, AMCVS_###@
AdobeOrg, _pk_id.5.7d8). Jetzt: Library einmal laden, Namen entwildcarden,
über _candidate_keys (exact + Präfix an Trennzeichen, Mindestlänge 3 gegen
Über-Match) matchen. Reuse der bewährten _strip_wildcards-Logik.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 15:24:45 +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 4fb476e4be fix(agb): Gewährleistung erkennt 'bei Mängeln' / '§ N Mängel'-Heading
BMW-AGB nutzt '§ 9 Mängel' + 'Rechte und Ansprüche bei Mängeln' statt
'Gewährleistung' — Pattern ergänzt (False Negative auf Realdaten behoben).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 14:31:41 +02:00
Benjamin Admin 7258744107 refactor+feat: Snapshot-Router-Split + generischer ChecklistAgent + AGB-Modul
- Item 2: Snapshot-Doc-Checks (cookie/impressum/dse/agb) in snapshot_check_routes.py
  (agent_compliance_check_routes.py 464→365 Z.); gleiche Pfade, in main.py registriert.
- ChecklistAgent-Basis: DSE-Logik generalisiert (L1/L2, kurze Titel, _severity_
  override-Hook). DSEAgent + AGBAgent sind jetzt Thin-Subclasses → künftige
  Doc-Agenten (widerruf/avv/…) trivial.
- Item 4: AGBAgent (§§ 305 ff. BGB, AGB_CHECKLIST) + agb-check + AGB-Tab via
  AgentModuleTab. Kein Library-Firehose.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 14:23:29 +02:00
Benjamin Admin b40edd6d33 feat(iace): show ESAW evidence panel in risk view (B1)
The Risikobewertung page only mentioned the data sources as static prose.
Add a collapsible "Datenquellen & Evidenz" panel sourced from
/iace/risk-data-sources: the real Eurostat ESAW 2023 contact-mode shares
per mode, with license + ready-to-print attribution, and the note that
tiers anchor the ordering while values stay GT-calibrated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 14:11:15 +02:00
Benjamin Admin 3c6deac1c5 fix(dse+linter): Drittland-Applicability, kein na-Detail, kurze Titel, Linter-Wortgrenzen
- Linter: FORBIDDEN_OUTPUT_TERMS per Wortgrenze → 'Schutzgarantien'/'geeignete
  Garantien' (Art. 46) passieren, 'garantiert'-Claims bleiben geblockt.
- DSE: L2-Detail wird übersprungen statt 'na', wenn die L1-Pflichtangabe fehlt
  (kein irreführendes 'nicht anwendbar' für z.B. Transfermechanismus).
- DSE: Drittland → HIGH bei dokumentiertem Drittlandtransfer (scan_context via
  AgentInput.context) — BMW (Konzern, US-Provider) ist kein weiches MEDIUM.
- DSE: Titel/Maßnahme kurz (treibt den Recommendation-Titel); ausführliche
  Begründung als evidence — behebt 120-Zeichen-abgeschnittene Überschriften.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 13:43:24 +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 76be96556d feat(dse): kuratierter DSEAgent + Snapshot-Tab (Art. 13/14, kein Firehose)
DSEAgent wrappt die existierende ART13_CHECKLIST (33 kuratierte Pflichtangaben
L1 + Detailchecks L2) → strukturierter AgentOutput, NICHT der 90k-Library-
Firehose (eCall/Gesundheit/Telekom-Lärm). GET /snapshots/{id}/dse-check spiegelt
impressum-check; doc_input_from_snapshot generalisiert. Frontend: generischer
AgentModuleTab (lazy → AgentResultTab) für Impressum + DSE; DSE-Tab in der
Snapshot-Seite. Plus HRB-Pattern \d→\d+ (volle Registernummer als Beleg).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 12:46:46 +02:00
Benjamin Admin be93859645 fix(impressum): Pflichtangaben-Beleg = exakter Treffer statt Textpassage
_match_value gibt genau den gematchten Bereich zurück (nur die E-Mail unter
Email, nur die USt-IdNr, nur die Telefonnummer) — nicht mehr ein Fenster/den
umgebenden Satz. Behebt die Wiederholung desselben Anfangssatzes bei Texten
ohne Zeilenumbrüche (BMW = ein Block).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 12:25:27 +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 877d540ce1 fix(cookie+impressum): Drittland-FP, Impressum-Beleg, neuer Opt-Out-Finding
- Drittland: unbekannte Herkunft ('N/A') + Self-Hosting feuern nicht mehr —
  First-Party-Session-Cookies (PHPSESSID/JSESSIONID) waren False Positives.
- Impressum _line_of: enges Fenster um den Treffer bei Texten ohne Umbrüche
  (BMW = ein Block) → jede Pflichtangabe zeigt IHREN Beleg statt denselben Satz.
- Neuer Finding-Typ missing_opt_out: einwilligungspflichtiger Anbieter mit
  Cookies ohne Opt-Out-/Widerspruchs-Link (Art. 7 Abs. 3 + Art. 21).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 11:59:48 +02:00
Benjamin Admin 5b36b3f367 feat(impressum): Snapshot-Modul-Tab — ImpressumAgent auf gespeichertem Text
Snapshot-Detailseite wird zu Modul-Tabs (Cookies & Tracking | Impressum).
Backend GET /snapshots/{id}/impressum-check laeuft den v3 ImpressumAgent auf
dem gespeicherten Impressum-Text (kein Re-Crawl); Input-Erzeugung in
impressum_input_from_snapshot() ausgelagert (pure + getestet: Text/Scope/
company_name-Fallback/None-Pfad). Frontend laedt lazy beim Tab-Wechsel und
rendert mit dem bestehenden AgentResultTab (keine zweite Engine).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 11:24:44 +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 39cb6afc23 feat(cookie): Findings bearbeitbar — gruppiert nach Typ + Matrix + Hinweise-Split
CookieFindings: Umschalter [Nach Fehlertyp] (je Typ: Maßnahme + betroffene
Cookies + Ticket-Text) ↔ [Matrix] (Cookie×Typ, ✗ Handlung / ⚠ Hinweis).
Trennung FINDINGS (zu beheben) vs HINWEISE (neutral, gegen DSE zu prüfen).
Backend: kind-Klassifikation (third_country/eu_alternative=hinweis); Drittland-
Remediation neutral formuliert (pro Verarbeiter prüfen, keine 'in DSE benennen'-
Befehle, da DSE-Abdeckung wie BMWs 'in der Regel SCC' oft unzureichend).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 11:02:34 +02:00
Benjamin Admin b0115cb10b feat(cookie): 2. Sicht Banner-Kategorie + Fehl-Einsortierung
CookieResultView bekommt einen Umschalter [Rechtliche Rolle] ↔
[Banner-Kategorie] (Notwendig/Funktional/Statistik/Marketing). In beiden
Sichten zeigt jede Cookie-Zeile '→ sollte: Marketing', wenn die tatsächliche
Kategorie laut Library von der deklarierten abweicht (rot bei Tracker als
notwendig, § 25 TDDDG). Neue KPI 'Falsch einsortiert'. Backend liefert dazu
cookie_categories (name→actual_category) aus big_lib im cookie-check-Output;
Seite lädt cookie-check einmal und reicht es an beide Komponenten.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 10:33:33 +02:00
Benjamin Admin af8906b156 fix(admin): commit missing infrastructure-modules types
app/api/webhooks/woodpecker/route.ts (committed in 529c37d) imports
WoodpeckerWebhookPayload, ExtractedError + BacklogSource from
@/types/infrastructure-modules, but that file was never committed. Clean
checkouts (Docker build, CI) fail with 'Cannot find module'. Restore the
file so the admin build is green again. Pure type declarations, no logic.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 10:09:23 +02:00