herbatrium/CLAUDE.md
Till JS 214b852f99
Some checks are pending
CI / validate (push) Waiting to run
η-0: Repo-Skelett
Citizen-Science-App für botanische Beobachtungen — föderierte App
des mana e.V.-Ökosystems. Erste Vereins-App mit 3-Schicht-Datenmodell
(Species/Specimen/Observation), 3-Stufen-GPS (exact/rounded_100m/
region_only) und fail-closed Sensitivity-Schutz für FFH-Anhang-IV-
Arten.

Stand η-0 (Plan v0.2 + Detail-Pass, Architect+Compliance-Audit 2026-05-17):
- Workspace: pnpm/turbo/tsconfig/prettier
- apps/api: Hono+Bun mit /healthz, /readyz, /healthz/details,
  /.well-known/mana-app.json. Smoke alle vier Endpoints grün.
- apps/web: SvelteKit 2 + Svelte 5 + Tailwind v4, Herbarium-Theme,
  Landing-Page mit Phasen-Plan. svelte-check 0 errors / 0 warnings.
- docker-compose: postgis/postgis:16-3.4 auf 5449, init-postgis.sql
- app-manifest.json: validiert gegen @mana/shared-share-protocol@0.4.0
  (2 shares, 2 accepts, 3 tools)
- Doku: CLAUDE.md, README.md, STATUS.md

Cross-Repo: @mana/shared-share-protocol@0.4.0 published mit 4 neuen
Share-Types (mana/photo, mana/geo-point, mana/plant-observation,
mana/plant-specimen). Plan: mana/docs/playbooks/HERBATRIUM_GREENFIELD.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 18:10:20 +02:00

