From e6e9c0753def74270a727cda455f8fb3633dae7e Mon Sep 17 00:00:00 2001 From: flemming-it Date: Thu, 18 Jun 2026 15:43:15 +0200 Subject: [PATCH] feat(hub): default to live hub on startup + accept partial flow bags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two small changes that together get Recl∆Im out of demo mode on the very first launch after a flow.completed event lands: 1. HubSettings.useHub defaults to true. New installs and post-reset launches probe the hub straight away; if the probe fails (no hub running, wrong port, auth denied), the existing graceful fallback in main.dart drops back to MockRepository. A user can still flip useHub off via the Hub-Reiter. 2. HubRepository._parse now only requires the 'norm' key to consider an event 'Recl∆Im-shaped'. SKM, benefit and frust default to zero if absent; tier_lowest falls back to the cohort source's tier. This makes the partial bag produced by flows/durchstich-from-file.yaml — which can run without econ.skm_score's nested-input gymnastics — enough to surface a real heatmap point. Wired together: launch the app while chain serve runs and at least one flow.completed event is in the audit log → the Heatmap-Tab shows that norm live, with its tier and norm-title parsed from the actual audit-log JSON instead of Fixtures. Signed-off-by: flemming-it --- app/lib/data/hub_endpoint.dart | 2 +- app/lib/data/hub_repository.dart | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/lib/data/hub_endpoint.dart b/app/lib/data/hub_endpoint.dart index b20f433..663d9a4 100644 --- a/app/lib/data/hub_endpoint.dart +++ b/app/lib/data/hub_endpoint.dart @@ -49,7 +49,7 @@ class HubSettings { port: prefs.getInt(_kPort) ?? defaultPort, secure: prefs.getBool(_kSecure) ?? false, authToken: prefs.getString(_kToken), - useHub: prefs.getBool(_kUseHub) ?? false, + useHub: prefs.getBool(_kUseHub) ?? true, ); } diff --git a/app/lib/data/hub_repository.dart b/app/lib/data/hub_repository.dart index 9c6e35b..2f1676e 100644 --- a/app/lib/data/hub_repository.dart +++ b/app/lib/data/hub_repository.dart @@ -94,8 +94,8 @@ class HubRepository implements EvaluationRepository { if (bag == null) return null; final normJson = _asMap(bag['norm']); + if (normJson == null) return null; final skmJson = _asMap(bag['skm']); - if (normJson == null || skmJson == null) return null; final norm = Norm( eli: (normJson['eli'] as String?) ?? 'eli/unknown', @@ -108,8 +108,17 @@ class HubRepository implements EvaluationRepository { paragraphs: _parseParagraphs(normJson['paragraphs']), ); - final skmEur = _asDouble(skmJson['total_eur_per_year']); - final tierLowest = _parseTier(skmJson['tier_lowest']); + final cohortJson = _asMap(bag['cohort']); + final affected = _asInt(cohortJson?['count']); + + // SKM may be missing if the flow only ran the normalize-/ + // cohort-arm (e.g. flows/durchstich-from-file.yaml). Default + // to zero and let the tier fall back to the cohort source's + // tier — that's still the most-honest reading. + final skmEur = _asDouble(skmJson?['total_eur_per_year']); + final tierLowest = skmJson != null + ? _parseTier(skmJson['tier_lowest']) + : _parseTier(cohortJson?['tier']); final benefitJson = _asMap(bag['benefit']); final benefit = _asDouble(benefitJson?['total']); @@ -117,9 +126,6 @@ class HubRepository implements EvaluationRepository { final frustJson = _asMap(bag['frust']); final frust = _asDouble(frustJson?['composite_frust']); - final cohortJson = _asMap(bag['cohort']); - final affected = _asInt(cohortJson?['count']); - return Evaluation( norm: norm, skmEurPerYear: skmEur,