seepuls/.claude/skills/seepuls-validate-venue.md
Till JS 6125df9bdf
Some checks are pending
CI / validate (push) Waiting to run
α-6: Curator-Pipeline für Venues (YAML-as-SOT)
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>
2026-05-15 17:07:17 +02:00

5.2 KiB

name description argument-hint allowed-tools
seepuls-validate-venue Validiert ein seepuls-curate-venue-Draft (slug-unique, country-enum, region-enum, coords-plausibel, description-länge, robots-allow, source-coverage) bevor er ins curated/venues/ landet. Read-only, produziert validate/report.md. <venue-slug>
Read
Write
Edit
Bash(ls *)
Bash(grep *)
Bash(cat *)
Bash(curl *)
Bash(jq *)
Bash(docker exec *)

/seepuls-validate-venue — QA für ein Draft-Venue

Vorletzte Stufe der /seepuls-curate-venue-Pipeline. Liest ~/Documents/seepuls-drafts/<slug>/design/venue.yml und produziert <slug>/validate/report.md mit allen Findings.

Schwester-Skill von /cards-validate-deck (Cardecky-Spaced-Repetition) und der inline-Validate-Stage in /zitare-curate-quote.

Wann benutzen

  • Direkt nach Stage 3 von /seepuls-curate-venue (venue.yml steht).
  • Vor dem Publish (Stage 5). Bei rotem Report: nicht eigenmächtig fixen, sondern dem User die Findings melden + welche Stufe nochmal.

Inputs

  1. Draft-Slug (Pflicht) — z.B. museum-rosenegg.

Checks

1. slug-unique

Slug ist neu im Korpus:

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>'"

Treffer → rot. Vorschlag: <slug>-<stadt> oder <slug>-2.

Exception: Wenn der User explizit ergänzen will (--slug <existing> in curate-venue), nur Warning, kein rot.

2. country-enum

countryCode ∈ {DE, CH, AT, LI}. Treffer-Miss → rot.

3. region-enum

regionSlug ∈ {bodensee, hegau, thurgau-west, oberschwaben} ODER fehlt komplett (null). Andere Werte → rot (Region erst seed-en).

4. coords-plausibel

coordinates.lat ∈ [47.4, 47.9] UND coordinates.lng ∈ [8.5, 9.7] (Bodensee-Bounding-Box). Außerhalb → rot, vermutlich falscher Geocode oder falsche Region.

Plus: Cross-Check countryCode:

  • DE-Venue mit lat < 47.55 (Schweizer Seeufer) → Warning.
  • CH-Venue mit lat > 47.66 (deutsches Ufer) → Warning.

5. description-länge

description.length ∈ [10, 220]:

  • < 10 → rot, zu kurz für sinnvolle Anzeige.
  • 220 → rot, AGGREGATOR_POLICY §2 verlangt ≤ 200 (mit kleinem Puffer auf 220 für UTF-8-Zähl-Diff).

6. robots-allow

source.url-Domain erlaubt das Crawlen seepuls-Bot:

curl -sf -H "User-Agent: seepuls/0.0.1 (+https://seepuls.mana.how; kontakt@mana.how)" \
  --max-time 5 "https://<domain>/robots.txt" | head -40

Manuelles Checken: gibt es Disallow: / für * oder für seepuls? → rot, Eintrag nicht anlegen. Domain in blocked_domains tragen.

Wenn robots.txt 404 oder timeout: warnung (default-allow), aber flaggen.

7. source-coverage

source.refs[] enthält ≥ 1 Eintrag, jeder ≥ 3 Zeichen. Leerer Array → rot, Curator hat keine Quelle dokumentiert.

source.url und source.domain müssen übereinstimmen:

echo '<source.url>' | sed -E 's|^https?://([^/]+).*|\1|' | tr 'A-Z' 'a-z'

Sollte gleich source.domain sein (modulo www-Stripping). Sonst rot.

8. enum-fit (crawlStatus)

crawlStatus ∈ {manual-entry, pending-review, claimed}. Andere Werte → rot, sonst greift der Importer (oder spätere Constraints).

Output

~/Documents/seepuls-drafts/<slug>/validate/report.md:

# Validate-Report — <slug>

Draft-Path: <…>
Geprüft: 2026-MM-DD HH:MM

## Findings

✓ slug-unique:         neu (kein YAML, keine DB-Row)
✓ country-enum:        CH
✓ region-enum:         bodensee
✓ coords-plausibel:    47.6442, 9.1720 — im Bodensee-BBOX
✓ description-länge:   137 Zeichen
✓ robots-allow:        robots.txt erlaubt /
✓ source-coverage:     2 refs
✓ enum-fit:            crawlStatus=manual-entry ok

## Empfehlung

Grün — bereit für Publish (Stage 5).

Bei roten Findings:

✘ coords-plausibel:    47.123, 9.456 — Lat zu südlich, außerhalb Bodensee-BBOX
✘ description-länge:   245 Zeichen (Limit 220)

## Empfehlung

Stop vor Publish. Nochmal Stufe 3:
- Adresse re-geocoden — vielleicht falsche Stadt erwischt.
- Description am Satz-Ende kürzen, nicht umformulieren.

Was dieser Skill NICHT macht

  • Eigenmächtig fixen. Findings → User entscheidet.
  • YAML schreiben oder editieren. Read-only.
  • DB-State ändern. Read-only.
  • Robots-Tiefen-Parse. Heuristik: Disallow: / für * oder seepuls. Komplexe Path-Patterns → User soll manuell checken.

Offene Punkte

  • Robots-Lookup könnte in src/crawl/robots.ts der API wiederverwendet werden. Aktuell ist der Validator-Skill bash-only — der echte Parser (mit Cache + Path-Match) lebt in Bun-Code. Bei systematischen False-Positives: Validator als bun script mit Import aus apps/api/src/crawl/robots.ts umbauen.
  • Geographische Region-Inference. Bei klar falschem Region-Slug (z.B. „Friedrichshafen" mit regionSlug: thurgau-west) wäre eine Vorschlag-Heuristik nett. Heute: Warning, kein Auto-Fix.

Referenzen

  • Schwester-Skill: /seepuls-curate-venue
  • Cross-Repo-Pattern: /cards-validate-deck
  • Aggregator-Policy: ../mana/docs/AGGREGATOR_POLICY.md