Commit graph

31 commits

Author SHA1 Message Date
b4461cca13 feat(ui): jurist review page (Freigaben) — confirm/reject approvals
A new 'Freigaben' nav destination lists the hub's pending
system.approval tasks (listApprovals), shows each prompt + norm
payload, and lets a jurist confirm ('Korrekt — freigeben') or reject
with a reason via the SDK (approve/reject). The reviewer's name is
carried into the hub's hash-chained audit log. Review is optional and
live-only; the trusted-jurist registry + login is enforced hub-side
(chain handoff). Verified against the live hub: lists 3 pending.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-19 02:46:50 +02:00
4e00c83591 feat(ui): heatmap view modes (all / jurist-reviewed / marked)
A segmented toggle over the heatmap: 'Alle' shows every evaluation,
'Geprüft (N)' filters to jurist-confirmed ones, 'Markiert' shows all
with a confirm-green check on the reviewed points. Jurists are
optional — the platform works without them and the default is 'Alle';
review only confirms a figure. Empty filtered view gets its own hint.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-19 02:42:47 +02:00
425a0e5790 feat(live): real live mode via HubClient.submit → outputs
HubRepository.list() now runs the inline reclaim-evaluate flow per
pilot norm via submit() and maps the returned outputs to Evaluation
— the correct path, since the hub audit log stores only hashes, not
outputs. skm/benefit get their structured input as JSON (submit
jsonInputs), so the live heatmap gets real Schaden/Nutzen axes.

- live_inputs.dart: the inline eval flow + the 5 pilot norms' inputs
  (XML asset, cohort_id, skm duties, benefit ratings) plus curated
  overrides the bund adapter can't derive (true Geltungsbereich for
  the EU/Berlin norms, freshness, jurist sign-off).
