From e33ab30b831b58ee80a44ab29c743d8af638acc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=7BAI=7Df=20D=2E=20M=C3=BCller?= Date: Wed, 15 Apr 2026 13:54:06 +0200 Subject: [PATCH 1/4] feat: add LLM Runtime Integration modifier (Phase 1+2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces a cross-cutting modifier (L0-L4) that describes how heavily the software integrates LLMs at runtime, separate from the 5 code dimensions. L3+ pushes the effective tier floor: - L3 (Tool Use) → min Tier 3 - L4 (Agentic) → min Tier 4 At L3+, a callout points to specialized frameworks (OWASP LLM Top 10, Palo Alto SHIELD, Aikido VCAL, Google SAIF) since the built-in mitigation catalog covers build-time risks only. Phase 1 (data model + tier logic): - getTierIndex(values, llmRuntimeLevel) with hard floor multiplier - llmRuntimeLevel state in RiskRadar, passed to RadarChart - RadarChart's rAF updater reads level via ref (closure-safe) Phase 2 (minimal UI): - Pill-button row between chart and tier badge - Callout box at L3+ with framework links - DE/EN i18n for labels, level descriptions, callout text Phases 3-5 (presets, extended docs, skills integration) follow. Refs #20 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/RadarChart.jsx | 10 +++- src/components/RiskRadar.jsx | 52 ++++++++++++++++- src/components/RiskRadar.module.css | 88 +++++++++++++++++++++++++++++ src/i18n.js | 44 +++++++++++++++ src/utils.js | 7 ++- 5 files changed, 196 insertions(+), 5 deletions(-) diff --git a/src/components/RadarChart.jsx b/src/components/RadarChart.jsx index c05c78d..f8777f8 100644 --- a/src/components/RadarChart.jsx +++ b/src/components/RadarChart.jsx @@ -118,6 +118,7 @@ export default function RadarChart({ size = 460, determiningKey = null, registerUpdater = null, + llmRuntimeLevel = 0, }) { // Safe fallbacks to prevent math from crashing if data is missing const safeDims = dimensions || []; @@ -130,7 +131,12 @@ export default function RadarChart({ const n = safeDims.length || 1; // prevents division by zero const angStep = 360 / n; - const ti = getTierIndex(safeVals); + // Ref mirrors the prop so the rAF-driven registerUpdater closure + // can read the current level without re-binding the callback. + const llmRuntimeLevelRef = useRef(llmRuntimeLevel); + llmRuntimeLevelRef.current = llmRuntimeLevel; + + const ti = getTierIndex(safeVals, llmRuntimeLevel); const tc = TIER_BG[ti] || "#6b7280"; const [tooltip, setTooltip] = useState(null); @@ -150,7 +156,7 @@ export default function RadarChart({ registerUpdater((newValues) => { if (!mountedRef.current) return; const dims = dimensionsRef.current; - const newTi = getTierIndex(newValues); + const newTi = getTierIndex(newValues, llmRuntimeLevelRef.current); const newTc = TIER_BG[newTi] || "#6b7280"; const newRiskPts = dims.map((d, i) => polarToCartesian(cx, cy, (maxR / levels) * (newValues[d.key] + 1), i * angStep), diff --git a/src/components/RiskRadar.jsx b/src/components/RiskRadar.jsx index 7eea52f..b304e0a 100644 --- a/src/components/RiskRadar.jsx +++ b/src/components/RiskRadar.jsx @@ -20,6 +20,15 @@ export default function RiskRadar() { // React state holds only the final integer values (for tier badge, level labels etc.) const [values, setValues] = useState({ codeType: 0, language: 1, deployment: 0, data: 0, blastRadius: 0 }); + // LLM Runtime Integration Level (0=none, 1=classify, 2=generate, 3=tools, 4=agentic) + // Cross-cutting modifier — at L3+ it pushes the effective tier floor up. + const [llmRuntimeLevel, setLlmRuntimeLevelState] = useState(0); + const llmRuntimeLevelRef = useRef(0); + const setLlmRuntimeLevel = useCallback((level) => { + llmRuntimeLevelRef.current = level; + setLlmRuntimeLevelState(level); + }, []); + // Animated float values live in a ref — updated every rAF frame // Both sliders and RadarChart read from this ref via their own refs const floatValuesRef = useRef({ ...values }); @@ -88,7 +97,7 @@ export default function RiskRadar() { roundedValues[k] = Math.round(values[k]); }); - const ti = getTierIndex(roundedValues); + const ti = getTierIndex(roundedValues, llmRuntimeLevel); const tier = t.tiers[ti]; const tc = TIER_BG[ti]; @@ -162,12 +171,53 @@ export default function RiskRadar() { { chartValuesRef.current = fn; }} /> + {/* LLM Runtime Integration Modifier */} +
+
{t.llmRuntime.label}
+
+ {[0, 1, 2, 3, 4].map((lvl) => { + const isActive = llmRuntimeLevel === lvl; + const levelInfo = t.llmRuntime.levels[lvl]; + return ( + + ); + })} +
+ {llmRuntimeLevel >= 3 && ( +
+ {t.llmRuntime.calloutTitle} {t.llmRuntime.calloutBody} +
+ {t.llmRuntime.frameworks.map((fw) => ( + + {fw.name} + + ))} +
+
+ )} +
+ {/* Tier badge */}
= 4 ? 3 : llmRuntimeLevel >= 3 ? 2 : 0; + return Math.max(base, floor); } export function polarToCartesian(cx, cy, r, angleDeg) { From 9cbfde9cb4a8e7399857eeb3f3cbe929888f301c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=7BAI=7Df=20D=2E=20M=C3=BCller?= Date: Wed, 15 Apr 2026 14:25:48 +0200 Subject: [PATCH 2/4] feat: add agentic presets and preset-level reset (Phase 3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds three new presets demonstrating the LLM Runtime Modifier: - Support Chatbot / Chatbot → L2 (generative output) - RAG Knowledge Assistant / RAG-Wissensassistent → L3 (tool use) - Coding Agent / Coding-Agent → L4 (agentic) Clicking any preset now sets llmRuntimeLevel explicitly (defaulting to 0 for legacy presets via `?? 0`), so switching between classical and LLM-integrated presets resets the modifier correctly. The active state also compares levels to prevent false-positive highlights. Refs #20 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/RiskRadar.jsx | 8 ++++++-- src/i18n.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/components/RiskRadar.jsx b/src/components/RiskRadar.jsx index b304e0a..e8b0512 100644 --- a/src/components/RiskRadar.jsx +++ b/src/components/RiskRadar.jsx @@ -147,11 +147,15 @@ export default function RiskRadar() { {/* Presets */}
{t.presets.map((p) => { - const active = JSON.stringify(roundedValues) === JSON.stringify(p.values); + const presetLevel = p.llmRuntimeLevel ?? 0; + const active = JSON.stringify(roundedValues) === JSON.stringify(p.values) && llmRuntimeLevel === presetLevel; return (