feat(compliance-check): P12 — TDM-Override mit dokumentierter Kunden-Erlaubnis
CI / guardrail-integrity (push) Has been skipped
CI / nodejs-build (push) Successful in 3m5s
CI / test-go (push) Has been skipped
CI / detect-changes (push) Successful in 10s
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 / validate-canonical-controls (push) Successful in 16s
CI / loc-budget (push) Successful in 17s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-backend (push) Successful in 40s
CI / test-python-document-crawler (push) Has been skipped

Backend: ComplianceCheckRequest um tdm_override + tdm_override_reason
erweitert. Worker im _run_compliance_check Pfad: bei
tdm_override=True UND Reason >= 10 Zeichen wird der TDM-Vorbehalt
nur dokumentiert (job.tdm_override.{reason, original_status}) und
NICHT als Abbruch-Grund gewertet. Ohne Reason: Override ignoriert.
Audit-Spur via logger.warning(reason).

Frontend: ComplianceCheckTab um Checkbox + Pflicht-Reason-Feld
("Schriftliche Crawl-Erlaubnis vorhanden") direkt vor dem Submit-
Button. Pflicht: Reason >= 10 Zeichen. Submit sendet die Flags ans
Backend.

Anwendungsfall: Safetykon-Pattern — robots.txt + ai.txt setzen
Vorbehalt, aber Kunde hat schriftlich zugestimmt (Auftrags-Audit).

[guardrail-change] ComplianceCheckTab.tsx (511 LOC) in loc-exceptions
ergaenzt — Split nach _components/TDMOverride + CompliancePolling
ist P11-Tech-Debt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-19 08:56:50 +02:00
parent a220f0d0a7
commit 78b27d4684
3 changed files with 36 additions and 1 deletions
+4
View File
@@ -178,3 +178,7 @@ backend-compliance/compliance/services/cookie_knowledge_db.py
# — selbst-kontainierter Code-Generator, Split wuerde Generator-Logik
# fragmentieren ohne Nutzen.
admin-compliance/lib/sdk/einwilligungen/generator/cookie-banner-embed.ts
# ComplianceCheckTab.tsx (511): zentrale UI fuer Compliance-Check-Form mit
# Polling, Storage, History, Agent-Toggle, TDM-Override. Split nach Concerns
# (_components/CompliancePolling, _components/TDMOverride) ist P11-Tech-Debt.
admin-compliance/app/sdk/agent/_components/ComplianceCheckTab.tsx
@@ -73,6 +73,8 @@ interface HistoryEntry {
export function ComplianceCheckTab() {
const [docs, setDocs] = useState<DocsState>(initState)
const [useAgent, setUseAgent] = useState(false)
const [tdmOverride, setTdmOverride] = useState(false)
const [tdmOverrideReason, setTdmOverrideReason] = useState('')
const [loading, setLoading] = useState(false)
const [progress, setProgress] = useState('')
const [progressPct, setProgressPct] = useState(0)
@@ -197,6 +199,8 @@ export function ComplianceCheckTab() {
body: JSON.stringify({
documents: entries,
use_agent: useAgent,
tdm_override: tdmOverride && tdmOverrideReason.trim().length >= 10,
tdm_override_reason: tdmOverrideReason.trim(),
}),
})
if (!startRes.ok) throw new Error(`Pruefung konnte nicht gestartet werden: ${startRes.status}`)
@@ -319,6 +323,11 @@ export function ComplianceCheckTab() {
</span>
</div>
<div className="bg-amber-50/60 border border-amber-200 rounded-lg p-3 space-y-2">
<label className="flex items-start gap-2 cursor-pointer"><input type="checkbox" checked={tdmOverride} onChange={e => setTdmOverride(e.target.checked)} className="mt-0.5 accent-amber-600" /><span className="text-xs text-amber-900"><strong>Schriftliche Crawl-Erlaubnis vorhanden</strong> uebergeht TDM-Vorbehalte (robots.txt / ai.txt)</span></label>
{tdmOverride && <input type="text" value={tdmOverrideReason} onChange={e => setTdmOverrideReason(e.target.value)} placeholder="z.B. Auftragsbeziehung Safetykon GmbH, Email Hr. X vom 18.05.2026" className="w-full px-3 py-2 text-xs border border-amber-300 rounded bg-white" />}
{tdmOverride && tdmOverrideReason.trim().length < 10 && <p className="text-[10px] text-amber-700">Pflicht: Reason mit min. 10 Zeichen (Audit-Spur).</p>}
</div>
{/* Submit button */}
<button
onClick={handleSubmit}
@@ -46,6 +46,11 @@ class ComplianceCheckRequest(BaseModel):
documents: list[DocumentInput]
use_agent: bool = False
recipient: str = "dsb@breakpilot.local"
# P12: Override fuer TDM-Vorbehalt bei dokumentierter Kunden-Erlaubnis.
# Pflichtfeld tdm_override_reason wenn tdm_override=True
# (z.B. "Auftragsbeziehung Safetykon GmbH, Email Hr. X 18.05.2026").
tdm_override: bool = False
tdm_override_reason: str = ""
class ComplianceCheckStartResponse(BaseModel):
@@ -178,7 +183,13 @@ async def _run_compliance_check(check_id: str, req: ComplianceCheckRequest):
if first_url:
tdm = await check_tdm_reservation(first_url)
_compliance_check_jobs[check_id]["tdm"] = tdm
if not is_crawl_allowed(tdm):
# P12: Bei tdm_override + Reason wird NICHT abgebrochen,
# sondern nur dokumentiert. Override ohne Reason wird ignoriert.
override_active = (
req.tdm_override
and len((req.tdm_override_reason or "").strip()) >= 10
)
if not is_crawl_allowed(tdm) and not override_active:
_compliance_check_jobs[check_id]["status"] = "skipped_tdm"
_compliance_check_jobs[check_id]["error"] = (
f"TDM-Vorbehalt fuer {tdm.get('domain')} erkannt "
@@ -190,6 +201,17 @@ async def _run_compliance_check(check_id: str, req: ComplianceCheckRequest):
logger.info("TDM-skip check_id=%s domain=%s status=%s",
check_id, tdm.get("domain"), tdm.get("status"))
return
if override_active and not is_crawl_allowed(tdm):
_compliance_check_jobs[check_id]["tdm_override"] = {
"reason": req.tdm_override_reason.strip()[:500],
"original_status": tdm.get("status"),
}
logger.warning(
"TDM-Override aktiv: check_id=%s domain=%s "
"status=%s reason=%r",
check_id, tdm.get("domain"), tdm.get("status"),
req.tdm_override_reason.strip()[:80],
)
except Exception as e:
logger.warning("TDM-check failed (proceeding): %s", e)