feat(agent): Impressum-Tab auf Haupt-Engine + Profil/§36-Fixes

Ergebnis-Tab rendert jetzt result.results (Haupt-Doc-Check) statt des
abweichenden v3-Agenten — BMW korrekt statt False Positives:
- DocResultView: ein Dokument als Pflichtangaben-Tabelle (Label + gefundener
  Text + 3-Tier-Status), KEINE MC-IDs. ComplianceResultTabs speist Tabs aus
  result.results; ChecklistView-Bausteine exportiert + wiederverwendet.
- profile_extractor: Firmenname/Rechtsform = fruehester Treffer + ausge-
  schriebene Formen (Aktiengesellschaft) -> BMW AG statt "juris GmbH".
- 36 VSBG (MC-010): reines b2c -> POSSIBLY_APPLICABLE (Pruef-Hinweis) statt
  MEDIUM-FAIL; hart nur bei ecommerce. possibly_hint pro MC.
- McCoverage traegt label + found (Snippet); mc_possibly-Aggregat.
- AgentFindingCard/Methodik: interne check_id/mc_id nicht mehr angezeigt.

Tests: test_four_status (16) + Frontend-Vitest gruen; CI-Suite 206, v3/GT
unveraendert. Nur eigene Dateien (geteilter Tree).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-10 23:44:01 +02:00
parent a7dc12f30f
commit 3f23a64d5f
15 changed files with 450 additions and 187 deletions
@@ -178,6 +178,7 @@ def test_editorial_possible_yields_possibly_applicable():
assert red.status == CheckStatus.POSSIBLY_APPLICABLE.value
assert red.severity == Severity.LOW.value
assert out.mc_possibly >= 1
assert "§ 18 MStV" in red.action # MC-spezifischer Hinweis
def test_editorial_absent_is_not_applicable():
@@ -193,3 +194,30 @@ def test_derive_scope_editorial_tiers():
# Medienunternehmen gewinnt — nicht beide Tokens.
s = _derive_scope({"industry": "media", "has_editorial_content": True})
assert "editorial" in s and "editorial_possible" not in s
# ── §36 VSBG Graubereich (BMW-Fall): reines b2c ≠ harter Verstoß ────
def test_vsbg_b2c_is_possibly_applicable():
# Reine B2C-Orientierung (z.B. OEM-Markenseite, Verkauf über Händler) →
# §36 VSBG = Graubereich, KEIN MEDIUM-FAIL (BMW-False-Positive-Fix).
out = asyncio.run(ImpressumAgent().evaluate(AgentInput(
doc_type="impressum", text=TEXT_NO_LEGAL_FORM,
business_scope=["b2c"])))
vsbg = _by_field(out, "verbraucher_streitbeilegung")
assert vsbg is not None
assert vsbg.status == CheckStatus.POSSIBLY_APPLICABLE.value
assert vsbg.severity == Severity.LOW.value
assert "VSBG" in vsbg.action # §36-Hinweis, nicht §18
def test_vsbg_ecommerce_is_hard_fail():
# Echter Online-Shop (ecommerce) → §36 VSBG harte Pflicht (MEDIUM).
out = asyncio.run(ImpressumAgent().evaluate(AgentInput(
doc_type="impressum", text=TEXT_NO_LEGAL_FORM,
business_scope=["ecommerce"])))
vsbg = _by_field(out, "verbraucher_streitbeilegung")
assert vsbg is not None
assert vsbg.status == CheckStatus.FAIL.value
assert vsbg.severity == Severity.MEDIUM.value