Some checks are pending
CI / validate (push) Waiting to run
Drei Skills + Importer + Migration der 5 existierenden Venues.
Skills (alle repo-lokal in .claude/skills/):
- seepuls-curate-venue: 5-Stufen-Pipeline (Plan → Recherche → Design
→ Validate → Publish), Reviewer-Stops Pflicht, Nominatim-Geocoding
mit Politeness, YAML als Output. Bulk-Mode für >10 Venues.
- seepuls-validate-venue: read-only, 8 Checks (slug-unique,
country-enum, region-enum, coords-plausibel, description-länge,
robots-allow, source-coverage, enum-fit).
- seepuls-curate-events: zwei Modi (Crawl via admin-API mit
Service-Key, oder Manual aus User-Input). DB-as-SOT (nicht YAML),
weil Events kurzlebig sind.
Importer:
- apps/api/src/jobs/import-venues.ts: liest curated/venues/*.yml,
upsert in seepuls.venues, idempotent (zweiter Run = 0 inserted,
N updated). Zod-Validation der YAML-Form.
- pnpm db:import-venues script.
- Pattern an zitare-Importer orientiert.
SOT-Migration der 5 bestehenden Venues:
- apps/api/src/data/curated/venues/{museum-rosenegg,
seemuseum-kreuzlingen, bodensee-planetarium-kreuzlingen,
rosgartenmuseum, archaeologisches-landesmuseum-konstanz}.yml
- README.md mit Format-Doku.
Doku:
- seepuls/docs/CURATOR.md: Workflow-Übersicht, verweist auf
mana/docs/CONTENT_PIPELINES.md als Cross-Repo-Pattern.
Lokal validiert (gegen Postgres :5441):
- 1. Lauf: 5 inserted ✓
- 2. Lauf: 0 inserted, 5 updated ✓ (idempotent)
- type-check + 43/43 Tests grün.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
257 lines
10 KiB
Markdown
257 lines
10 KiB
Markdown
---
|
|
name: seepuls-curate-venue
|
|
description: Legt eine Venue im Seepuls-Korpus an oder reichert sie an — Recherche → strukturierte Felder → Geocoding → Validate → YAML in apps/api/src/data/curated/venues/<slug>.yml → Import. Mit Reviewer-Stops, Quellen-Permalinks, Compliance-Veto.
|
|
argument-hint: '<venue-name-oder-url> [--slug <existing-slug>] [--country DE|CH|AT|LI] [--region <slug>]'
|
|
allowed-tools:
|
|
- Read
|
|
- Write
|
|
- Edit
|
|
- WebSearch
|
|
- WebFetch
|
|
- Bash(ls *)
|
|
- Bash(mkdir *)
|
|
- Bash(grep *)
|
|
- Bash(cat *)
|
|
- Bash(curl *)
|
|
- Bash(jq *)
|
|
- Bash(docker exec *)
|
|
- Bash(pnpm *)
|
|
- AskUserQuestion
|
|
---
|
|
|
|
# `/seepuls-curate-venue` — Curator-Workflow für eine Venue
|
|
|
|
Du legst eine Venue (Veranstaltungsort) im Seepuls-Korpus an oder
|
|
reicherst sie an. **YAML in `apps/api/src/data/curated/venues/<slug>.yml`
|
|
ist die SOT** — DB-Eintrag entsteht durch `pnpm db:import-venues`.
|
|
|
|
Schwester-Skill von [`/seepuls-validate-venue`](./seepuls-validate-venue.md)
|
|
und [`/seepuls-curate-events`](./seepuls-curate-events.md). Cross-Repo-
|
|
Best-Practices: [`../mana/docs/CONTENT_PIPELINES.md`](../../mana/docs/CONTENT_PIPELINES.md).
|
|
|
|
## Wann benutzen
|
|
|
|
- User will eine **neue Venue** anlegen (Museum, Club, Kulturhaus,
|
|
Bar, Verein, Festival-Spielstätte) für die Bodensee-Region (DE+CH+AT+LI).
|
|
- User will eine **bestehende Venue** korrigieren (Adresse, Beschreibung,
|
|
Logo, Social).
|
|
- User hat eine **konkrete URL** und will daraus einen Eintrag.
|
|
|
|
Für die **automatische Crawl-Pipeline** (mana-research → mana-llm →
|
|
Reviewer-Stop) ist `POST /api/v1/admin/venues/from-url` der Pfad — er
|
|
schreibt direkt in DB mit `crawl_status='pending-review'`. Dieser Skill
|
|
hier ist der **manuelle Pfad** mit YAML-as-SOT, idempotent re-importierbar.
|
|
|
|
## Pflicht-Lektüre vor erstem Run
|
|
|
|
- `seepuls/STATUS.md` — Phasenstand der App.
|
|
- `seepuls/apps/api/src/data/curated/venues/README.md` — YAML-Format.
|
|
- `seepuls/docs/CURATOR.md` — Workflow-Übersicht.
|
|
- `mana/docs/AGGREGATOR_POLICY.md` — Hard-Rules (robots.txt, Attribution,
|
|
≤ 200 Zeichen Description).
|
|
- `mana/docs/CONTENT_PIPELINES.md` — generelles Pipeline-Pattern.
|
|
|
|
## Inputs
|
|
|
|
1. **Venue-Name oder Website-URL** (Pflicht) — z.B. `"Museum Rosenegg
|
|
Kreuzlingen"` oder `"https://museumrosenegg.ch"`.
|
|
2. **`--slug <existing>`** (optional) — bestehenden Eintrag anreichern.
|
|
3. **`--country DE|CH|AT|LI`** (optional) — wenn aus dem Namen nicht
|
|
eindeutig. AskUserQuestion bei mehrdeutigen Treffern (z.B. „Stadthaus" —
|
|
welches?).
|
|
4. **`--region <slug>`** (optional, default `bodensee`) — eine der
|
|
curated Regions (`bodensee`/`hegau`/`thurgau-west`/`oberschwaben`).
|
|
5. **`--no-import`** (optional) — Stage-5 überspringen, nur YAML schreiben
|
|
(für dry-run / Review).
|
|
|
|
## Workspace
|
|
|
|
```
|
|
~/Documents/seepuls-drafts/<slug>/
|
|
├─ plan.md Stufe-1-Output: Name, Country/Region, Source-Kandidaten
|
|
├─ research/
|
|
│ ├─ sources.md Quellen mit Permalinks + Zugriffsdatum
|
|
│ └─ notes.md Roh-Notizen aus WebFetch
|
|
├─ design/
|
|
│ └─ venue.yml Stufe-3-Vorschlag (gleiche Form wie curated/venues/*.yml)
|
|
├─ validate/
|
|
│ └─ report.md Output von /seepuls-validate-venue
|
|
└─ publish/
|
|
└─ import.log Output von `pnpm db:import-venues`
|
|
```
|
|
|
|
`<slug>` = kebab-case aus Name. Drafts bleiben liegen als Audit-Trail.
|
|
|
|
## Pipeline (5 Stufen)
|
|
|
|
### Stufe 1 — Plan
|
|
|
|
1. Workspace anlegen:
|
|
`mkdir -p ~/Documents/seepuls-drafts/<slug>/{research,design,validate,publish}`.
|
|
2. **Slug bestimmen**: kebab-case aus Name, ohne Stop-Wörter
|
|
(„Museum X" → `museum-x`). Bei Konflikt mit existing: `<slug>-2` oder
|
|
ortsspezifischer (`museum-x-konstanz`).
|
|
3. **Existenz-Check**:
|
|
```bash
|
|
ls seepuls/apps/api/src/data/curated/venues/<slug>.yml 2>/dev/null
|
|
docker exec seepuls-postgres psql -U seepuls -d seepuls -tAc \
|
|
"SELECT slug FROM seepuls.venues WHERE slug='<slug>'"
|
|
```
|
|
Wenn YAML existiert: User fragen ob ergänzen oder neuer Slug.
|
|
4. `plan.md` mit:
|
|
- Venue-Name (1 Zeile)
|
|
- Country + Region (Begründung warum diese Region)
|
|
- Source-URL-Kandidaten (offizielle Website, ggf. Wikipedia)
|
|
- Streitfälle (z.B. „mehrere gleichnamige Orte" — welcher?)
|
|
5. **Reviewer-Stop**: User sieht Plan, sagt go oder korrigiert Slug/Region.
|
|
|
|
### Stufe 2 — Recherche
|
|
|
|
1. **WebFetch der offiziellen Website** — Name, Adresse, Beschreibung,
|
|
Logo-URL, Social-URLs, Öffnungszeiten-Hinweis.
|
|
2. **Wikipedia-Cross-Check** (optional) — bei historischen oder
|
|
prominenten Orten. Permalink mit `oldid`.
|
|
3. **Adresse präzise erfassen** — Straße + Hausnummer + PLZ + Ort.
|
|
4. `research/sources.md`: nummerierte Quelle, jede mit:
|
|
- Titel + URL
|
|
- Zugriffsdatum (ISO `YYYY-MM-DD`)
|
|
- Permalink wenn möglich (Wikipedia `oldid`)
|
|
5. `research/notes.md`: Roh-Texte, Adress-Varianten, evtl. abweichende
|
|
Schreibweisen. Streitfälle (z.B. „Website sagt Strasse, Wikipedia
|
|
sagt Straße") markieren.
|
|
|
|
### Stufe 3 — Design + Geocoding
|
|
|
|
1. **Nominatim Forward-Geocode** für die Adresse:
|
|
```bash
|
|
UA='seepuls/0.0.1 (+https://seepuls.mana.how; kontakt@mana.how)'
|
|
curl -sf -H "User-Agent: $UA" --max-time 10 \
|
|
--data-urlencode 'q=Straße Nr, PLZ Ort' \
|
|
--data 'format=json&limit=2&countrycodes=de,ch,at,li' \
|
|
-G 'https://nominatim.openstreetmap.org/search' \
|
|
| jq -r '.[] | "\(.lat),\(.lon) \(.display_name)"'
|
|
```
|
|
**Politeness Pflicht**: User-Agent mit Kontakt-Mail, ≥ 1.1 s zwischen
|
|
Requests. Bei mehreren Treffern: AskUserQuestion mit beiden Optionen.
|
|
2. **Plausibilität prüfen**: lat ∈ [47.4, 47.9], lng ∈ [8.5, 9.7]
|
|
(Bodensee-Bounding-Box). Außerhalb → Plan-Stop, möglich falsche
|
|
Region.
|
|
3. `design/venue.yml` schreiben in der Form aus
|
|
`curated/venues/README.md`. Felder:
|
|
- `slug` (aus Plan)
|
|
- `name` (offizielle Schreibweise)
|
|
- `countryCode` (ISO DE/CH/AT/LI)
|
|
- `regionSlug` (curated; default `bodensee`)
|
|
- `city`, `address`
|
|
- `coordinates: {lat, lng}` aus Nominatim
|
|
- `description` (**≤ 200 Zeichen**, in Originalsprache,
|
|
**kein LLM-Re-Phrasing** — direkte Übernahme oder leichte Kürzung)
|
|
- `websiteUrl`, `logoUrl` (oder null), `socialUrls` (Map)
|
|
- `source`: url + domain + accessedOn + refs
|
|
- `crawlStatus: manual-entry`
|
|
- `attributionRequired: true`
|
|
4. **Reviewer-Stop**: User sieht die YAML, sagt go oder korrigiert.
|
|
|
|
### Stufe 4 — Validate
|
|
|
|
Skill `/seepuls-validate-venue <slug>` aufrufen. Read-only, schreibt
|
|
`validate/report.md` mit 7 Checks (siehe Schwester-Skill).
|
|
|
|
Bei roten Items: **nicht eigenmächtig fixen** — User entscheidet welche
|
|
Stufe nochmal durch.
|
|
|
|
### Stufe 5 — Publish (YAML in SOT + Import)
|
|
|
|
1. **YAML kopieren** nach `seepuls/apps/api/src/data/curated/venues/<slug>.yml`.
|
|
2. **Compliance-Veto** via Sub-Agent:
|
|
```
|
|
Agent(subagent_type="mana-compliance", prompt="<diff der venue.yml>")
|
|
```
|
|
- ✅ → weiter.
|
|
- ⚠️ → User informieren.
|
|
- 🛑 → **abbrechen**. YAML aus curated/ entfernen.
|
|
3. **Import**:
|
|
```bash
|
|
cd seepuls && pnpm --filter ./apps/api db:import-venues
|
|
```
|
|
Output in `publish/import.log`. Idempotent (zweiter Run = 0 inserted,
|
|
1 updated).
|
|
4. **Cloudflare-Tunnel-Pause** unnötig — die public Read-API zeigt die
|
|
Venue sofort (keine Build-Cache-Invalidation nötig, Astro-Web ist SSR).
|
|
5. **Smoke**:
|
|
```bash
|
|
curl --resolve seepuls-api.mana.how:443:188.114.96.12 -s \
|
|
'https://seepuls-api.mana.how/api/v1/venues/<slug>' | jq
|
|
```
|
|
|
|
## Anti-Halluzinations-Regeln
|
|
|
|
- **Niemals Adresse oder Koordinaten raten.** Wenn Nominatim nichts
|
|
findet: stoppen, User fragen, Adresse manuell verifizieren.
|
|
- **Niemals Description erfinden oder LLM-paraphrasieren.** Direktes
|
|
Zitat aus offizieller Quelle, ≤ 200 Zeichen. Wenn länger: kürzen
|
|
am Satz-Ende, nicht umformulieren.
|
|
- **Niemals geratene Social-URLs.** Wenn die Website keinen Insta-Link
|
|
hat: `socialUrls: {}`, nicht raten.
|
|
- **Niemals `logoUrl` mit Wikipedia-Logo befüllen** — nur das offizielle
|
|
Logo von der Venue-Website (Hot-Link gemäß AGGREGATOR_POLICY §3),
|
|
oder `null`.
|
|
|
|
## Bulk-Mode (>10 Venues auf einmal)
|
|
|
|
Eine Curated-Liste (z.B. „alle Konstanzer Museen", „alle Konzert-Lokale
|
|
Kreuzlingen") als Master-Quelle. Dann:
|
|
|
|
1. **Curated-Listings-Site als Quelle** (z.B. konstanz.de/tourismus/museen,
|
|
thurgau-bodensee.ch/freizeit/museen).
|
|
2. **Sampling-Reviewer-Stop**: 5 zufällige Venues + Streitfall-Liste.
|
|
3. **Skript-getrieben**: Python-Heredoc liest CSV (slug, name, address,
|
|
website) und produziert N YAMLs. Geocoding-Loop mit Politeness-Delay.
|
|
4. **Atomic-Import**: `pnpm db:import-venues` macht alle in einer
|
|
Transaction.
|
|
|
|
## Reviewer-Stops sind Pflicht
|
|
|
|
Nach Stufe 1 (Plan) und Stufe 3 (Design + Geocoding), **vor** Stufe 5
|
|
(Publish). Auch wenn der User „mach einfach" gesagt hat. Stops sind kurz
|
|
(Markdown-Preview + ja/nein), retten den Korpus vor falschen Adressen.
|
|
|
|
## Was dieser Skill NICHT macht
|
|
|
|
- **Events anlegen** — `/seepuls-curate-events` ist Schwester-Skill.
|
|
- **Crawl-Pipeline triggern** — der admin-Endpoint
|
|
`/api/v1/admin/venues/from-url` ist der automatische Pfad
|
|
(Service-Key Pflicht).
|
|
- **Live-DB direkt schreiben** — YAML ist SOT, Import macht den Rest.
|
|
- **DSGVO-Sicht** — Venue-Daten sind public-aggregierte Infos, keine
|
|
PII. Personen-Daten (Veranstalter:innen-Mails etc.) gehören nicht
|
|
ins YAML.
|
|
|
|
## Dependencies
|
|
|
|
- `pnpm db:import-venues` — `apps/api/src/jobs/import-venues.ts`
|
|
- `docker exec seepuls-postgres psql` — Slug-Existenz-Checks
|
|
- Nominatim-API (OSM, gratis, ≥ 1 req/s Politeness)
|
|
- Sub-Agent `mana-compliance` für Veto
|
|
|
|
## Offene Punkte
|
|
|
|
- **Logo-Hot-Link-Verifikation.** Manche Websites haben
|
|
Hot-Link-Schutz; Logo lädt im Browser, aber nicht im img-Tag eines
|
|
anderen Hosts. Beim Smoke-Check (Stufe 5d) im Browser prüfen oder
|
|
fallback `logoUrl: null`.
|
|
- **Slug-Konsistenz mit ECOSYSTEM-City-Tags.** Wenn eine Stadt mehrere
|
|
Venues hat (Konstanz: Rosgarten + ALM): keine Konvention für
|
|
`<venue>-<stadt>` vs nur `<venue>`. Aktuell: Eindeutigkeit
|
|
reicht.
|
|
- **Wikidata-Anbindung.** Zitare nutzt Wikidata für Author-Enrich; bei
|
|
Venues gibt es Wikidata-Properties für viele Museen. Optional zur
|
|
V2 dazu.
|
|
|
|
## Referenzen
|
|
|
|
- Format: `apps/api/src/data/curated/venues/README.md`
|
|
- Workflow-Übersicht: `seepuls/docs/CURATOR.md`
|
|
- Schwester-Skill: `/seepuls-validate-venue`, `/seepuls-curate-events`
|
|
- Cross-Repo-Best-Practices: `../mana/docs/CONTENT_PIPELINES.md`
|
|
- Aggregator-Policy: `../mana/docs/AGGREGATOR_POLICY.md`
|