198 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CLAUDE.md — Herbatrium
Guidance für Claude Code in diesem Repo.
> **Wenn du gerade neu bist:** lies zuerst [`STATUS.md`](STATUS.md) — dort
> steht der aktuelle Phasen-Stand. Dieses CLAUDE.md ist nur die
> Konventions- und Architektur-Referenz. Der vollständige Plan lebt in
> [`../mana/docs/playbooks/HERBATRIUM_GREENFIELD.md`](../mana/docs/playbooks/HERBATRIUM_GREENFIELD.md).
## Was dieses Repo ist
**Herbatrium** — Citizen-Science-App für botanische Beobachtungen in
freier Natur. Föderierte App des **mana e.V.**-Ökosystems. Erste
Vereins-App mit **3-Schicht-Datenmodell** (Species/Specimen/Observation),
**3-Stufen-GPS-Sichtbarkeit** (`exact`/`rounded_100m`/`region_only`)
und **fail-closed Sensitivity-Schutz** für FFH-Anhang-IV-Arten.
```
HTTPS ┌──────────────────┐
mana e.V. ◄─────────────── │ herbatrium/ │ Postgres + PostGIS
Plattform │ (this repo) │ herbatrium.mana.how
• mana-auth (JWKS) │ SvelteKit-Web │
• mana-llm (primär ID) │ + Hono-API │
• mana-media (Photos) └──────────────────┘
• mana-geocoding (Reverse) ▲
• mana-share/links/events │ HTTPS
• mana-mcp (Tools) │
┌─────────┴────────┐
│ Pl@ntNet API │
│ (AGPV / EU) │
│ Sekundär-Verifier│
└──────────────────┘
┌──────────────────┐
│ GBIF / IUCN / │
│ FFH-Listen │
│ (Sensitivity) │
└──────────────────┘
```
## Status
**Phase η-0 — Repo-Skelett (2026-05-17).** Manifest + API/Web-
Boilerplate stehen, DB-Schema und Klassifikations-Pipeline noch nicht
implementiert. Roadmap siehe [`STATUS.md`](STATUS.md). Vollständiger
Plan in
[`../mana/docs/playbooks/HERBATRIUM_GREENFIELD.md`](../mana/docs/playbooks/HERBATRIUM_GREENFIELD.md).
## Architektonische Invarianten
Beschlossen. Nicht ohne explizite Diskussion antasten.
1. **Fail-closed Sensitivity.** Jede Species startet
`is_sensitive=true` bis ein erfolgreicher GBIF-IUCN/FFH-Lookup das
Gegenteil belegt. Sensitive Specimens werden niemals mit
100 m-GPS public — nur als Region (admin_level=4, ~3050 km).
Verhindert BNatSchG-§44-Risiko.
2. **Pl@ntNet-Outbound nur `display`-Layer.** Niemals `original/` mit
GPS-EXIF an Drittdienste. Festgenagelt durch Compliance-Audit
2026-05-17.
3. **mana-llm primär, Pl@ntNet sekundär (Verifier).** Reihenfolge-
Entscheidung 2026-05-17: LLM-Vision liefert Top-3 + Phenology in
einem Pass; Pl@ntNet wird zugeschaltet bei niedrigem Confidence
oder Sensitivity-Verdacht. Spart Quota.
4. **3-Layer-Foto-Storage (original/display/thumb)**. Original
verlustfrei mit EXIF, Display EXIF-stripped 2048px, Thumb 512px.
Jeder Original-Access loggt in `photo_original_access_log`.
5. **Lizenz-Picker beim Publish, kein stiller Default.** Default
`license=null`, beim Publish-Step explizit CC-BY-SA / CC-BY-NC /
ARR wählen. Daten-Souveränität-Prinzip.
6. **Eigene Postgres-DB `mana_herbatrium` mit PostGIS** (Image
`postgis/postgis:16-3.4`, Port 5449). Schema-Isolation via
`pgSchema('herbatrium')`.
7. **Server-authoritative MVP.** Keine Dexie. Frontend = HTTP-Client.
Local-First nachrüstbar via mana-sync in Phase η-9.
8. **Web ist SvelteKit 2 + Svelte 5 (runes)**, API ist Hono+Bun+
Drizzle. Plattform-Standard.
9. **Tier-Gate:** `public` lesen, `beta` schreiben. Beta+alpha+founder
dürfen Observations anlegen.
## Stack
| Layer | Wahl |
|---|---|
| Web | SvelteKit 2 + Svelte 5 (runes) + Tailwind v4 |
| API | Hono + Bun |
| Datenbank | PostgreSQL + PostGIS + Drizzle ORM |
| Auth | JWKS aus mana-auth, ab η-1 |
| Föderation | `@mana/shared-app-tpl` ab η-5 |
| Klassifikation | mana-llm (primär) + Pl@ntNet (Verifier) |
| Geo | `mana-geocoding`, PostGIS `ST_DWithin` |
| Storage | `mana-media` (3-Layer: original/display/thumb) |
| Map | MapLibre + Cloudflare-Pull-through-OSM |
Ports: API `3101`, Web `3102`, Postgres `5449` (siehe
[`../mana/docs/PORTS.md`](../mana/docs/PORTS.md)).
## Repo-Struktur
```
herbatrium/
├── apps/
│ ├── api/ Hono+Bun-Backend (Port 3101)
│ │ └── src/
│ │ ├── index.ts App-Boot
│ │ ├── routes/
│ │ │ ├── health.ts /healthz, /readyz, /healthz/details
│ │ │ └── manifest.ts /.well-known/mana-app.json
│ │ ├── db/schema/ Drizzle, pgSchema('herbatrium'), folgt η-1
│ │ ├── lib/ plantnet, gbif, photo-pipeline, …
│ │ ├── share-handlers/ save_photo_as_observation, …
│ │ └── middleware/ JWT, service-key
│ └── web/ SvelteKit-Frontend (Port 3102)
│ └── src/
│ ├── app.html, app.css (Herbarium-Theme)
│ ├── routes/
│ │ ├── +layout.svelte
│ │ └── +page.svelte Landing-Stub mit Phasen-Anzeige
│ └── lib/ api-clients, stores, components
├── packages/ leer, app-eigene Pakete später
├── infrastructure/
│ └── init-postgis.sql CREATE EXTENSION postgis (Container-Init)
├── docs/ repo-eigene Doks (PHOTO_PIPELINE, GPS_PRIVACY, …)
├── app-manifest.json Föderations-Manifest
├── docker-compose.yml lokales PostGIS :5449
├── STATUS.md aktueller Phasen-Stand
└── .github/workflows/ ci.yml
```
## Föderations-Vertrag
Pflicht-Endpoints (alle ab η-5 via `@mana/shared-app-tpl`):
| Method | Path | Phase |
|---|---|---|
| `GET` | `/healthz`, `/readyz`, `/healthz/details` | η-0 (jetzt) |
| `GET` | `/.well-known/mana-app.json` | η-0 (jetzt) |
| `POST` | `/api/v1/photos` | η-1 |
| `POST` | `/api/v1/observations` | η-1 |
| `POST` | `/api/v1/identify` | η-2 |
| `GET` | `/api/v1/specimens?near=…` | η-3 |
| `GET` | `/api/v1/public/feed` | η-4 |
| `POST` | `/api/v1/share/receive` | η-5 |
| `POST` | `/api/v1/tools/:name` | η-5 |
| `GET` | `/api/v1/dsgvo/export` | η-5 |
| `POST` | `/api/v1/takedown` | η-7 |
## Konventionen
- **pnpm 9.15.x**, Node 20+, Turborepo 2.x, Bun für API-Runtime
- **Tabs** für Indent, single quotes, 100-col Prettier
- **Drizzle 0.38 / drizzle-kit 0.30** (Plattform-Einklang)
- **JWT-Validation lokal** über JWKS-Cache (5 min), kein Live-Call
- **Service-to-Service** via `X-Service-Key` (`MANA_SERVICE_KEY`)
- **Tests**: Vitest
- **PostGIS-Geometry-Felder**: Drizzle `customType<{ data: string }>`
für `geometry(Point, 4326)`-Spalten
## Sensitivity-Disziplin (Pflicht ab η-1)
Jeder species-Insert muss durch `lib/gbif.ts` (folgt η-2) gehen:
- GBIF-Match auf scientific_name → IUCN-Category (`CR`/`EN`/`VU`/...)
- FFH-Anhang-IV-Liste querchecken (statische Liste, `lib/ffh-list.ts`)
- Setzt `is_sensitive`, `iucn_category`, `ffh_annex_iv`,
`sensitivity_source`, `sensitivity_resolved_at`
- Bei Fehler oder Timeout: `is_sensitive=true` bleibt stehen (fail-closed)
Sensitive Specimens dürfen NIE mit GPS-Marker public sein — nur als
Region-Cluster. Compliance-Audit hängt von dieser Disziplin ab.
## Lokal entwickeln
```bash
pnpm install # @mana/* aus pkg.mana.how
docker compose up -d # PostGIS :5449
pnpm --filter ./apps/api dev # API :3101
pnpm --filter ./apps/web dev # Web :3102
```
Voraussetzungen:
- mana-Plattform-Stack lokal laufend (mindestens `mana-auth` :3001;
`mana-llm`, `mana-media`, `mana-geocoding` für Klassifikation +
Foto-Pipeline + Reverse-Geocode)
- Verdaccio-Token in `~/.npmrc`
(`npm login --registry=https://pkg.mana.how`)
- Pl@ntNet-API-Key in `.env.local` (Sekundär-Verifier; ohne läuft die
App, nur LLM-Klassifikation; siehe Playbook „Operative Helfer")
## Wichtige Cross-Repo-Doks
- [`../mana/docs/playbooks/HERBATRIUM_GREENFIELD.md`](../mana/docs/playbooks/HERBATRIUM_GREENFIELD.md) — vollständiger Plan v0.2
- [`../mana/docs/FEDERATION.md`](../mana/docs/FEDERATION.md) — Architektur-Grundlage
- [`../mana/docs/PORTS.md`](../mana/docs/PORTS.md) — Port-Allokation
- [`../mana/docs/COMPLIANCE.md`](../mana/docs/COMPLIANCE.md) — DSGVO, BNatSchG, DSA
- [`../mana/docs/AGGREGATOR_POLICY.md`](../mana/docs/AGGREGATOR_POLICY.md) — Drittdienst-Disziplin (Pl@ntNet-Geist)
- [`../mana/services/mana-llm/CLAUDE.md`](../mana/services/mana-llm/CLAUDE.md) — primärer Klassifikator
- [`../mana/services/mana-media/CLAUDE.md`](../mana/services/mana-media/CLAUDE.md) — Foto-Storage
- [`../mana/services/mana-geocoding/CLAUDE.md`](../mana/services/mana-geocoding/CLAUDE.md) — Reverse-Geocode