fix: Pass 0b — Duplicate Guard, Severity-Kalibrierung, Title-Truncation
All checks were successful
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 55s
CI/CD / test-python-backend-compliance (push) Successful in 36s
CI/CD / test-python-document-crawler (push) Successful in 23s
CI/CD / test-python-dsms-gateway (push) Successful in 20s
CI/CD / validate-canonical-controls (push) Successful in 11s
CI/CD / Deploy (push) Successful in 4s

1. Duplicate Guard: merge_hint-Lookup vor INSERT in _write_atomic_control()
   verhindert semantisch identische Controls unter demselben Parent.
2. Severity-Kalibrierung: action_type-basiert statt blind vom Parent.
   define/review/test → max medium, implement/monitor → max high.
3. Title-Truncation: Schnitt am Wortende statt mitten im Wort.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-27 08:38:33 +01:00
parent f39e5a71af
commit fb2cf29b34
2 changed files with 187 additions and 6 deletions

View File

@@ -40,6 +40,8 @@ from compliance.services.decomposition_pass import (
_format_citation,
_compute_extraction_confidence,
_normalize_severity,
_calibrate_severity,
_truncate_title,
_compose_deterministic,
_classify_action,
_classify_object,
@@ -704,7 +706,8 @@ class TestComposeDeterministic:
# Object placeholder should use parent_title
assert "System Security" in ac.test_procedure[0]
def test_severity_inherited(self):
def test_severity_calibrated(self):
# implement caps at high — critical is reserved for parent-level controls
ac = _compose_deterministic(
obligation_text="Kritische Pflicht",
action="implementieren",
@@ -715,7 +718,7 @@ class TestComposeDeterministic:
is_test=False,
is_reporting=False,
)
assert ac.severity == "critical"
assert ac.severity == "high"
def test_category_inherited(self):
ac = _compose_deterministic(
@@ -2431,3 +2434,113 @@ class TestPass0bWithEnrichment:
# Invalid JSON
assert _parse_citation("not json") == {}
# ---------------------------------------------------------------------------
# TRUNCATE TITLE TESTS
# ---------------------------------------------------------------------------
class TestTruncateTitle:
"""Tests for _truncate_title — word-boundary truncation."""
def test_short_title_unchanged(self):
assert _truncate_title("Rate-Limiting umgesetzt") == "Rate-Limiting umgesetzt"
def test_exactly_80_unchanged(self):
title = "A" * 80
assert _truncate_title(title) == title
def test_long_title_cuts_at_word_boundary(self):
title = "Maximale Payload-Groessen fuer API-Anfragen und API-Antworten definiert und technisch durchgesetzt"
result = _truncate_title(title)
assert len(result) <= 80
assert not result.endswith(" ")
# Should not cut mid-word
assert result[-1].isalpha() or result[-1] in ("-", ")")
def test_no_mid_word_cut(self):
# "definieren" would be cut to "defin" with naive [:80]
title = "x" * 75 + " definieren"
result = _truncate_title(title)
assert "defin" not in result or "definieren" in result
def test_custom_max_len(self):
result = _truncate_title("Rate-Limiting fuer alle Endpunkte", max_len=20)
assert len(result) <= 20
# ---------------------------------------------------------------------------
# SEVERITY CALIBRATION TESTS
# ---------------------------------------------------------------------------
class TestCalibrateSeverity:
"""Tests for _calibrate_severity — action-type-based severity."""
def test_implement_keeps_high(self):
assert _calibrate_severity("high", "implement") == "high"
def test_define_caps_to_medium(self):
assert _calibrate_severity("high", "define") == "medium"
def test_review_caps_to_medium(self):
assert _calibrate_severity("high", "review") == "medium"
def test_test_caps_to_medium(self):
assert _calibrate_severity("high", "test") == "medium"
def test_document_caps_to_medium(self):
assert _calibrate_severity("high", "document") == "medium"
def test_monitor_keeps_high(self):
assert _calibrate_severity("high", "monitor") == "high"
def test_low_parent_stays_low(self):
# Even for implement, if parent is low, stays low
assert _calibrate_severity("low", "implement") == "low"
def test_medium_parent_define_stays_medium(self):
assert _calibrate_severity("medium", "define") == "medium"
def test_unknown_action_inherits_parent(self):
assert _calibrate_severity("high", "unknown_action") == "high"
def test_critical_implement_caps_to_high(self):
# implement caps at high — critical is reserved for parent-level controls
assert _calibrate_severity("critical", "implement") == "high"
def test_critical_define_caps_to_medium(self):
assert _calibrate_severity("critical", "define") == "medium"
# ---------------------------------------------------------------------------
# COMPOSE DETERMINISTIC — SEVERITY CALIBRATION INTEGRATION
# ---------------------------------------------------------------------------
class TestComposeDeterministicSeverity:
"""Verify _compose_deterministic uses calibrated severity."""
def test_define_action_gets_medium(self):
atomic = _compose_deterministic(
obligation_text="Payload-Grenzen sind verbindlich festzulegen.",
action="definieren",
object_="Payload-Grenzen",
parent_title="API Ressourcen",
parent_severity="high",
parent_category="security",
is_test=False,
is_reporting=False,
)
assert atomic.severity == "medium"
def test_implement_action_keeps_high(self):
atomic = _compose_deterministic(
obligation_text="Rate-Limiting muss technisch umgesetzt werden.",
action="implementieren",
object_="Rate-Limiting",
parent_title="API Ressourcen",
parent_severity="high",
parent_category="security",
is_test=False,
is_reporting=False,
)
assert atomic.severity == "high"