chain's catalog charset (naming.rs check_segment, capability-namespaces.yaml)
allows only [a-z0-9-] per dot-segment — underscores are rejected by
`chain validate`. Repack all 8 modules with the dash capability form
(econ.skm-score, law.benefit-score, …) and align the store index, flows,
app strings and docs to match. Bundles now use the .chain extension
(content-addressed; legacy .fai would still install).
- store/store.yaml: name: dash, wasm_url .chain, refreshed sha256
- store/SHA256.txt: 8 new .chain hashes
- flows + app/lib + RUN.md + MACHBARKEITSSTUDIE.md: capability refs _ -> -
- left untouched: text_akoma_normalize.wasm artifact, DB field skm_score,
backlog module law.skm_score
Signed-off-by: flemming-it <sf@flemming.it>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>