Files
paliad/internal/db/migrations/058_paliadin_poc.down.sql
m 7b66c4d035 feat(t-paliad-146): Paliadin PoC — tmux-Claude in-app AI buddy
Phase 0 of the Paliadin design (docs/design-paliadin-2026-05-07.md
§0.5). m-only laptop scope, gated behind PALIADIN_ENABLED=false on
prod. Lifts the goldi/mVoice tmux-Claude pattern (mVoice/server.py:
250-380) into a Go service: long-lived `claude` pane in a tmux
session, prompts in via `tmux send-keys -l`, responses out via a
per-turn file (/tmp/paliadin/{turn_id}.txt) the system prompt
instructs Claude to write.

What landed
-----------
- migration 058_paliadin_poc — paliad.paliadin_turns audit table
  (full prompt + response stored at PoC scope; redaction returns
  at production v1 per design §3.3). RLS: user sees own,
  global_admin sees all.

- internal/services/paliadin.go — the orchestrator. ensurePane()
  finds-or-creates the tagged tmux window, sendToPane sends the
  framed [PALIADIN:turn_id] envelope, pollForResponse reads the
  per-turn file, splitTrailer parses the [paliadin-meta] block
  Claude appends to every reply (used_tools, rows_seen,
  classifier_tag).

- internal/services/paliadin_prompt.go — the system prompt sent
  once to a fresh Claude pane. Defines the response protocol
  (Write-to-file + meta trailer), the action-chip marker syntax,
  the visibility-gate rule (paliad.can_see_project required in
  every project-scoped query), and 9 SQL recipes covering m's
  paliad data + cross-schema youpc case-law lookup.

- internal/handlers/paliadin.go — POST /api/paliadin/turn kicks
  off the work in a goroutine and returns an SSE URL; GET
  /api/paliadin/stream/{id} relays per-turn channel events
  (meta/content/end/error/ping) to EventSource. Routes register
  ONLY when PaliadinService is wired — paliadinSvc nil → no
  handlers exist, prod surface is clean.

- /admin/paliadin dashboard — global_admin-only. Shows total
  turns, last-7-days, median/p90 duration, tool-use rate (the
  load-bearing §0.5.7 metric), abandon rate, classifier
  histogram, daily sparkline, top prompts, recent turn log.
  Powered by PaliadinService.Stats() + ListRecentTurns().

- frontend: paliadin.tsx + client/paliadin.ts (chat panel with
  starter prompts, EventSource consumer, typewriter render of
  one-shot content blob, citation-chip parser, "Stop" + "New
  conversation" buttons, localStorage history); admin-paliadin
  pair (read-only stats dashboard).

- Sidebar: Paliadin entry under Übersicht (ICON_SPARKLE);
  Paliadin Monitor under Admin.

- 36 i18n keys (DE+EN), CSS for chat panel + dashboard.

- main.go: PaliadinService wires only on PALIADIN_ENABLED=true,
  with PALIADIN_TMUX_SESSION + PALIADIN_RESPONSE_DIR overrides.
  Logs visibly so the operator can confirm at boot.

- CLAUDE.md: ANTHROPIC_API_KEY row updated (PoC doesn't need it
  — Claude CLI uses m's subscription; key reserved for future
  production-v1). New rows for the three PALIADIN_* env vars.

Tests
-----
- 7 unit tests on the trailer parser, chip counter, token approx,
  and tmux-input sanitiser. All pass. The trailer parser is
  load-bearing for monitoring; an unobserved parser bug = silent
  dashboard rot.

What's NOT in v1 (stays deferred)
---------------------------------
- The Anthropic API client (production v1, gated on PoC success
  per §0.5.7).
- BYO-AI / OpenAI adapter.
- Per-user rate limiting.
- Multi-replica SSE bus.
- Mascot / avatar SVG.
- Persistent threads (history is browser localStorage only).

How to use locally
------------------
  $ export PALIADIN_ENABLED=true
  $ ./paliad
  # browse /paliadin → type a question → answers stream back
  # /admin/paliadin shows the monitoring dashboard

Migration: 058 (skips fritz's t-147 on 057). Safe on prod
because PALIADIN_ENABLED defaults to false; the table is created
but no routes touch it until the env var flips.
2026-05-07 21:49:33 +02:00

4 lines
107 B
SQL

-- t-paliad-146: Paliadin PoC — drop paliad.paliadin_turns.
DROP TABLE IF EXISTS paliad.paliadin_turns;