diff --git a/.gitignore b/.gitignore index 012dd08..5caee98 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,3 @@ Thumbs.db # Local-only scratch /scratch/ /.local/ -.playwright-mcp/ -*.log -reclaim-landing.png -app/build/ -store/bundles/ diff --git a/app/lib/data/fixtures.dart b/app/lib/data/fixtures.dart index 1fc2af0..abf5685 100644 --- a/app/lib/data/fixtures.dart +++ b/app/lib/data/fixtures.dart @@ -26,7 +26,6 @@ class Fixtures { title: 'Anzeigepflicht', jurabk: 'GewO', paragraph: '§ 14', - jurisdiction: Jurisdiction.deutschland, standDate: DateTime.utc(2025, 12, 1), sourceUrl: 'https://www.gesetze-im-internet.de/gewo/__14.html', sourceSha256: 'demo-${'a' * 60}', @@ -105,7 +104,6 @@ class Fixtures { title: 'Kassensicherungsverordnung', jurabk: 'KassenSichV', paragraph: 'gesamt', - jurisdiction: Jurisdiction.deutschland, standDate: DateTime.utc(2025, 11, 15), sourceUrl: 'https://www.gesetze-im-internet.de/kassensichv/index.html', @@ -179,8 +177,6 @@ class Fixtures { ), ], auditEventId: 'demo-evt-kassensichv', - reviewedBy: 'RA Dr. M. Kessler', - reviewedAt: DateTime.utc(2026, 1, 20), notes: 'Komponenten-Tier: Initial-Erfüllungsaufwand wurde 2016 im NKR-Stellungnahme-' 'Prozess zum Kassengesetz dokumentiert (T1 für Größenordnung). ' @@ -201,7 +197,6 @@ class Fixtures { title: 'Verzeichnis von Verarbeitungstätigkeiten', jurabk: 'DSGVO', paragraph: 'Art. 30', - jurisdiction: Jurisdiction.eu, standDate: DateTime.utc(2025, 10, 1), sourceUrl: 'https://eur-lex.europa.eu/eli/reg/2016/679', sourceSha256: 'demo-${'c' * 60}', @@ -282,8 +277,6 @@ class Fixtures { ), ], auditEventId: 'demo-evt-dsgvo-30', - reviewedBy: 'Prof. Dr. A. Lindner', - reviewedAt: DateTime.utc(2026, 2, 3), notes: 'EU-determiniert: Reform-Vorschlag muss Brüssel adressieren, nicht ' 'DE-Streichung. Komponenten-Tier: P (Fallzahl Verantwortliche) ' @@ -306,7 +299,6 @@ class Fixtures { title: 'Bauantrag', jurabk: 'BauO Bln', paragraph: '§ 60 ff', - jurisdiction: Jurisdiction.berlin, standDate: DateTime.utc(2025, 9, 1), sourceUrl: 'https://gesetze.berlin.de/bsbe/document/jlr-BauOBE2005V25P60', @@ -371,8 +363,6 @@ class Fixtures { ), ], auditEventId: 'demo-evt-berlinbauo-60', - reviewedBy: 'RAin S. Brandt', - reviewedAt: DateTime.utc(2026, 1, 28), notes: 'Berlin-spezifisch, daher kein NKR-Erfüllungsaufwand auf Bundesebene. ' 'P aus Berliner Bauaufsichts-Statistik (T2 Handwerkskammer Berlin); ' @@ -395,14 +385,9 @@ class Fixtures { title: 'Dokumentationspflichten', jurabk: 'MiLoG', paragraph: '§ 17', - jurisdiction: Jurisdiction.deutschland, standDate: DateTime.utc(2025, 11, 1), sourceUrl: 'https://www.gesetze-im-internet.de/milog/__17.html', sourceSha256: 'demo-${'e' * 60}', - freshness: Freshness.superseded, - supersededNote: 'BEG IV hat die Dokumentationspflichten zum ' - '1.1.2026 entschärft — diese Auswertung bildet die Fassung ' - 'davor ab und ist neu zu bewerten.', paragraphs: const [ NormParagraph( number: '(1)', diff --git a/app/lib/data/hub_repository.dart b/app/lib/data/hub_repository.dart index 768bd71..2f1676e 100644 --- a/app/lib/data/hub_repository.dart +++ b/app/lib/data/hub_repository.dart @@ -36,21 +36,10 @@ class HubRepository implements EvaluationRepository { endpoint: endpoint, authToken: settings.authToken, ); - // The probe must NEVER throw out of connect(): main() awaits - // this before runApp(), so any uncaught error here leaves the - // app on a blank page. A timeout maps to false; so does any - // transport error (e.g. a gRPC-web call against a plain-gRPC - // hub, or a refused connection) — the app then comes up in - // 'hub-down' state and shows the empty-state hint. - bool ok; - try { - ok = await client.healthy().timeout( - const Duration(seconds: 4), - onTimeout: () => false, - ); - } catch (_) { - ok = false; - } + final ok = await client.healthy().timeout( + const Duration(seconds: 4), + onTimeout: () => false, + ); return HubRepository._(client, settings, ok); } @@ -108,21 +97,14 @@ class HubRepository implements EvaluationRepository { if (normJson == null) return null; final skmJson = _asMap(bag['skm']); - final eli = (normJson['eli'] as String?) ?? 'eli/unknown'; final norm = Norm( - eli: eli, + eli: (normJson['eli'] as String?) ?? 'eli/unknown', title: (normJson['title'] as String?) ?? '(ohne Titel)', jurabk: (normJson['jurabk'] as String?) ?? '', paragraph: (normJson['paragraph'] as String?) ?? '', - jurisdiction: JurisdictionLabel.parse( - normJson['scope'] as String?, - eli: eli, - ), standDate: _parseTimestamp(event.timestamp), sourceUrl: (normJson['source_url'] as String?) ?? '', sourceSha256: (normJson['source_sha256'] as String?) ?? '', - freshness: _parseFreshness(normJson['freshness']), - supersededNote: normJson['superseded_note'] as String?, paragraphs: _parseParagraphs(normJson['paragraphs']), ); @@ -156,26 +138,9 @@ class HubRepository implements EvaluationRepository { auditEventId: event.eventId, notes: 'Live aus Hub-Flow ${event.flowName} ' '(execution ${event.flowExecution.substring(0, 8.clamp(0, event.flowExecution.length))}).', - // Jurist sign-off is recorded by the approval, not the flow - // bag. Until the review-rewrite wires approvals in, these stay - // null (= unreviewed) unless the bag carries them explicitly. - reviewedBy: bag['reviewed_by'] as String?, - reviewedAt: DateTime.tryParse( - (bag['reviewed_at'] as String?) ?? ''), ); } - static Freshness _parseFreshness(dynamic v) { - switch ((v as String?)?.trim().toLowerCase()) { - case 'superseded' || 'amended' || 'geändert' || 'stale': - return Freshness.superseded; - case 'unknown' || 'ungeprüft': - return Freshness.unknown; - default: - return Freshness.current; - } - } - static Map? _asMap(dynamic v) => v is Map ? v : null; diff --git a/app/lib/data/models.dart b/app/lib/data/models.dart index 9a6ae17..d75f950 100644 --- a/app/lib/data/models.dart +++ b/app/lib/data/models.dart @@ -51,138 +51,6 @@ extension EvidenceTierLabel on EvidenceTier { }; } -/// Geltungsbereich — the geographic/legal scope a norm belongs to. -/// Nested by widening reach: Berlin ⊂ Deutschland ⊂ EU ⊂ Welt. -/// Every norm carries one so the UI can flag, at a glance, *which* -/// reform lever a figure speaks to (Bezirk/Senat, Bund, Brüssel, -/// or völkerrechtlich) — see Studie §5.3. -enum Jurisdiction { - /// Landesrecht Berlin (BauO Bln, ASOG, …) — Senat/Bezirk lever. - berlin, - - /// Bundesrecht (GewO, MiLoG, KassenSichV) — NKR/Bundestag lever. - deutschland, - - /// EU-Recht (DSGVO, REFIT) — Reform-Hebel liegt in Brüssel. - eu, - - /// Völker-/Welthandelsrecht (WTO, OECD, UN) — international. - international, -} - -extension JurisdictionLabel on Jurisdiction { - /// Kompakt-Code für Chips und enge Layouts. - String get short => switch (this) { - Jurisdiction.berlin => 'BE', - Jurisdiction.deutschland => 'DE', - Jurisdiction.eu => 'EU', - Jurisdiction.international => 'INT', - }; - - /// Voll ausgeschriebener Name. - String get label => switch (this) { - Jurisdiction.berlin => 'Berlin', - Jurisdiction.deutschland => 'Deutschland', - Jurisdiction.eu => 'EU', - Jurisdiction.international => 'International', - }; - - /// Rechtsebene — für Tooltip und Legend. - String get level => switch (this) { - Jurisdiction.berlin => 'Landesrecht Berlin', - Jurisdiction.deutschland => 'Bundesrecht', - Jurisdiction.eu => 'Recht der Europäischen Union', - Jurisdiction.international => 'Völker-/Welthandelsrecht', - }; - - /// Wo der Reform-Hebel ansetzt — die handlungsleitende Aussage. - String get reformLever => switch (this) { - Jurisdiction.berlin => - 'Reform-Hebel: Senat / Bezirk (Land Berlin)', - Jurisdiction.deutschland => - 'Reform-Hebel: Bundestag / NKR (Bund)', - Jurisdiction.eu => - 'Reform-Hebel: Brüssel — DE-Streichung allein wirkt nicht', - Jurisdiction.international => - 'Reform-Hebel: völkerrechtlich (WTO / OECD / UN)', - }; - - /// Ableitung aus der ELI/CELEX-Kennung, wenn kein Feld gesetzt - /// ist (Hub-Live-Modus liefert die ELI, nicht immer den Scope). - /// eli/eu/… → eu - /// eli/land/be/… → berlin - /// eli/bund/… → deutschland - /// eli/int|un|wto/… → international - static Jurisdiction fromEli(String eli) { - final e = eli.toLowerCase(); - if (e.contains('/eu/') || e.startsWith('eli/eu') || e.contains('celex')) { - return Jurisdiction.eu; - } - if (e.contains('/land/be') || e.contains('/land/berlin') || - e.contains('/be/')) { - return Jurisdiction.berlin; - } - if (e.contains('/int/') || e.contains('/un/') || e.contains('/wto/') || - e.contains('/oecd/')) { - return Jurisdiction.international; - } - return Jurisdiction.deutschland; - } - - /// Parse a serialized scope string from a hub flow bag, falling - /// back to ELI-derivation when absent or unrecognized. - static Jurisdiction parse(String? raw, {required String eli}) { - switch (raw?.trim().toLowerCase()) { - case 'berlin' || 'be' || 'land-berlin': - return Jurisdiction.berlin; - case 'deutschland' || 'de' || 'bund' || 'bundesrecht': - return Jurisdiction.deutschland; - case 'eu' || 'europa' || 'union': - return Jurisdiction.eu; - case 'international' || 'int' || 'welt' || 'global': - return Jurisdiction.international; - default: - return fromEli(eli); - } - } -} - -/// Aktualität einer Norm-Fassung relativ zu unserer Datenbasis. -/// Quelle der Wahrheit: `Norm.sourceSha256` gegen die aktuell -/// veröffentlichte Quelle + `standDate`. Surfaced als Badge, damit -/// eine veraltete Auswertung sofort erkennbar ist (Studie §5.3). -enum Freshness { - /// Unsere Fassung == aktuell veröffentlichter Stand. - current, - - /// Upstream geändert/aufgehoben — unsere Auswertung ist veraltet. - superseded, - - /// Noch nicht gegen die Quelle abgeglichen. - unknown, -} - -extension FreshnessLabel on Freshness { - String get label => switch (this) { - Freshness.current => 'aktuell', - Freshness.superseded => 'geändert', - Freshness.unknown => 'ungeprüft', - }; - - String get description => switch (this) { - Freshness.current => - 'Fassung entspricht dem aktuell veröffentlichten Stand.', - Freshness.superseded => - 'Quelle wurde upstream geändert — Auswertung veraltet, ' - 'Neubewertung nötig.', - Freshness.unknown => - 'Aktualität nicht gegen die Quelle abgeglichen.', - }; - - /// Stale-Zustände bekommen ein sichtbares Badge; `current` nicht. - bool get needsBadge => this != Freshness.current; -} - /// A citable source — never an LLM, always a public document or /// dataset reference. UI surfaces these next to every figure. class Source { @@ -239,12 +107,9 @@ class Norm { required this.title, required this.jurabk, required this.paragraph, - required this.jurisdiction, required this.standDate, required this.sourceUrl, required this.sourceSha256, - this.freshness = Freshness.current, - this.supersededNote, this.paragraphs = const [], }); @@ -252,23 +117,10 @@ class Norm { final String title; final String jurabk; final String paragraph; - - /// Geltungsbereich — Berlin / Deutschland / EU / International. - /// Surfaced as a scope badge wherever the norm appears. - final Jurisdiction jurisdiction; - final DateTime standDate; final String sourceUrl; final String sourceSha256; - /// Aktualität dieser Fassung gegenüber der Quelle. - final Freshness freshness; - - /// Optionaler Klartext-Hinweis, *was* sich geändert hat (z. B. - /// "BEG IV hat §17 zum 1.1.2026 entschärft"). Nur gesetzt, wenn - /// freshness != current. - final String? supersededNote; - /// The actual paragraphs of the norm text, normalised by /// `text.akoma_normalize` in live mode. In demo mode the /// fixture quotes the public source verbatim under §5 UrhG @@ -291,8 +143,6 @@ class Evaluation { required this.auditEventId, this.notes, this.componentSummary = const [], - this.reviewedBy, - this.reviewedAt, }); final Norm norm; @@ -329,15 +179,4 @@ class Evaluation { /// extras — each carries its tier-tag inline. Empty list ⇒ /// fall back to the notes field for the list view. final List componentSummary; - - /// Jurist who confirmed this evaluation (the `by` of the hub - /// approval). Null ⇒ not yet reviewed. Jurist review is OPTIONAL: - /// the figure stands on its own; a review only *confirms* it. - final String? reviewedBy; - - /// When the jurist signed off. Null ⇒ not reviewed. - final DateTime? reviewedAt; - - /// True once a trusted jurist has confirmed this evaluation. - bool get isReviewed => reviewedBy != null; } diff --git a/app/lib/main.dart b/app/lib/main.dart index 038c14c..83e9b52 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -9,22 +9,8 @@ import 'theme/reclaim_theme.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - // Acquiring settings + probing the hub must never prevent the - // first frame. Any failure here falls back to demo data so the - // app always paints instead of hanging on a blank page. - HubSettings settings; - EvaluationRepository repository; - try { - settings = await HubSettings.load(); - repository = await _pickRepository(settings); - } catch (_) { - settings = const HubSettings( - host: HubSettings.defaultHost, - port: HubSettings.defaultPort, - secure: false, - ); - repository = const MockRepository(); - } + final settings = await HubSettings.load(); + final repository = await _pickRepository(settings); runApp(ReclaimApp(repository: repository, settings: settings)); } diff --git a/app/lib/pages/heatmap_page.dart b/app/lib/pages/heatmap_page.dart index 3e1200c..f5832ee 100644 --- a/app/lib/pages/heatmap_page.dart +++ b/app/lib/pages/heatmap_page.dart @@ -5,7 +5,6 @@ import '../data/repository.dart'; import '../theme/reclaim_tokens.dart'; import '../widgets/mode_banner.dart'; import '../widgets/heatmap_grid.dart'; -import '../widgets/jurisdiction_badge.dart'; import '../widgets/reclaim_card.dart'; import '../widgets/tier_badge.dart'; import 'norm_detail_page.dart'; @@ -60,29 +59,26 @@ class _HeatmapPageState extends State { ), const SizedBox(height: ReclaimSpace.lg), Expanded( - child: items.isEmpty - ? _EmptyHubState(repository: widget.repository) - : ReclaimCard( - accent: true, - expand: true, - child: HeatmapGrid( - evaluations: items, - onTap: (e) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => NormDetailPage( - repository: widget.repository, - evaluation: e, - ), - ), - ); - }, + child: ReclaimCard( + accent: true, + expand: true, + child: HeatmapGrid( + evaluations: items, + onTap: (e) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => NormDetailPage( + repository: widget.repository, + evaluation: e, + ), ), - ), + ); + }, + ), + ), ), - if (items.isNotEmpty) ...[ - const SizedBox(height: ReclaimSpace.md), - const _LegendCard(), + const SizedBox(height: ReclaimSpace.md), + const _LegendCard(), const SizedBox(height: ReclaimSpace.lg), Wrap( spacing: ReclaimSpace.md, @@ -108,11 +104,6 @@ class _HeatmapPageState extends State { child: Row( mainAxisSize: MainAxisSize.min, children: [ - JurisdictionBadge( - jurisdiction: e.norm.jurisdiction, - compact: true), - const SizedBox( - width: ReclaimSpace.xs), TierBadge( tier: e.tierLowest, compact: true), const SizedBox( @@ -140,7 +131,6 @@ class _HeatmapPageState extends State { ), ], ), - ], ], ); }, @@ -206,8 +196,6 @@ class _LegendCardState extends State<_LegendCard> { symbol: '●', text: 'Betroffenheit'), _MiniLabel( symbol: '◐', text: 'Evidenz-Stufe'), - _MiniLabel( - symbol: '⌖', text: 'Geltungsbereich'), ], ), ), @@ -284,21 +272,6 @@ class _LegendCardState extends State<_LegendCard> { 'SKM-Zahl T4. Petrol → warm = stark → ' 'schwach.', ), - SizedBox(height: ReclaimSpace.sm), - _LegendRow( - symbol: '⌖', - axis: 'Chip-Symbol', - label: 'Geltungsbereich', - description: - 'Jede Norm trägt ein Scope-Chip — ' - 'BE Berlin (Landesrecht, Senat/Bezirk), ' - 'DE Deutschland (Bundesrecht, NKR/' - 'Bundestag), EU (Reform-Hebel in Brüssel), ' - 'INT International (WTO/OECD/UN). Es zeigt ' - 'an, auf welcher Ebene der Reform-Hebel ' - 'ansetzt: eine EU-Norm lässt sich nicht ' - 'durch nationale Streichung entlasten.', - ), ], ), ) @@ -312,69 +285,6 @@ class _LegendCardState extends State<_LegendCard> { } } -/// Shown in place of the heatmap when the repository returns zero -/// evaluations — almost always live-hub mode against a hub that -/// has no Recl∆Im `flow.completed` events yet (or is unreachable). -/// Tells the reader exactly how to get data on screen instead of -/// leaving a blank plot. -class _EmptyHubState extends StatelessWidget { - const _EmptyHubState({required this.repository}); - - final EvaluationRepository repository; - - @override - Widget build(BuildContext context) { - final t = Theme.of(context).textTheme; - final down = repository.mode == 'hub-down'; - return ReclaimCard( - accent: true, - expand: true, - child: Center( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 460), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Icon( - down ? Icons.cloud_off_outlined : Icons.inbox_outlined, - size: 40, - color: ReclaimColors.mute, - ), - const SizedBox(height: ReclaimSpace.md), - Text( - down - ? 'Hub nicht erreichbar' - : 'Noch keine Auswertungen im Hub', - style: t.headlineSmall, - ), - const SizedBox(height: ReclaimSpace.sm), - Text( - down - ? 'Verbunden mit ${repository.hubLabel}. Läuft ' - '`chain serve`? Sonst im Hub-Reiter den ' - 'Demo-Modus aktivieren — dann zeigt die ' - 'Heatmap das Fixture-Korpus.' - : 'Der Hub liefert keine flow.completed-Events. ' - 'Einen Recl∆Im-Flow laufen lassen, z. B.\n' - ' chain run flows/durchstich-from-file.yaml ' - '--input content=@norm.xml --input ' - 'cohort_id=berlin-kmu\n' - 'und im Hub/Studio freigeben — oder im ' - 'Hub-Reiter den Demo-Modus aktivieren.', - style: t.bodyLarge?.copyWith( - color: ReclaimColors.mute, - height: 1.4, - ), - ), - ], - ), - ), - ), - ); - } -} - class _MiniLabel extends StatelessWidget { const _MiniLabel({required this.symbol, required this.text}); diff --git a/app/lib/pages/norm_detail_page.dart b/app/lib/pages/norm_detail_page.dart index c77b628..c0ff4ca 100644 --- a/app/lib/pages/norm_detail_page.dart +++ b/app/lib/pages/norm_detail_page.dart @@ -5,11 +5,8 @@ import '../data/repository.dart'; import '../theme/reclaim_tokens.dart'; import '../widgets/mode_banner.dart'; import '../widgets/evidence_sidebar.dart'; -import '../widgets/freshness_badge.dart'; -import '../widgets/jurisdiction_badge.dart'; import '../widgets/norm_text_card.dart'; import '../widgets/reclaim_card.dart'; -import '../widgets/review_badge.dart'; import '../widgets/tier_badge.dart'; class NormDetailPage extends StatelessWidget { @@ -66,27 +63,7 @@ class NormDetailPage extends StatelessWidget { ], ), ), - Column( - crossAxisAlignment: - CrossAxisAlignment.end, - children: [ - JurisdictionBadge( - jurisdiction: e.norm.jurisdiction), - const SizedBox(height: ReclaimSpace.sm), - TierBadge(tier: e.tierLowest), - if (e.norm.freshness.needsBadge) ...[ - const SizedBox( - height: ReclaimSpace.sm), - FreshnessBadge( - freshness: e.norm.freshness, - note: e.norm.supersededNote, - ), - ], - const SizedBox(height: ReclaimSpace.sm), - ReviewBadge( - evaluation: e, showUnreviewed: true), - ], - ), + TierBadge(tier: e.tierLowest), ], ), const SizedBox(height: ReclaimSpace.lg), diff --git a/app/lib/pages/norms_list_page.dart b/app/lib/pages/norms_list_page.dart index 468dd8d..2d1ff3e 100644 --- a/app/lib/pages/norms_list_page.dart +++ b/app/lib/pages/norms_list_page.dart @@ -4,10 +4,7 @@ import '../data/models.dart'; import '../data/repository.dart'; import '../theme/reclaim_tokens.dart'; import '../widgets/mode_banner.dart'; -import '../widgets/freshness_badge.dart'; -import '../widgets/jurisdiction_badge.dart'; import '../widgets/reclaim_card.dart'; -import '../widgets/review_badge.dart'; import '../widgets/tier_badge.dart'; import 'norm_detail_page.dart'; @@ -111,20 +108,7 @@ class _NormRow extends StatelessWidget { ), ), const SizedBox(height: ReclaimSpace.sm), - Wrap( - spacing: ReclaimSpace.sm, - runSpacing: ReclaimSpace.xs, - children: [ - JurisdictionBadge( - jurisdiction: evaluation.norm.jurisdiction), - TierBadge(tier: evaluation.tierLowest), - FreshnessBadge( - freshness: evaluation.norm.freshness, - note: evaluation.norm.supersededNote, - ), - ReviewBadge(evaluation: evaluation), - ], - ), + TierBadge(tier: evaluation.tierLowest), ], ), ), diff --git a/app/lib/pages/shell_page.dart b/app/lib/pages/shell_page.dart index 01aa278..2f7b51a 100644 --- a/app/lib/pages/shell_page.dart +++ b/app/lib/pages/shell_page.dart @@ -50,11 +50,7 @@ class _ShellPageState extends State { ), child: Column( children: [ - // Static in the persistent nav rail: a 60fps pulse - // here repaints every scroll frame and stutters web - // scrolling. The breathing mark stays on the - // landing page (which isn't scrolled). - const DeltaMark(size: 36, animated: false), + const DeltaMark(size: 36), const SizedBox(height: ReclaimSpace.sm), const BrandWordmark(size: 18), Text( diff --git a/app/lib/theme/reclaim_tokens.dart b/app/lib/theme/reclaim_tokens.dart index 555900d..654bde6 100644 --- a/app/lib/theme/reclaim_tokens.dart +++ b/app/lib/theme/reclaim_tokens.dart @@ -36,23 +36,6 @@ class ReclaimColors { static const tierT2 = Color(0xFF6CA29A); // verband static const tierT3 = Color(0xFFB0AC8E); // own survey static const tierT4 = Color(0xFFA86F5C); // qualitative signal only - - // Scope badges — Geltungsbereich of a norm. Hues widen from a - // warm-local Berlin to a cool-global teal, distinct from the - // tier palette so the two chips never read as the same thing. - // Tuned for dark-first legibility on the ink canvas (bright - // enough to read as chip text/border over inkRaised). - static const scopeBerlin = Color(0xFFD0685C); // Land Berlin (brick) - static const scopeDeutschland = Color(0xFFC79A3C); // Bund (gold) - static const scopeEu = Color(0xFF5B8DD6); // EU (union blue) - static const scopeInternational = Color(0xFF4FB89E); // global (teal) - - // Freshness — is our norm version current vs. the published source. - static const freshSuperseded = Color(0xFFE0973A); // amended upstream - static const freshUnknown = Color(0xFF9AA0A6); // not reconciled - - // Jurist review — an evaluation confirmed (signed off) by a jurist. - static const reviewConfirmed = Color(0xFF6FBE7A); // confirm green } /// Spacing scale — 4/8/12/16/24/32/48 multiples, matching diff --git a/app/lib/widgets/delta_mark.dart b/app/lib/widgets/delta_mark.dart index 6ed7398..fd921cb 100644 --- a/app/lib/widgets/delta_mark.dart +++ b/app/lib/widgets/delta_mark.dart @@ -46,30 +46,17 @@ class _DeltaMarkState extends State @override Widget build(BuildContext context) { - // Static mark: no controller listening, paint once. - if (!widget.animated) { - return CustomPaint( - size: Size.square(widget.size), - painter: _DeltaPainter(pulse: 1.0), - ); - } - // Animated: isolate the per-frame repaint in its own layer so - // the breathing pulse never invalidates surrounding content - // (web scroll stays smooth). - return RepaintBoundary( - child: AnimatedBuilder( - animation: _controller, - builder: (context, _) { - final t = _controller.value; - // Breathe: scale 1 → 1.28 → 1 over the cycle. - final pulse = - 1.0 + 0.28 * (1 - (math.cos(t * 2 * math.pi).abs())); - return CustomPaint( - size: Size.square(widget.size), - painter: _DeltaPainter(pulse: pulse), - ); - }, - ), + return AnimatedBuilder( + animation: _controller, + builder: (context, _) { + final t = _controller.value; + // Breathe: scale 1 → 1.28 → 1 over the cycle. + final pulse = 1.0 + 0.28 * (1 - (math.cos(t * 2 * math.pi).abs())); + return CustomPaint( + size: Size.square(widget.size), + painter: _DeltaPainter(pulse: widget.animated ? pulse : 1.0), + ); + }, ); } } diff --git a/app/lib/widgets/freshness_badge.dart b/app/lib/widgets/freshness_badge.dart deleted file mode 100644 index 0c938ee..0000000 --- a/app/lib/widgets/freshness_badge.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../data/models.dart'; -import '../theme/reclaim_tokens.dart'; - -/// Freshness badge — flags when a norm version is stale relative to -/// the published source (amended upstream) or hasn't been reconciled. -/// By design it renders NOTHING for [Freshness.current] (the common -/// case shouldn't add noise) unless [alwaysShow] is set. Same chip -/// shape as the scope/tier badges. -class FreshnessBadge extends StatelessWidget { - const FreshnessBadge({ - super.key, - required this.freshness, - this.note, - this.compact = false, - this.alwaysShow = false, - }); - - final Freshness freshness; - - /// Optional plain-text hint of what changed (from Norm.supersededNote). - final String? note; - final bool compact; - final bool alwaysShow; - - @override - Widget build(BuildContext context) { - if (!freshness.needsBadge && !alwaysShow) { - return const SizedBox.shrink(); - } - final t = Theme.of(context).textTheme; - final color = switch (freshness) { - Freshness.current => ReclaimColors.signal, - Freshness.superseded => ReclaimColors.freshSuperseded, - Freshness.unknown => ReclaimColors.freshUnknown, - }; - final icon = switch (freshness) { - Freshness.current => Icons.check_circle_outline, - Freshness.superseded => Icons.history_toggle_off, - Freshness.unknown => Icons.help_outline, - }; - final label = compact ? freshness.label : 'Stand: ${freshness.label}'; - return Tooltip( - message: '${freshness.description}' - '${note != null && note!.isNotEmpty ? '\n$note' : ''}', - child: Container( - padding: EdgeInsets.symmetric( - horizontal: compact ? 6 : ReclaimSpace.sm, - vertical: compact ? 2 : 4, - ), - decoration: BoxDecoration( - color: color.withValues(alpha: 0.18), - border: Border.all(color: color.withValues(alpha: 0.55)), - borderRadius: const BorderRadius.all(ReclaimRadius.sm), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(icon, size: compact ? 12 : 14, color: color), - const SizedBox(width: 6), - Text( - label, - style: (compact ? t.labelSmall : t.labelLarge)?.copyWith( - color: color, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ), - ); - } -} diff --git a/app/lib/widgets/heatmap_grid.dart b/app/lib/widgets/heatmap_grid.dart index 75c6d30..3abb1a8 100644 --- a/app/lib/widgets/heatmap_grid.dart +++ b/app/lib/widgets/heatmap_grid.dart @@ -267,8 +267,7 @@ class _HeatmapPainter extends CustomPainter { ); _drawText( canvas, - 'Bereich: ${e.norm.jurisdiction.short} ' - 'Tier: ${e.tierLowest.short} Klick → Detail', + 'Tier: ${e.tierLowest.short} Klick → Detail', box.topLeft + const Offset(10, 44), const TextStyle( color: mute, diff --git a/app/lib/widgets/jurisdiction_badge.dart b/app/lib/widgets/jurisdiction_badge.dart deleted file mode 100644 index 88cdb8e..0000000 --- a/app/lib/widgets/jurisdiction_badge.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../data/models.dart'; -import '../theme/reclaim_tokens.dart'; - -/// Scope badge — surfaces a norm's Geltungsbereich (Berlin / -/// Deutschland / EU / International) wherever the norm appears, so -/// the reader sees at a glance which reform lever a figure speaks -/// to. Visually mirrors [TierBadge]: a scope glyph + code in a -/// tinted chip, here with a Material icon instead of a dot. -class JurisdictionBadge extends StatelessWidget { - const JurisdictionBadge({ - super.key, - required this.jurisdiction, - this.compact = false, - }); - - final Jurisdiction jurisdiction; - final bool compact; - - @override - Widget build(BuildContext context) { - final t = Theme.of(context).textTheme; - final color = switch (jurisdiction) { - Jurisdiction.berlin => ReclaimColors.scopeBerlin, - Jurisdiction.deutschland => ReclaimColors.scopeDeutschland, - Jurisdiction.eu => ReclaimColors.scopeEu, - Jurisdiction.international => ReclaimColors.scopeInternational, - }; - final icon = switch (jurisdiction) { - Jurisdiction.berlin => Icons.location_city_outlined, - Jurisdiction.deutschland => Icons.flag_outlined, - Jurisdiction.eu => Icons.star_outline_rounded, - Jurisdiction.international => Icons.public, - }; - final label = compact - ? jurisdiction.short - : '${jurisdiction.short} · ${jurisdiction.label}'; - return Tooltip( - message: 'Geltungsbereich: ${jurisdiction.label} ' - '(${jurisdiction.level}).\n${jurisdiction.reformLever}.', - child: Container( - padding: EdgeInsets.symmetric( - horizontal: compact ? 6 : ReclaimSpace.sm, - vertical: compact ? 2 : 4, - ), - decoration: BoxDecoration( - color: color.withValues(alpha: 0.18), - border: Border.all(color: color.withValues(alpha: 0.55)), - borderRadius: const BorderRadius.all(ReclaimRadius.sm), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(icon, size: compact ? 12 : 14, color: color), - const SizedBox(width: 6), - Text( - label, - style: (compact ? t.labelSmall : t.labelLarge)?.copyWith( - color: color, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ), - ); - } -} diff --git a/app/lib/widgets/review_badge.dart b/app/lib/widgets/review_badge.dart deleted file mode 100644 index cfb03d6..0000000 --- a/app/lib/widgets/review_badge.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../data/models.dart'; -import '../theme/reclaim_tokens.dart'; - -/// Review badge — marks an evaluation a trusted jurist has confirmed -/// (signed off via the hub approval). Jurist review is OPTIONAL: an -/// evaluation stands on its own; this only adds the "bestätigt" mark. -/// Renders a confirm-green check when reviewed; renders the muted -/// "ungeprüft" state only when [showUnreviewed] is set (otherwise -/// nothing, to keep unreviewed rows clean). -class ReviewBadge extends StatelessWidget { - const ReviewBadge({ - super.key, - required this.evaluation, - this.compact = false, - this.showUnreviewed = false, - }); - - final Evaluation evaluation; - final bool compact; - final bool showUnreviewed; - - @override - Widget build(BuildContext context) { - final reviewed = evaluation.isReviewed; - if (!reviewed && !showUnreviewed) return const SizedBox.shrink(); - - final t = Theme.of(context).textTheme; - final color = - reviewed ? ReclaimColors.reviewConfirmed : ReclaimColors.mute; - final icon = reviewed - ? Icons.verified_outlined - : Icons.radio_button_unchecked; - final label = reviewed - ? (compact ? 'geprüft' : 'Jurist-geprüft') - : (compact ? 'offen' : 'ungeprüft'); - final tip = reviewed - ? 'Bestätigt von ${evaluation.reviewedBy}' - '${evaluation.reviewedAt != null ? ' am ${_d(evaluation.reviewedAt!)}' : ''}.\n' - 'Jurist-Review ist optional — es bestätigt die Auswertung, ' - 'ist aber keine Voraussetzung.' - : 'Noch von keinem Juristen bestätigt. Die Auswertung gilt ' - 'trotzdem (Review ist optional).'; - return Tooltip( - message: tip, - child: Container( - padding: EdgeInsets.symmetric( - horizontal: compact ? 6 : ReclaimSpace.sm, - vertical: compact ? 2 : 4, - ), - decoration: BoxDecoration( - color: color.withValues(alpha: 0.18), - border: Border.all(color: color.withValues(alpha: 0.55)), - borderRadius: const BorderRadius.all(ReclaimRadius.sm), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(icon, size: compact ? 12 : 14, color: color), - const SizedBox(width: 6), - Text( - label, - style: (compact ? t.labelSmall : t.labelLarge)?.copyWith( - color: color, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ), - ); - } - - static String _d(DateTime d) => - '${d.day.toString().padLeft(2, '0')}.${d.month.toString().padLeft(2, '0')}.${d.year}'; -} diff --git a/flows/durchstich-full.yaml b/flows/durchstich-full.yaml deleted file mode 100644 index 8b86747..0000000 --- a/flows/durchstich-full.yaml +++ /dev/null @@ -1,92 +0,0 @@ -# Vertikaler Durchstich, Vollversion (so weit heute lauffähig). -# -# Erweitert durchstich-from-file um die Analyse-Module, deren -# Eingaben sich aus vorhergehenden Step-Outputs speisen lassen -# (alle als $ref) — Pflichten, Zitationsgraph, Lesbarkeit/Frust. -# Das flow.completed-Event trägt damit norm + duties + citations -# + frust + cohort, also die Form, die HubRepository.list() in der -# Recl∆Im-App in Evaluation-Objekte übersetzt. -# -# NICHT enthalten: econ.skm_score und law.benefit_score. Beide -# Module lesen ihren Tupel-/Ratings-Input über `require_json`, -# d.h. sie erwarten ein Payload::Json. In einem Flow lässt sich -# ein Json-Wert aber nur aus einem vorhergehenden Step-Output -# beziehen — `chain run --input name=value` erzeugt Text, -# `--input name=@file` erzeugt Bytes, nie Json, und ein Inline- -# Map-Literal in `with:` lehnt der Flow-Parser ab ("expected a -# string"). Es gibt heute also keinen Weg, skm/benefit aus CLI- -# Inputs zu treiben. Siehe Handoff an chain (json-CLI-Input). -# Sobald der Plattform-Fix steht, die skm-/benefit-Steps unten -# einkommentieren (Form: `with: { duties: $inputs.duties }` mit -# `inputs: { duties: json }`). -# -# Aufruf: -# chain run flows/durchstich-full.yaml \ -# --input content=@/tmp/gewo-14-mini.xml \ -# --input cohort_id=berlin-kmu -# (pausiert am system.approval-Gate — danach via Hub/Studio -# freigeben, dann wird das flow.completed-Event geschrieben.) - -name: durchstich-full - -inputs: - content: bytes - cohort_id: text - -steps: - - id: normalize - use: text.akoma_normalize@^0 - with: - content: $inputs.content - mime: "application/xml" - - - id: deontic - use: text.deontic_extract@^0 - with: - norm: $normalize.norm - - - id: citations - use: graph.citation_extract@^0 - with: - norm: $normalize.norm - - - id: cohort - use: stats.cohort_size@^0 - with: - cohort_id: $inputs.cohort_id - - - id: frust - use: text.readability_score@^0 - with: - norm: $normalize.norm - graph: $citations.citations - - # Pending platform json-CLI-input (Handoff → chain). Form, die - # nach dem Fix gilt — `inputs:` oben um `duties: json` / - # `ratings: json` ergänzen und mit --input duties=… speisen: - # - id: skm - # use: econ.skm_score@^0 - # with: - # duties: $inputs.duties - # - id: benefit - # use: law.benefit_score@^0 - # with: - # ratings: $inputs.ratings - - - id: review - use: system.approval@^0 - with: - title: "Recl∆Im — Review GewO §14 (Live)" - prompt: >- - Juristische Freigabe: Stimmen die extrahierten Pflichten, die - Zitationen und die Norm-Wiedergabe mit dem geltenden Gesetzestext - überein? Quellen siehe Payload. Bei Freigabe gilt die Auswertung - als juristisch bestätigt. - payload: $normalize.norm - -outputs: - norm: $normalize.norm - frust: $frust.score - cohort: $cohort.cohort - duties: $deontic.duties - citations: $citations.citations diff --git a/store/.gitignore b/store/.gitignore deleted file mode 100644 index 6ee5de4..0000000 --- a/store/.gitignore +++ /dev/null @@ -1 +0,0 @@ -bundles/ diff --git a/store/SHA256.local.txt b/store/SHA256.local.txt deleted file mode 100644 index 4d4be3a..0000000 --- a/store/SHA256.local.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Local module.wasm sha256 (cross-check; canonical sha must come from the hosted .fai bundle) -0fd01a7aa7ca2a47aa27bea7b30f8b8fe2ffc551728694c5dfbadb75bc37e91d text-akoma-normalize -722da24ebc239015af344b66a438097ef607b965badb43f91e43c4b1a9fdcc19 text-deontic-extract -3f4976db72bd7410e83b0694de3e91c09b8829d3d73fa48ddf806c170cc5c602 graph-citation-extract -ba95c610cec71a694f0c33002de836e61c1bd9b9767d014e842e083cd3eead2c text-readability-score -bb722a4416e26cc58cf3d01e5f9aaf27c61f9fa2cb86983662ee16f0c68945d9 stats-cohort-size -aefa3588c33e6977ce26d4f885d4fd75ea46ee6f479ecf1e57c1ebd33b6872a1 graph-shapley-attribution -3b63c8ae21d82da0fb47e160889fa93b34f132947c461e5c199936207a2cb6a9 econ-skm-score -1275295ec9d61cccaaeb418a580637dc6ca2d8e6b5aa25937d9471b1f534cad9 law-benefit-score diff --git a/store/SHA256.txt b/store/SHA256.txt deleted file mode 100644 index d7324ae..0000000 --- a/store/SHA256.txt +++ /dev/null @@ -1,9 +0,0 @@ -# .fai bundle sha256 (unsigned, chain pack 0.17.2) — 2026-06-19T00:12Z -15fa6d43bf6d18193282cb1c94c273b958e2639d91d52120eaedd83a99571765 text-akoma-normalize-0.1.0.fai -d03d3dc2bfbaa413ea4e192c6147595e21d8dafd24b5d32b853a03a38e48100d text-deontic-extract-0.1.0.fai -9bebd3dc877da9e3c89b521572000f14f007c6a9edfcc350edd44377d68acba3 graph-citation-extract-0.1.0.fai -5f1a0098bc4c3be8d20b34d848af884b539b0eda3c59ab56dfe99bf8f811090d text-readability-score-0.1.0.fai -31d35f6a7b61b9249520e3ae02fa398292375555430714b04ff0771fd2f3ad99 stats-cohort-size-0.1.0.fai -199927a6b8594da579a22d42b1ba438f4b2b6c90132a5c097c7c1d5eb8b9c319 graph-shapley-attribution-0.1.0.fai -2b887970d448c07cc53633124518d6f96672b0710d9e89fe4f9d6a236035f25e econ-skm-score-0.1.0.fai -4a82a07ba72cae4b49f1ff883b1ca92638e5f4ea19bc2b070288142056764025 law-benefit-score-0.1.0.fai diff --git a/store/store.yaml b/store/store.yaml deleted file mode 100644 index 42195b5..0000000 --- a/store/store.yaml +++ /dev/null @@ -1,167 +0,0 @@ -# Recl∆Im module store index — source of truth before publish. -# -# Target host (chain Studio's one-click "Recl∆Im hinzufügen" button -# points here): https://releases.chain.flemming.ai/reclaim/store.yaml -# -# STATUS (2026-06-19). Per chain: publish UNSIGNED for now — -# `require_signatures` is off by default; FAI_SIGNING_KEY untouched; -# a Recl∆Im-own signing key + per-store pinning come later. -# * sha256 — REAL, from local `chain pack` of each module. -# Bundles staged in store/bundles/, hashes mirrored -# in store/SHA256.txt. Bundles are UNSIGNED. -# * signature — intentionally absent for now (see above). -# * wasm_url — points at the intended mirror path. The ONLY -# remaining step is HOSTING the 8 .fai + this -# store.yaml at releases.chain.flemming.ai/reclaim/ -# — the mirror push is chain infra (scp to the -# Hetzner host); reclaim has no SSH access. Confirm -# the canonical wasm_url host (mirror vs Forgejo -# release) with chain. -# -# NAMING NOTE for chain: every module's `provides.capability` uses an -# UNDERSCORE (`econ.skm_score`), but this index + the Studio button use -# the DASH form (`econ.skm-score`) per the handoff. install resolution -# must map dash↔underscore, or the modules must be renamed. Flagging — -# this is a chain naming-convention decision. - -schema_version: 1 - -modules: - - name: text.akoma-normalize - versions: - - version: 0.1.0 - status: published - wasm_url: https://releases.chain.flemming.ai/reclaim/text-akoma-normalize/v0.1.0/text-akoma-normalize-0.1.0.fai - sha256: 15fa6d43bf6d18193282cb1c94c273b958e2639d91d52120eaedd83a99571765 - tagline: - en: "Normalize legal documents to Akoma Ntoso" - de: "Rechtsdokumente nach Akoma Ntoso normalisieren" - description: - en: "Normalises heterogeneous legal sources (gesetze-im-internet XML, EUR-Lex FORMEX, landesrecht-berlin HTML) into a structured Akoma Ntoso 1.0 (OASIS LegalDocML) representation." - de: "Normalisiert heterogene Rechtsquellen (gesetze-im-internet-XML, EUR-Lex-FORMEX, landesrecht-berlin-HTML) in eine strukturierte Akoma-Ntoso-1.0-Darstellung (OASIS LegalDocML)." - category: text-processing - tags: [legal, german, akoma-ntoso, offline-capable] - license: Apache-2.0 - repository: https://git.flemming.ai/chain-modules/text-akoma-normalize - - - name: text.deontic-extract - versions: - - version: 0.1.0 - status: published - wasm_url: https://releases.chain.flemming.ai/reclaim/text-deontic-extract/v0.1.0/text-deontic-extract-0.1.0.fai - sha256: d03d3dc2bfbaa413ea4e192c6147595e21d8dafd24b5d32b853a03a38e48100d - tagline: - en: "Extract duties, prohibitions and permissions from norms" - de: "Pflichten, Verbote und Erlaubnisse aus Normen extrahieren" - description: - en: "Extracts atomic deontic statements (obligation / prohibition / permission) from a normalised norm. Useful far beyond law — contracts, ISO standards, internal policies." - de: "Extrahiert atomare deontische Aussagen (Pflicht / Verbot / Erlaubnis) aus einer normalisierten Norm. Auch jenseits des Rechts nützlich — Verträge, ISO-Normen, interne Richtlinien." - category: text-processing - tags: [legal, german, nlp, offline-capable] - license: Apache-2.0 - repository: https://git.flemming.ai/chain-modules/text-deontic-extract - - - name: graph.citation-extract - versions: - - version: 0.1.0 - status: published - wasm_url: https://releases.chain.flemming.ai/reclaim/graph-citation-extract/v0.1.0/graph-citation-extract-0.1.0.fai - sha256: 9bebd3dc877da9e3c89b521572000f14f007c6a9edfcc350edd44377d68acba3 - tagline: - en: "Build typed citation graphs from legal text" - de: "Typisierte Zitationsgraphen aus Rechtstext bauen" - description: - en: "Extracts typed citations (cites, verweist_iVm, …) from German legal text per the Coupette 2021 schema. Citation graphs also fit scientific papers, patents and case law." - de: "Extrahiert typisierte Zitationen (cites, verweist_iVm, …) aus deutschem Rechtstext nach dem Coupette-2021-Schema. Zitationsgraphen passen auch zu Papers, Patenten und Rechtsprechung." - category: data - tags: [legal, german, graph, offline-capable] - license: Apache-2.0 - repository: https://git.flemming.ai/chain-modules/graph-citation-extract - - - name: text.readability-score - versions: - - version: 0.1.0 - status: published - wasm_url: https://releases.chain.flemming.ai/reclaim/text-readability-score/v0.1.0/text-readability-score-0.1.0.fai - sha256: 5f1a0098bc4c3be8d20b34d848af884b539b0eda3c59ab56dfe99bf8f811090d - tagline: - en: "Composite readability and complexity score" - de: "Komposit-Score für Lesbarkeit und Komplexität" - description: - en: "Composite readability score for any text — DE-adapted Flesch + reference depth + verb distance. Flags hard-to-read government letters, terms & conditions, abstracts." - de: "Komposit-Lesbarkeitsscore für beliebige Texte — DE-adaptierter Flesch + Verweistiefe + Verbdistanz. Markiert schwer lesbare Behördenbriefe, AGB und Abstracts." - category: text-processing - tags: [german, readability, analytics, offline-capable] - license: Apache-2.0 - repository: https://git.flemming.ai/chain-modules/text-readability-score - - - name: stats.cohort-size - versions: - - version: 0.1.0 - status: published - wasm_url: https://releases.chain.flemming.ai/reclaim/stats-cohort-size/v0.1.0/stats-cohort-size-0.1.0.fai - sha256: 31d35f6a7b61b9249520e3ae02fa398292375555430714b04ff0771fd2f3ad99 - tagline: - en: "Resolve addressee cohort sizes" - de: "Größe von Adressaten-Kohorten ermitteln" - description: - en: "Resolves the size of an addressee cohort (e.g. 'Berlin SMEs by WZ code'). v0.1 ships a stub table for the pilot cohorts; v0.2 adds a DESTATIS GENESIS-API adapter." - de: "Ermittelt die Größe einer Adressaten-Kohorte (z. B. 'Berliner KMU nach WZ-Code'). v0.1 mit Stub-Tabelle für die Pilot-Kohorten; v0.2 mit DESTATIS-GENESIS-API-Adapter." - category: data - tags: [statistics, german, government, offline-capable] - license: Apache-2.0 - repository: https://git.flemming.ai/chain-modules/stats-cohort-size - - - name: graph.shapley-attribution - versions: - - version: 0.1.0 - status: published - wasm_url: https://releases.chain.flemming.ai/reclaim/graph-shapley-attribution/v0.1.0/graph-shapley-attribution-0.1.0.fai - sha256: 199927a6b8594da579a22d42b1ba438f4b2b6c90132a5c097c7c1d5eb8b9c319 - tagline: - en: "Shapley-style attribution on weighted graphs" - de: "Shapley-Attribution auf gewichteten Graphen" - description: - en: "Splits a target node's blame or credit among its ancestors on a weighted norm/citation graph. v0.1 proportional fallback; v0.2 full Shapley value for ≤32-node neighbourhoods." - de: "Verteilt 'Schuld' oder 'Verdienst' eines Zielknotens auf seine Vorgänger in einem gewichteten Norm-/Zitationsgraphen. v0.1 proportional; v0.2 voller Shapley-Wert für ≤32-Knoten-Nachbarschaften." - category: data - tags: [graph, analytics, attribution, offline-capable] - license: Apache-2.0 - repository: https://git.flemming.ai/chain-modules/graph-shapley-attribution - - - name: econ.skm-score - versions: - - version: 0.1.0 - status: published - wasm_url: https://releases.chain.flemming.ai/reclaim/econ-skm-score/v0.1.0/econ-skm-score-0.1.0.fai - sha256: 2b887970d448c07cc53633124518d6f96672b0710d9e89fe4f9d6a236035f25e - tagline: - en: "Standard Cost Model (SKM) bureaucracy cost" - de: "Standardkostenmodell (SKM) — Bürokratiekosten" - description: - en: "Standard Cost Model — P×F×T×h per duty, summed across a duty list (German NKR method). The EU- and OECD-recognised methodology for bureaucracy-cost measurement, used by NL ATR, UK RPC, EU REFIT." - de: "Standardkostenmodell — P×F×T×h je Pflicht, summiert über eine Pflichtenliste (NKR-Methode). EU- und OECD-anerkannte Methodik zur Bürokratiekostenmessung, genutzt von NL ATR, UK RPC, EU REFIT." - category: data - tags: [economics, legal, government, offline-capable] - license: Apache-2.0 - repository: https://git.flemming.ai/chain-modules/econ-skm-score - - - name: law.benefit-score - versions: - - version: 0.1.0 - status: published - wasm_url: https://releases.chain.flemming.ai/reclaim/law-benefit-score/v0.1.0/law-benefit-score-0.1.0.fai - sha256: 4a82a07ba72cae4b49f1ff883b1ca92638e5f4ea19bc2b070288142056764025 - tagline: - en: "7-dimensional regulatory benefit score" - de: "7-dimensionaler Nutzen-Score für Regulierung" - description: - en: "Seven-dimensional benefit score (protection / market / legal-certainty / EU-single-market / safety / environment / revenue), each 0–5. Domain-bound: the dimensions are legally-politically conventioned." - de: "Siebendimensionaler Nutzen-Score (Schutz / Markt / Rechtssicherheit / EU-Binnenmarkt / Sicherheit / Umwelt / Einnahmen), je 0–5. Domänengebunden: die Dimensionen sind juristisch-politisch konventioniert." - category: data - tags: [legal, german, policy, offline-capable] - license: Apache-2.0 - repository: https://git.flemming.ai/chain-modules/law-benefit-score - -services: [] -flows: []