feat(onboarding): make the advisor visibly responsive — headline leads with the moving number + auto-recompute

Testing surfaced that toggling certifications appeared to "do nothing": the headline led with the TOTAL
requirement count (constant per target, e.g. 17 for CRA), and the page only recomputed on an explicit
button click. Both fixed:
  - engine.py headline now leads with the number that actually moves: "11 von 17 Anforderungen offen ·
    6 wahrscheinlich (Zertifikate) · 5 zu klären" (was "17 Anforderungen erkannt · …"). Keeps the
    "automatisch erkannt (Intake)" substring.
  - frontend auto-recomputes on certifications / target / scanner-signal change (no button needed).

Now ISO27001 alone -> "13 von 17 offen · 4 wahrscheinlich"; + ISO9001+TISAX+IEC62443 -> "11 von 17 offen ·
6 wahrscheinlich". (Domain truth stays visible: CRA's product-cyber gaps barely move with management-system
certs.) 28 onboarding+transition tests pass, check-loc 0.
This commit is contained in:
Benjamin Admin
2026-06-28 19:31:15 +02:00
parent 26a8518107
commit 867f8c3854
2 changed files with 5 additions and 2 deletions
@@ -96,6 +96,9 @@ export default function OnboardingAdvisorPage() {
} finally { setLoading(false) } } finally { setLoading(false) }
} }
// auto-recompute when certifications / target / scanner signals change (no button click needed)
useEffect(() => { if (certs.length) run() }, [certs, target, findings]) // eslint-disable-line react-hooks/exhaustive-deps
return ( return (
<div className="min-h-screen bg-gray-50 py-8"> <div className="min-h-screen bg-gray-50 py-8">
<div className="max-w-5xl mx-auto px-4"> <div className="max-w-5xl mx-auto px-4">
@@ -143,8 +143,8 @@ def advisor_start(
next_best_questions=next_q, capability_delta=delta, top_measures=measures, next_best_questions=next_q, capability_delta=delta, top_measures=measures,
evidence_requests=evidence, unsupported_domains=unsupported, evidence_requests=evidence, unsupported_domains=unsupported,
completeness_summary=rep.completeness_summary, completeness_summary=rep.completeness_summary,
headline="%d Anforderungen erkannt · %d automatisch erkannt (Intake) · %d wahrscheinlich (Zertifikate) · %d zu klären" headline="%d von %d Anforderungen offen · %d automatisch erkannt (Intake) · %d wahrscheinlich (Zertifikate) · %d zu klären"
% (len(assess.coverage), len(auto_detected), len(probably), len(next_q))) % (len(delta), len(assess.coverage), len(auto_detected), len(probably), len(next_q)))
def apply_answer(known_capabilities: Sequence[str], capability_id: str, answer: str) -> List[str]: def apply_answer(known_capabilities: Sequence[str], capability_id: str, answer: str) -> List[str]: