Fix Scope evaluation crash: align property names between engine, types, and components
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Failing after 35s
CI / test-python-backend-compliance (push) Successful in 39s
CI / test-python-document-crawler (push) Successful in 27s
CI / test-python-dsms-gateway (push) Successful in 25s

The engine used short property names (risk, complexity, assurance, composite) while
the ComplianceScores interface defined (risk_score, complexity_score, assurance_need,
composite_score). Components used yet another convention (riskScore, level, hardTriggers).
The main crash was DEPTH_LEVEL_COLORS[decision.level] where decision.level was undefined
(correct property: decision.determinedLevel).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-10 16:47:01 +01:00
parent 579fe1b5e1
commit 24afed69c1
4 changed files with 53 additions and 51 deletions

View File

@@ -446,13 +446,13 @@ export default function ComplianceScopePage() {
{scopeState.decision && (
<>
<div>
<span className="font-semibold">Level:</span> {scopeState.decision.level}
<span className="font-semibold">Level:</span> {scopeState.decision.determinedLevel}
</div>
<div>
<span className="font-semibold">Score:</span> {scopeState.decision.score}
<span className="font-semibold">Score:</span> {scopeState.decision.scores?.composite_score}
</div>
<div>
<span className="font-semibold">Hard Triggers:</span> {scopeState.decision.hardTriggers.length}
<span className="font-semibold">Hard Triggers:</span> {scopeState.decision.triggeredHardTriggers.length}
</div>
</>
)}

View File

@@ -99,18 +99,18 @@ export function ScopeDecisionTab({
return (
<div className="space-y-6">
{/* Level Determination */}
<div className={`${DEPTH_LEVEL_COLORS[decision.level].bg} border-2 ${DEPTH_LEVEL_COLORS[decision.level].border} rounded-xl p-6`}>
<div className={`${DEPTH_LEVEL_COLORS[decision.determinedLevel].bg} border-2 ${DEPTH_LEVEL_COLORS[decision.determinedLevel].border} rounded-xl p-6`}>
<div className="flex items-start gap-6">
<div className={`flex-shrink-0 w-20 h-20 ${DEPTH_LEVEL_COLORS[decision.level].badge} rounded-xl flex items-center justify-center`}>
<span className={`text-3xl font-bold ${DEPTH_LEVEL_COLORS[decision.level].text}`}>
{decision.level}
<div className={`flex-shrink-0 w-20 h-20 ${DEPTH_LEVEL_COLORS[decision.determinedLevel].badge} rounded-xl flex items-center justify-center`}>
<span className={`text-3xl font-bold ${DEPTH_LEVEL_COLORS[decision.determinedLevel].text}`}>
{decision.determinedLevel}
</span>
</div>
<div className="flex-1">
<h2 className={`text-2xl font-bold ${DEPTH_LEVEL_COLORS[decision.level].text} mb-2`}>
{DEPTH_LEVEL_LABELS[decision.level]}
<h2 className={`text-2xl font-bold ${DEPTH_LEVEL_COLORS[decision.determinedLevel].text} mb-2`}>
{DEPTH_LEVEL_LABELS[decision.determinedLevel]}
</h2>
<p className="text-gray-700 mb-3">{DEPTH_LEVEL_DESCRIPTIONS[decision.level]}</p>
<p className="text-gray-700 mb-3">{DEPTH_LEVEL_DESCRIPTIONS[decision.determinedLevel]}</p>
{decision.reasoning && (
<p className="text-sm text-gray-600 italic">{decision.reasoning}</p>
)}
@@ -123,11 +123,11 @@ export function ScopeDecisionTab({
<div className="bg-white rounded-xl border border-gray-200 p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Score-Analyse</h3>
<div className="space-y-4">
{renderScoreBar('Risiko-Score', decision.scores.riskScore)}
{renderScoreBar('Komplexitäts-Score', decision.scores.complexityScore)}
{renderScoreBar('Assurance-Score', decision.scores.assuranceScore)}
{renderScoreBar('Risiko-Score', decision.scores.risk_score)}
{renderScoreBar('Komplexitäts-Score', decision.scores.complexity_score)}
{renderScoreBar('Assurance-Score', decision.scores.assurance_need)}
<div className="pt-4 border-t border-gray-200">
{renderScoreBar('Gesamt-Score', decision.scores.compositeScore)}
{renderScoreBar('Gesamt-Score', decision.scores.composite_score)}
</div>
</div>
</div>
@@ -231,16 +231,14 @@ export function ScopeDecisionTab({
)}
{/* Hard Triggers */}
{decision.hardTriggers && decision.hardTriggers.length > 0 && (
{decision.triggeredHardTriggers && decision.triggeredHardTriggers.length > 0 && (
<div className="bg-white rounded-xl border border-gray-200 p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Hard-Trigger</h3>
<div className="space-y-3">
{decision.hardTriggers.map((trigger, idx) => (
{decision.triggeredHardTriggers.map((trigger, idx) => (
<div
key={idx}
className={`border rounded-lg overflow-hidden ${
trigger.matched ? 'border-red-300 bg-red-50' : 'border-gray-200 bg-gray-50'
}`}
className="border rounded-lg overflow-hidden border-red-300 bg-red-50"
>
<button
type="button"
@@ -248,17 +246,18 @@ export function ScopeDecisionTab({
className="w-full px-4 py-3 flex items-center justify-between hover:bg-opacity-80 transition-colors"
>
<div className="flex items-center gap-3">
{trigger.matched && (
<svg className="w-5 h-5 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
)}
<span className="font-medium text-gray-900">{trigger.label}</span>
<svg className="w-5 h-5 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<span className="font-medium text-gray-900">{trigger.description}</span>
<span className="text-xs px-2 py-0.5 rounded-full bg-red-200 text-red-800 font-medium">
Min. {trigger.minimumLevel}
</span>
</div>
<svg
className={`w-5 h-5 text-gray-500 transition-transform ${
@@ -279,11 +278,14 @@ export function ScopeDecisionTab({
<span className="font-medium">Rechtsgrundlage:</span> {trigger.legalReference}
</p>
)}
{trigger.matchedValue && (
{trigger.category && (
<p className="text-xs text-gray-700">
<span className="font-medium">Erfasster Wert:</span> {trigger.matchedValue}
<span className="font-medium">Kategorie:</span> {trigger.category}
</p>
)}
{trigger.requiresDSFA && (
<p className="text-xs text-orange-700 font-medium mt-1">DSFA erforderlich</p>
)}
</div>
)}
</div>

View File

@@ -65,26 +65,26 @@ export function ScopeOverviewTab({ scopeState, completionStats, onStartProfiling
)
}
const levelColors = DEPTH_LEVEL_COLORS[decision.level]
const levelColors = DEPTH_LEVEL_COLORS[decision.determinedLevel]
return (
<div className={`${levelColors.bg} border-2 ${levelColors.border} rounded-xl p-8 text-center`}>
<div className={`inline-flex items-center justify-center w-24 h-24 ${levelColors.badge} rounded-full mb-4`}>
<span className={`text-4xl font-bold ${levelColors.text}`}>{decision.level}</span>
<span className={`text-4xl font-bold ${levelColors.text}`}>{decision.determinedLevel}</span>
</div>
<h3 className={`text-xl font-semibold ${levelColors.text} mb-2`}>
{DEPTH_LEVEL_LABELS[decision.level]}
{DEPTH_LEVEL_LABELS[decision.determinedLevel]}
</h3>
<p className="text-gray-600">{DEPTH_LEVEL_DESCRIPTIONS[decision.level]}</p>
<p className="text-gray-600">{DEPTH_LEVEL_DESCRIPTIONS[decision.determinedLevel]}</p>
</div>
)
}
const renderActiveHardTriggers = () => {
if (!decision?.hardTriggers || decision.hardTriggers.length === 0) {
if (!decision?.hardTriggers || decision.triggeredHardTriggers.length === 0) {
return null
}
const activeHardTriggers = decision.hardTriggers.filter((ht) => ht.matched)
const activeHardTriggers = decision.triggeredHardTriggers.filter((ht) => ht.matched)
if (activeHardTriggers.length === 0) {
return null

View File

@@ -1051,10 +1051,10 @@ export class ComplianceScopeEngine {
const composite = riskScore * 0.4 + complexityScore * 0.3 + assuranceScore * 0.3
return {
risk: Math.round(riskScore * 10) / 10,
complexity: Math.round(complexityScore * 10) / 10,
assurance: Math.round(assuranceScore * 10) / 10,
composite: Math.round(composite * 10) / 10,
risk_score: Math.round(riskScore * 10) / 10,
complexity_score: Math.round(complexityScore * 10) / 10,
assurance_need: Math.round(assuranceScore * 10) / 10,
composite_score: Math.round(composite * 10) / 10,
}
}
@@ -1276,9 +1276,9 @@ export class ComplianceScopeEngine {
): ComplianceDepthLevel {
// Score-basiertes Level
let levelFromScore: ComplianceDepthLevel
if (scores.composite <= 25) levelFromScore = 'L1'
else if (scores.composite <= 50) levelFromScore = 'L2'
else if (scores.composite <= 75) levelFromScore = 'L3'
if (scores.composite_score <= 25) levelFromScore = 'L1'
else if (scores.composite_score <= 50) levelFromScore = 'L2'
else if (scores.composite_score <= 75) levelFromScore = 'L3'
else levelFromScore = 'L4'
// Höchstes Level aus Triggers
@@ -1650,12 +1650,12 @@ export class ComplianceScopeEngine {
step: 'score_calculation',
description: 'Risikobasierte Score-Berechnung aus Profiling-Antworten',
factors: [
`Risiko-Score: ${scores.risk}/10`,
`Komplexitäts-Score: ${scores.complexity}/10`,
`Assurance-Score: ${scores.assurance}/10`,
`Composite Score: ${scores.composite}/10`,
`Risiko-Score: ${scores.risk_score}/10`,
`Komplexitäts-Score: ${scores.complexity_score}/10`,
`Assurance-Score: ${scores.assurance_need}/10`,
`Composite Score: ${scores.composite_score}/10`,
],
impact: `Score-basiertes Level: ${this.getLevelFromScore(scores.composite)}`,
impact: `Score-basiertes Level: ${this.getLevelFromScore(scores.composite_score)}`,
})
// 2. Hard Trigger Evaluation
@@ -1675,7 +1675,7 @@ export class ComplianceScopeEngine {
step: 'level_determination',
description: 'Finales Compliance-Level durch Maximum aus Score und Triggers',
factors: [
`Score-Level: ${this.getLevelFromScore(scores.composite)}`,
`Score-Level: ${this.getLevelFromScore(scores.composite_score)}`,
`Trigger-Level: ${this.getMaxTriggerLevel(triggers)}`,
],
impact: `Finales Level: ${level}`,