- assets/norms/*.xml: 5 gesetze-im-internet-style source documents.
- tool/live_smoke.dart: dev smoke test (5/5 evaluate against a live
  hub: Schaden 3M–450M €, Nutzen 2.0–3.8, EU/Berlin scope correct).

Needs econ.skm_score's float-population fix (chain-modules commit
80f6620).

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-19 02:39:15 +02:00
aabff2b140 feat(ui): scope, freshness and jurist-review badges + live empty-state
Adds three dark-first chip badges in the TierBadge pattern:
- Jurisdiction (Berlin/DE/EU/International) — derived from the ELI,
  surfaced on the norms list, detail header and heatmap chips/hover.
- Freshness (aktuell/geaendert/ungeprueft) — flags norms amended
  upstream (standDate + sourceSha256); renders only when stale.
- Jurist review — marks evaluations a trusted jurist has confirmed
  (optional; the figure stands on its own).
Models gain Jurisdiction/Freshness enums, Norm.freshness/supersededNote
and Evaluation.reviewedBy/reviewedAt; HubRepository parses them from
the flow bag (ELI fallback). HeatmapPage shows a real empty-state
(hub-down vs no-evaluations) instead of a blank plot, and connect()
no longer throws on a failed probe.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-19 02:25:39 +02:00
9dfa418e92 fix(ui): never hang on a blank frame when the hub is unreachable
main() awaited the hub health probe before runApp(); a gRPC-web
error out of HubRepository.connect propagated and left the app on a
white page. Guard the settings+probe in main() with a MockRepository
fallback so the first frame always paints.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-19 02:25:39 +02:00
136d3b48c6 perf(ui): stop the nav-rail brand pulse to fix web scroll jank
The DeltaMark ran an endless 9s breathing animation (60fps repaint)
inside the persistent nav rail, so it repainted on every scroll
frame and stuttered scrolling on Flutter web. Make the rail mark
static (animated: false) and isolate the animated variant (landing
page) behind a RepaintBoundary.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-19 02:25:38 +02:00
51beb332c4 feat(ui): ModeBanner replaces DemoBanner + no silent mock-fallback
Two related fixes so the app never silently presents itself as
a demo when it is in fact running against a real hub:

  - The orange 'Demo-Daten · KEINE Rechtsberatung' banner was
    hard-coded into every data screen, regardless of whether
    the active repository was the in-memory MockRepository or
    the live HubRepository. Replaced with ModeBanner, which
    picks one of three voices by repository.mode:

      demo      orange T4 — 'Demo-Daten · KEINE Rechtsberatung'
      hub-live  petrol     — 'Live aus Hub · <endpoint>'
      hub-down  warm-warn  — 'Hub nicht erreichbar — leere Liste'

  - main.dart's _pickRepository used to fall back to
    MockRepository whenever HubRepository.connect() came back
    unhealthy. That silently downgraded a live deployment to
    demo data, which is exactly what the user is trying to avoid.
    Removed: when useHub=true, the HubRepository is returned
    regardless. Its list() returns [] on hub-down, and the
    'hub-down' banner explains why the screens are empty.
    Mock data only shows when the user explicitly turns useHub
    off in the Hub-Reiter.

Also: legend arrow icons now point in the actual axis direction
(→ for Schaden, ↑ for Nutzen) instead of the bidirectional ↔/↕,
which suggested the axes ran both ways. Mini-chips and the
expanded legend rows are consistent.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 15:57:50 +02:00
e6e9c0753d feat(hub): default to live hub on startup + accept partial flow bags
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 <sf@flemming.it>
2026-06-18 15:43:15 +02:00
1be2ad075d fix(ui): chip wrap below the heatmap shows the norm title too
The compact chips under the heatmap showed only [Tier-Badge]
jurabk paragraph (e.g. 'T4 GewO § 14'), which was enough for a
methodologically oriented reader but not for someone scanning
'what was that pilot norm about?'. Adds a muted mid-dot + the
norm.title after the paragraph, so each chip now reads:

  T4 · amtlich  GewO § 14  ·  Anzeigepflicht

The chips stay clickable, navigation behaviour unchanged.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 15:38:23 +02:00
ae9fc45177 feat(ui): show SKM component-tier summary on the Normen list
Each Evaluation grows a componentSummary field — a small list of
plain strings, one per SKM ingredient (P / F / T / h), tagged
with its tier in the form 'P · T1 — DESTATIS Gewerbeanzeigen'.
The fixtures fill it in for all five pilot norms; the field
defaults to an empty list so flow-produced Evaluations remain
backwards-compatible until a future flow step writes one.

NormsListPage re-arranges the row to make use of the wide-screen
real-estate on the right that used to sit empty:

  ┌─────────────────────────────────────────────────────────────┐
  │  GewO § 14 — Anzeigepflicht  │ Schaden    │ Komponenten      │
  │  eli/bund/gewo/14             │ 2.8 Mio €  │ P · T1 …         │
  │  [T1 · amtlich]               │ Nutzen     │ F · T1 …         │
  │                               │ 2.5 / 5    │ T · T1 …         │
  │                               │ Betroffene │ h · T3 …         │
  │                               │ 180 k      │                  │
  └─────────────────────────────────────────────────────────────┘

The TierBadge moved into the title column (right under the eli)
so the eye lands on the tier of the figure right where the
figure's identity is named. The three metric columns stack
vertically (no more horizontal-only row) which leaves the right
third of the row free for the componentSummary lines — the
information a reviewer was always asking for ('which Komponente
is the bottleneck here?') is now visible without opening the
detail page.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 15:06:33 +02:00
c6400302dd feat(brand): BrandWordmark renders ∆I in petrol, lifts the logo
Adds a dedicated BrandWordmark widget that splits 'Recl∆Im'
into three TextSpans and tints the middle ∆I in the petrol
signal colour. The fai_web universe uses this same accent rule
for the F∆I and Ch∆In glyphs — Recl∆Im now follows it, so
the brand stays visually coherent across every F∆I surface.

Where it lands:

  - Landing page: large DeltaMark (96 px) and BrandWordmark
    (56 px) sit on a single row, the wordmark to the right of
    the mark. The two together carry the brand identity that
    a viewer sees first when opening the app.
  - NavigationRail leading: the muted 'Recl∆Im' Text line is
    replaced with the petrol-accented BrandWordmark (18 px),
    keeping the vendor tag 'F∆I' as a small muted line under it.

widget_test.dart adapts: the literal 'Recl∆Im'-find no longer
matches because the wordmark is now three text spans, so the
test checks for BrandWordmark by type — equivalent assertion,
robust to glyph-level splits.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 15:06:13 +02:00
94f4473686 feat(ui): collapsible legend gives the heatmap more vertical room
The legend below the heatmap used four full prose paragraphs at
all times — visually it claimed about a quarter of the heatmap
page's height while the plot itself was the actual artefact.
Now the legend defaults to a single-line header strip:

  Legende    ↔ Schaden    ↕ Nutzen    ● Betroffenheit
             ◐ Evidenz-Stufe                              ⌄

Tap anywhere on the strip and an AnimatedSize transition opens
the full _LegendRow descriptions (180 ms). Same content, hidden
until asked for. The Expanded(heatmap) above reclaims the
height — concretely on a 900 px window the plot grows from
~420 px to ~600 px tall, which is what makes hover-targeting
the smaller points actually comfortable.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 14:54:01 +02:00
7c92268238 chore(lint): make const-correct _LegendRow callsites
Pure lint follow-up, no behaviour change.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 14:46:36 +02:00
1ad523a91a fix(ui): make T1–T4 readable + lift Recl∆Im into the nav rail
Three small frictions a first-time reader hit at the same time,
fixed in one pass:

  1. The TierBadge said 'T1' without context. Default form is
     now 'T1 · amtlich' / 'T2 · Verband' / 'T3 · Erhebung' /
     'T4 · Signal'. Compact form (heatmap chip wrap) keeps the
     bare code. Tooltip spells out the long form + cites the
     Regel der niedrigsten Stufe (Studie §6.7) so the meaning
     of the cap is at hand on hover.

  2. The Legende's tier row was the abstract one of the four —
     it now inlines all four stage names (amtlich / Verband /
     Erhebung / Signal) and explains why a single T4 component
     drags a whole figure to T4. No more 'what is Tier?' on
     screen.

  3. The DemoBanner repeated 'Tier T4' without explaining what
     T4 is. Now: 'qualitatives Signal — bis NKR/Verbands-
     Pipeline + Juristen-Approval laufen'. The cause-and-cure
     is visible at a glance.

  4. The NavigationRail leading column showed only 'F∆I' as
     brand mark. Now shows 'Recl∆Im' prominently (petrol-signal,
     bold) with 'F∆I' as a small muted vendor tag below.
     Product first, vendor second.

EvidenceTierLabel extension grows a new 'headline' getter for
the one-word default ('amtlich'/'Verband'/…), and 'description'
gets longer, source-naming text (NKR / DESTATIS / DIHK / IHK /
ZDH / BDI). Code paths that used the old short-only badge keep
working because 'short' still returns 'T1'.

flutter pub get refreshed against chain_client_sdk 0.18, which
the fai_chain agent confirmed already carries the gRPC-Web
channel — no SDK bump needed for the web-hub-mode wire.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 14:44:14 +02:00
f61865a3c2 feat(ui): add legend card below the heatmap
The four-line description above the heatmap was concise but
opaque: 'X: log10 €/year. Y: 0–5. Punktgröße.' said the right
words, but a reviewer who isn't already methodologically
oriented can't tell what they're looking at.

Adds a dedicated Legende-Karte directly under the heatmap.
Four rows, each: leading symbol (↔ ↕ ● ◐), axis-or-quantity
label, prominent name (Schaden / Nutzen / Betroffenheit /
Evidenz-Stufe), and a short prose explanation tying the visual
back to the Studie:

  ↔  Abszisse (X)  Schaden       — log10 €/Jahr, SKM §3.1
  ↕  Ordinate (Y)  Nutzen        — 7-dim. Komposit §3.2
  ●  Punktgröße    Betroffenheit — sqrt scale, DESTATIS-/IHK
  ◐  Tier-Farbe    Evidenz-Stufe — Regel der niedrigsten §6.7

Top description tightened to one line of context only:
'Schaden × Nutzen × Betroffenheit pro Norm. Klick auf einen
Punkt öffnet das Detail …'. No redundancy with the legend,
no jargon up top.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 14:41:07 +02:00
5de87697cf fix(web): replace Flutter default placeholders with Recl∆Im branding
The generated web target still carried 'reclaim_app' titles and the
'A new Flutter project.' description. Set the document title,
apple-mobile-web-app-title, PWA manifest name/short_name to the
display mark Recl∆Im and give both the index meta and the manifest
a real product description. The Dart package identifier reclaim_app
stays — only the user-facing strings change.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 14:29:24 +02:00
5fc1ecc98a docs(macos): extend CFBundleDisplayName with the one-line claim
Window title and menu bar now read 'Recl∆Im — Pflicht-basierte
Wirkungsanalyse' instead of just 'Recl∆Im'. The product name
alone said nothing about what the app does; the tagline ties
the brand to the Studie's methodische Kernaussage in the
fenster-chrome itself — that's the line a reviewer will see
on screenshots, AG-Folien, Dock-Tooltips.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 14:26:20 +02:00
febbf0b505 feat(web): add serve-web.sh + document hub smoke + prod-deploy path
serve-web.sh builds the release web bundle and serves it via
python3's stdlib http.server on http://127.0.0.1:8080, so an
AG reviewer can click a link instead of being asked to install
Flutter to see the demo.

RUN.md grows three new sections:

  - Web-Variante: single-command local serve, scope (mock works
    1:1, hub mode pending gRPC-Web in chain_client_sdk_dart).
  - Production deploy path: Hetzner-CX + Caddy, DNS
    reclaim.flemming.ai — matches the existing F∆I Hetzner
    pattern documented in fai_chain release-host.
  - Hub-Smoke-Test: how to verify the new HubRepository.list()
    wiring against a separately-running 'chain serve'
    (Hub-Reiter → Verbindung testen → flow.completed events
    populate the Heatmap-Tab once chain run has been invoked
    and the approval gate is cleared).

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 14:19:59 +02:00
dff5685686 feat(hub): implement HubRepository.list() — flow.completed → Evaluation
Replaces the Phase-0 placeholder (returns const []) with a real
event-log reader. When the app starts in hub mode and the hub
is healthy, list() now:

  1. Calls HubClient.eventLog(types: ['flow.completed']) over
     gRPC (up to 200 most recent events).
  2. Decodes each event's detail JSON as the Recl∆Im output bag
     produced by flows/durchstich-gewo-14.yaml — keys: norm,
     skm, benefit, frust, cohort, duties.
  3. Skips events from other flows (detected by absence of
     norm + skm keys). One hub can serve many flows; only
     Recl∆Im-shaped runs surface here.
  4. Sorts by skmEurPerYear DESC, matching MockRepository's
     'most-harmful first' ordering — UI behaves identically
     across the mock-vs-live switch.

Field mapping (tolerant on absence, defaults documented):

  norm.eli / title / jurabk / paragraph / paragraphs[]
                                          -> Norm
  skm.total_eur_per_year, skm.tier_lowest -> skmEurPerYear, tier
  benefit.total                           -> benefitScore
  frust.composite_frust                   -> frustScore
  cohort.count                            -> affectedCount
  cohort.source + .tier                   -> first Source entry
  duties.duties[]                         -> List<Duty>
  event.eventId                           -> auditEventId
  event.timestamp                         -> norm.standDate

byEli() walks the same list once; for small per-norm caches we
defer batching to Phase 2.

Empty list = honest state: 'hub connected, no Recl∆Im flow
runs yet'. Caller decides how to render that (HeatmapPage
currently shows an empty grid; a clearer 'chain run flows/'-
hint lands in the same pass as the demo-banner reorganisation).

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 14:18:10 +02:00
12b6961f7e docs(fixtures): expose P/F/T/h component-tier rationale per pilot norm
Each of the five fixture Evaluations now carries a notes field
that breaks the lowest-tier-rule (Studie §6.7) into its four
SKM components (Population, Frequenz, Tarif, Zeitaufwand) and
names which one is the bottleneck. Tier markings stay
conservatively T4 on the surface — what changes is that a
reviewer reading the norm-detail panel now sees:

  - which Komponenten are real T1 numbers (DESTATIS-derived P,
    Verdiensterhebung-T) vs. where the demo extrapolates
  - which Komponenten still wait on NKR-Validierung (mostly h)
  - the political bottom line per norm: GewO §14 needs an
    NKR-h-Wert; KassenSichV is largely documented; DSGVO Art.30
    is EU-bound, reform-Hebel sits in Brüssel; BauO Bln §60 is
    Land-domain without Bundes-NKR support; MiLoG §17 is under
    Bürokratieentlastungsgesetz-IV-Bewegung.

No data-model change. Same fixtures, more honest reading.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 14:15:51 +02:00
7e9687afc6 fix(macos): set CFBundleDisplayName to Recl∆Im
The macOS window title and the application menu bar (About / Hide
/ Quit) were rendering 'reclaim_app' — the technical bundle name —
because Info.plist only carried CFBundleName from $(PRODUCT_NAME).
CFBundleDisplayName takes precedence for everything the user sees,
so adding it pins the product name independently of the binary
filename. The .app is still reclaim_app.app on disk; the title bar
now reads Recl∆Im.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 14:13:52 +02:00
7efe84de34 docs(legal): fill Impressum + Datenschutz from authoritative fai_web data
Replaces the [PLACEHOLDER] entries in the Recht-Tab with the
real legal-entity data published on flemming.ai (single source
of truth across F∆I surfaces):

  - Anbieter   Dr. Stefan Flemming, Flemming.IT, freiberuflich
  - Anschrift  Dardanellenweg 41, 12109 Berlin
  - Kontakt    contact@flemming.ai, +49 30 66302929
  - USt-IdNr.  DE269393425 (§ 27a UStG)
  - Verantw.   Dr. Stefan Flemming (§ 18 Abs. 2 MStV)
  - Hosting    Hetzner Online GmbH, Gunzenhausen (AVV nach Art. 28)
  - Logs       max. 14 Tage

Also brings the wording in line with the current §-Lage:

  - § 5 TMG / § 7 Abs. 1 TMG → § 5 DDG / § 7 Abs. 1 DDG
    (Digitale-Dienste-Gesetz seit Mai 2024 — das TMG existiert
    in der zitierten Form nicht mehr).
  - Adds the standard 'Haftung für Inhalte', 'Haftung für
    Links' und 'Urheberrecht'-Abschnitte des fai_web-Templates,
    Letzterer verweist auf Abschnitt 3 (§ 5 UrhG amtliche Werke)
    für die in der App dargestellten Norm-Texte.

Recl∆Im ist damit aus Recht-Sicht so weit, wie fai_web es ist —
und gleichermaßen reif für eine öffentliche Sichtbarkeit, sobald
Methodik-Beirat + Volljuristen-Approval-Kette stehen.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 13:58:29 +02:00
214f909c1f feat(legal): add Recht page (disclaimer, AI Act, UrhG, GDPR, Impressum, OSS)
Recl∆Im now carries the six-section legal layer that any
public-facing duty-evaluation tool needs before either the
.app or the web build can go live to an audience beyond the
working group itself.

Sections, in the order a careful reader cares about them:

  1. Disclaimer — keine Rechtsberatung, RDG-konform geframed,
     T1–T4 Tier-Bedeutung explizit, Demo-Daten klar abgesetzt.
  2. EU AI Act Art. 50 — Transparenz-Notice, klare 'kein
     Hochrisiko-KI-System nach Anhang III'-Positionierung,
     Human-in-the-Loop via system.approval@^0, Modell-Digest
     im Audit-Log.
  3. Quellen + Urheberrecht — § 5 UrhG-Hinweis für die
     dargestellten amtlichen Werke, T1- und T2-Quellen-Liste,
     Apache 2.0 für eigenen Code und Methodik.
  4. Datenschutz — DSGVO Art. 13-Pflichtangaben, Hosting-
     Provider-Platzhalter, Server-Log-Rechtsgrundlage Art. 6
     Abs. 1 lit. f, Betroffenenrechte, Berliner
     Aufsichtsbehörde.
  5. Impressum — § 5 TMG + § 18 Abs. 2 MStV-Pflichtangaben
     als Vorlage, ODR-Plattform-Hinweis.
  6. Open Source — Apache 2.0, Repo-Link, Commit-Konvention.

Jede Section markiert deployment-spezifische Werte
(Anbieter-Adresse, Hosting-Provider, AVV-Stand,
Kontakt-E-Mail) als [PLACEHOLDER] und der Schlussabsatz weist
ausdrücklich darauf hin, dass eine Volljuristen-Prüfung vor
der ersten öffentlichen Veröffentlichung pflichtig ist.

Plumbing

  - ShellPage gets a fifth NavigationRail-Destination 'Recht'
    mit gavel-Icon.
  - LegalPage scrollt mit ReclaimCard-Sektionen und nutzt
    SelectableText überall, damit Reviewer-Zitate direkt
    rauskopierbar sind.
  - DemoBanner erweitert um 'KEINE Rechtsberatung'-Hinweis und
    verweist auf den Recht-Reiter — der politische Disclaimer-
    Pfad ist damit aus jeder Datenseite einen Klick entfernt.

flutter analyze: clean. flutter test: 2/2 grün. Web- und
macOS-Build laufen, Recht-Reiter rendert auf beiden Targets
identisch.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 13:52:45 +02:00
b06e686001 chore(web): enable Flutter web target
Adds the platform scaffolding flutter create produces for web
(web/index.html, manifest.json, favicon, icons). flutter build
web --release compiles the existing codebase to JS in ~16 s.
.metadata records the platform now lists web.

What works on web today:

  - Mock-mode UI (the same five fixture norms, sorted,
    inline-text-card, browser-open via launchUrl which becomes
    'open in new tab' in the browser).
  - All static screens (Heatmap, Normen, Methodik, Recht).

What does NOT yet work on web:

  - Hub-mode gRPC. chain_client_sdk_dart uses package:grpc,
    which in a browser needs the gRPC-Web wire format
    (text/grpc-web+proto over fetch). The Ch∆In Hub speaks
    gRPC-Web on the same port per docs/architecture/
    protocol-surfaces.md, but the client SDK needs a small
    web-channel branch to actually use it. Phase 1 task.

Why this matters: a single URL the AG can click is a different
political artefact than a downloaded .app. Web + macOS share
the same Recl∆Im codebase; nobody has to choose.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 13:52:19 +02:00
116310b65b feat(ui): inline the normative text + open-source URL
Adds the actual paragraphs of the law right next to the
evaluation panel on the norm-detail page. A reviewer no longer
has to leave the app to confirm what a score is about; the
canonical source URL stays one click away via 'Im Browser
öffnen' (url_launcher) and one click away as a clipboard copy.

Schema

  models.dart gains a NormParagraph (number + text). Norm carries
  a List<NormParagraph> paragraphs, defaulted to const [] so
  norms harvested by text.akoma_normalize@^0 in live mode slot
  in without breaking the Mock path.

Demo data

  fixtures.dart embeds the public source paragraphs verbatim
  under §5 UrhG (amtliche Werke) for the five pilot norms.

Plumbing

  NormTextCard (lib/widgets/norm_text_card.dart) is the renderer:
  per-paragraph hanging label, selectable body text, source URL
  in mono at the bottom, two action buttons. NormDetailPage
  drops the card between the metric row and the duties card so
  the eye lands on it during a 'why this score?' read-through.

macOS sandbox

  The Release entitlement file gains
  com.apple.security.network.client so url_launcher's NSWorkspace
  call and the HubRepository's gRPC channel can both reach the
  network. The DebugProfile already had server; client matches
  the production posture.

  GeneratedPluginRegistrant.swift was re-generated by
  flutter pub get after url_launcher was added — no hand edits.

flutter analyze: clean. flutter test: 2/2 passing. The macOS
build via build-macos.sh launches and the new card renders the
GewO § 14, KassenSichV, DSGVO Art. 30, BauO Bln § 60 ff and
MiLoG § 17 fixtures with their actual normative text.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 13:43:22 +02:00
e08cedcd3f feat(brand): rename product to Recl∆Im (Reclaim) across app, bundle, docs
Recl∆Im — the F∆I product line for Pflicht-basierte
Wirkungsanalyse und Reform-Vorschlags-Pipelines. Replaces the
working title 'Law-Heatmap', which described the demo view
rather than the platform. Heatmap, Norms list, Hub view,
Methodik are now four panes of the same Recl∆Im app.

Renames in this commit:

  Flutter package      lawheatmap_app   -> reclaim_app
  macOS bundle id      ai.flemming.lawheatmapApp -> ai.flemming.reclaim
  macOS PRODUCT_NAME   lawheatmap_app   -> reclaim_app
  Theme tokens         LawHeatmapColors/Space/Radius/Typography/Theme
                       -> Reclaim*
  Surface card         LawHeatmapCard   -> ReclaimCard
  Top-level widget     LawHeatmapApp    -> ReclaimApp
  Theme files          lawheatmap_theme.dart/lawheatmap_tokens.dart
                       -> reclaim_theme.dart/reclaim_tokens.dart
  Widget file          lawheatmap_card.dart -> reclaim_card.dart
  Build script         build-macos.sh paths and headline string
  Docs                 MACHBARKEITSSTUDIE.md, METHODIK.md, RUN.md,
                       app/README.md, flow YAML
  UI strings           'F∆I Law-Heatmap' / 'Law-Heatmap'
                       -> 'Recl∆Im' / 'Recl∆Im (F∆I)' for vendor-
                       prefixed contexts

flutter analyze: clean. flutter test: 2/2 passing.

The local working directory stays fai_lawheatmap/ for now —
the dir is referenced by the .claude project metadata; renaming
it would break session continuity for no real gain. The Forgejo
repo was renamed via API in the same change-set (fai/lawheatmap
-> fai/reclaim), with the legacy slug serving a redirect
courtesy of Forgejo's built-in rename handling.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 13:42:55 +02:00
90b7500aaf docs: drop 'vormals F∆I-Plattform' legacy clarification
The Ch∆In rename is months old and the AG audience never knew
the previous name. Calling out 'vormals …' in three places —
the study header, the glossary, and the About page — added
noise without informing anyone. Ch∆In stands on its own.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 13:18:30 +02:00
852f9da703 fix(ui): sort evaluations by SKM €/year descending
Repository.list() now hands the UI a list sorted by the harm
proxy (Studie §6 SKM-€/Jahr). Most-harmful first, deterministic
across launches and across mock-vs-hub repositories. Removes
the perceived non-determinism in the Norms list and gives the
heatmap chip-wrap a politically meaningful first row.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 13:18:30 +02:00
cf0ee4fd7a feat(hub): list installed capabilities from the connected hub
HubCapabilitiesCard wires HubClient.listCapabilities() into the
hub-status page below the connection form: once the app starts
in hub mode and the connection is healthy, the card shows
capability@version, the providing module, and its kind for
every entry the hub reports.

In mock mode the card prints an explanatory hint instead of
loading, so no confusing empty-state appears when the user is
just browsing the demo data.

A small refresh-icon next to the heading lets the user re-poll
without a full page reload — useful right after a chain install
in another terminal.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 11:47:28 +02:00
39733628b6 feat: wire chain_client_sdk gRPC client + HubRepository scaffold
Adds a real Ch∆In hub backend to the Flutter client as a
sibling-path dependency on chain_client_sdk_dart, alongside the
existing MockRepository (which stays the default until the
flow.completed-to-Evaluation pipeline lands in Phase 0 week 1).

Wiring

  HubSettings (lib/data/hub_endpoint.dart) — persists host /
  port / secure / token / useHub flags via SharedPreferences
  under the Studio-compatible hub.* keys. Distinct from the
  SDK's chain.HubEndpoint, which is a transport-level value
  type — HubSettings adapts in that direction.

  HubRepository (lib/data/hub_repository.dart) — implements
  EvaluationRepository on top of HubClient. connect() runs a
  4-second healthy() probe; on failure returns a repository
  with isHealthy=false rather than throwing, so the UI can
  surface "hub-down" without an exception-handling round-trip.
  list() / byEli() return empty data in Phase 0 because no
  hub-side flow yet produces Evaluation objects in the
  lawheatmap-specific shape.

  main.dart — loads HubSettings on startup; if useHub=false
  starts with MockRepository (fast offline default), otherwise
  probes the hub and falls back to MockRepository on probe
  failure. No mid-session swap to keep the Phase 0 wiring
  simple; a settings save instructs the user to restart.

  HubStatusPage — read-only summary card at the top, editable
  endpoint form, "Verbindung testen" probe button, "Speichern"
  persist button. Live-flow runbook below describes the
  chain serve / install / run / approve sequence so a user
  can wire the real flow themselves once Phase 1 lands.

flutter analyze: clean. flutter test: 2/2 passing.
Smoke-launch on macOS: app boots, Dart VM service comes up,
no runtime errors in mock mode.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 11:45:48 +02:00
8ee6a294a1 feat: initial F∆I Law-Heatmap Phase 0 scaffold
First end-to-end-buildable cut of the pflicht-/graph-basierte
Wirkungsanalyse von Rechtsnormen, running on Ch∆In. Demo runs
without external dependencies and without a hub connection so
the methodology + UI stack can be reviewed before any Verbands-
Kooperation or Beirats-Validierung kicks in.

Tree layout

  MACHBARKEITSSTUDIE.md       v0.3 — pflicht-basierte Methodik,
                              4-Tier-Evidenz, 8 law*-Module
                              auf Ch∆In statt der alten F∆I-
                              Plattform.
  RUN.md                      one-page how-to-run.
  flows/durchstich-gewo-14.yaml
                              vertical Phase-0 flow: pull →
                              normalize → duties → citations →
                              cohort → skm → frust → benefit →
                              attribution → system.approval.
  app/                        Flutter macOS client, design
                              language pulled from fai_web
                              (ink #08090A, paper #F4F3EF,
                              petrol signal #2E8F9E). Mock
                              repository ships five fixture
                              norms with real source URLs and a
                              permanent T4 demo-banner so no
                              figure can be mistaken for a
                              validated one.

Module repos (separate, see chain-modules*/):

  text-akoma-normalize, text-deontic-extract, graph-citation-
  extract, text-readability-score, stats-cohort-size, graph-
  shapley-attribution, econ-skm-score, law-benefit-score.

What runs today

  - flutter analyze: 0 issues
  - flutter test: 2/2 (landing + nav-to-shell smoke)
  - flutter build macos (via app/build-macos.sh) and ad-hoc
    codesign, app launches and the Dart VM service comes up
  - native cargo test green on every module
  - cargo build --release --target wasm32-wasip2 produces a
    130 KiB artefact for text-akoma-normalize

What is deliberately mock / stub

  - gRPC wire from the client to a chain serve hub (Repository
    abstraction is in place; live impl is the next step)
  - NKR Bürokratiekosten-Datenbank ingestion (for the canonical
    h-Werte that close out the Engpass per Studie §6.7)
  - DESTATIS GENESIS-API adapter for stats.cohort_size

License: Apache-2.0. Author/contact in MACHBARKEITSSTUDIE.md.
Signed-off-by: flemming-it <sf@flemming.it>
2026-06-18 11:26:28 +02:00