Files
paliad/scripts/skills/paliadin/references/sql-recipes.md
m 9579032f94 feat(t-paliad-155): re-author paliadin skill via /write-a-skill conventions
Splits the 250-line hand-rolled SKILL.md into a 96-line SKILL.md
(under the 100-line soft cap from agentskills-extras) plus
references/sql-recipes.md (134 lines). Description rewritten in
imperative voice with explicit pushy triggers — including the short-
message case ('Hey', 'wer bin ich?') so Claude doesn't second-guess
when the prefix [PALIADIN:<uuid>] is present but the body looks like
normal chat.

SKILL.md keeps: persona, response-file format, classifier table,
action chips, hard rules, full example, first-turn rule. Out: 8 SQL
recipes, moved to references/sql-recipes.md with a concrete pointer
trigger ('Read before any project / deadline / appointment / court /
glossary / deadline-rule / UPC-judgment lookup').

install-paliadin-skill now mirrors the entire skill tree (SKILL.md +
references/) and clears stale aux files on each run. Manual one-shot
— m's call to skip a post-merge auto-refresh hook for now.
2026-05-08 12:48:00 +02:00

4.5 KiB

SQL recipes — Paliadin tool catalogue

Read this file before any project / deadline / appointment / court / glossary / deadline-rule / UPC-judgment lookup. Every query goes through the Supabase MCP via mcp__supabase__execute_sql. Two schemas in the same physical DB:

  • paliad.* — Patentpraxis-Daten (projects, deadlines, appointments, parties, courts, deadline_rules, users)
  • data.* — youpc.org UPC case law (judgments, headnotes, knowledge graph)

Every project-scoped query MUST include paliad.can_see_project(project_id) — even when m is global_admin (see SKILL.md rule 4).

1. whats_on_my_plate — Dashboard-Übersicht

SELECT
  (SELECT count(*) FROM paliad.deadlines d
   WHERE paliad.can_see_project(d.project_id)
     AND d.status = 'pending' AND d.due_date < current_date) AS overdue,
  (SELECT count(*) FROM paliad.deadlines d
   WHERE paliad.can_see_project(d.project_id)
     AND d.status = 'pending' AND d.due_date = current_date) AS today,
  (SELECT count(*) FROM paliad.deadlines d
   WHERE paliad.can_see_project(d.project_id)
     AND d.status = 'pending'
     AND d.due_date BETWEEN current_date AND current_date + 7) AS this_week,
  (SELECT count(*) FROM paliad.appointments a
   WHERE (a.project_id IS NULL OR paliad.can_see_project(a.project_id))
     AND a.start_at::date = current_date) AS appointments_today;

2. list_my_projects

SELECT id, kind, label, status, parent_id, path
  FROM paliad.projects
 WHERE paliad.can_see_project(id)
   AND status = 'active'
 ORDER BY path
 LIMIT 25;

3. get_project_detail (per slug oder id)

SELECT p.*,
       (SELECT json_agg(d ORDER BY d.due_date)
          FROM paliad.deadlines d WHERE d.project_id = p.id
            AND paliad.can_see_project(d.project_id)) AS deadlines,
       (SELECT json_agg(a ORDER BY a.start_at)
          FROM paliad.appointments a WHERE a.project_id = p.id
            AND paliad.can_see_project(a.project_id)) AS appointments,
       (SELECT json_agg(pa) FROM paliad.parties pa WHERE pa.project_id = p.id) AS parties
  FROM paliad.projects p
 WHERE paliad.can_see_project(p.id)
   AND (p.id::text = '<UUID>' OR p.slug = '<slug>')
 LIMIT 1;

4. search_my_deadlines (status / Datum / Projekt)

SELECT d.id, d.title, d.due_date, d.status, p.label AS project_label, d.event_id
  FROM paliad.deadlines d
  JOIN paliad.projects p ON p.id = d.project_id
 WHERE paliad.can_see_project(d.project_id)
   AND ($status::text IS NULL OR d.status = $status)
   AND ($due_after::date IS NULL OR d.due_date >= $due_after)
   AND ($due_before::date IS NULL OR d.due_date <= $due_before)
 ORDER BY d.due_date ASC
 LIMIT 25;

5. list_my_appointments (Zeitfenster)

SELECT a.id, a.title, a.start_at, a.end_at, a.location, p.label AS project_label
  FROM paliad.appointments a
  LEFT JOIN paliad.projects p ON p.id = a.project_id
 WHERE (a.project_id IS NULL OR paliad.can_see_project(a.project_id))
   AND a.start_at >= $from
   AND a.start_at <= $to
 ORDER BY a.start_at ASC
 LIMIT 25;

6. lookup_court (firm-wide reference)

SELECT c.slug, c.name, c.country, c.kind, c.address
  FROM paliad.courts c
 WHERE c.name ILIKE '%' || $q || '%'
    OR c.slug ILIKE '%' || $q || '%'
 ORDER BY similarity(c.name, $q) DESC
 LIMIT 10;

7. lookup_deadline_rule (Fristenrechner-Konzepte)

SELECT r.rule_code, r.concept_label, r.trigger_event, r.deadline_text,
       r.deadline_text_en, r.legal_source, r.deadline_notes, r.deadline_notes_en
  FROM paliad.deadline_rules r
 WHERE r.concept_label ILIKE '%' || $q || '%'
    OR r.rule_code ILIKE '%' || $q || '%'
    OR r.legal_source ILIKE '%' || $q || '%'
 ORDER BY similarity(r.concept_label, $q) DESC
 LIMIT 5;

8. lookup_youpc_case (UPC-Rechtsprechung — cross-schema)

SELECT j.node_id, j.upc_number, j.court_division, j.judgment_type,
       j.proceedings_type, j.decision_date, j.headnote_summary,
       j.tags
  FROM data.judgments j
 WHERE j.upc_number ILIKE '%' || $q || '%'
    OR j.headnote_summary ILIKE '%' || $q || '%'
    OR j.tags::text ILIKE '%' || $q || '%'
 ORDER BY j.decision_date DESC
 LIMIT 5;

Volltext eines Urteils (wenn m fragt "was steht in dem Urteil?"):

SELECT content
  FROM data.judgment_markdown_content
 WHERE judgment_node_id = <node_id>
 ORDER BY chunk_index
 LIMIT 1;

Glossar — keine SQL-Tabelle

Der Patent-Glossar lebt statisch in internal/handlers/glossary.go (JSON beim Boot geladen). Für reine Begriffsfragen reicht dein Wissen + optional Cross-Check via paliad.deadline_rules.legal_source.