docs: add KanzlAI → Paliad integration design
Strategic pivot: Paliad becomes all-in-one platform for HLC patent practice (knowledge base + matter management). Design doc covers all 8 items from t-paliad-001: - Integration strategy: full merge into Paliad's stack (single Go binary, single Supabase schema, single Dokploy deployment) - Auth: drop multi-tenancy entirely (HLC is one tenant) - Schema: paliad.* schema absorbs kanzlai.*; matters renames cases - Feature prioritization: port deadlines/matters/appointments/CalDAV/ dashboard/AI/notes; drop billing/RVG/multi-tenant infrastructure - UI: single sidebar with 5 grouped sections; lime branding stays - Deployment: kanzlai.msbls.de → 301 to paliad.de, retire KanzlAI app - Roadmap update: rewrite "What patholo Is NOT" section, drop §2.3 UPC Rechtsprechung (link to youpc.org), add Phase 0 matter mgmt - Migration plan: 10 phases, ~52h coder time, ~2-3 weeks - Risks: KanzlAI audit issues must be fixed during port (not carried forward); React → Bun TSX rewrite needs HTML-first discipline
This commit is contained in:
547
docs/design-kanzlai-integration.md
Normal file
547
docs/design-kanzlai-integration.md
Normal file
@@ -0,0 +1,547 @@
|
||||
# KanzlAI → Paliad Integration
|
||||
|
||||
**Author:** cronus (inventor)
|
||||
**Date:** 2026-04-16
|
||||
**Task:** t-paliad-001
|
||||
**Status:** Design draft for review
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
**Recommendation: Full merge into a single Paliad codebase, single Go binary, single Supabase schema, single Dokploy deployment.** Port KanzlAI's domain logic (deadline rules, calculator, holiday service, matter/deadline/appointment/notes models, CalDAV sync, AI extraction) into Paliad's existing Go backend. Rewrite KanzlAI's React+Next.js frontend in Paliad's Bun + TSX stack. Retire `kanzlai.msbls.de` to a 301 redirect once parity is reached.
|
||||
|
||||
**Why this path.** Two backends behind one domain doubles operational surface for zero user benefit. The two products share an audience (HLC patent practice), a stack philosophy (Go + Supabase), and an auth model (Supabase password). The only real cost is rewriting the React frontend — which is a smaller burden than it sounds, because KanzlAI's UI is mostly forms, lists, calendar grids, and tabbed details, all of which fit Paliad's server-rendered TSX + per-page client JS pattern. The merger from HL → HLC is the right moment to consolidate: one platform, one URL, one mental model. Knowledge tools and matter management belong together — a lawyer who looks up a UPC fee on Paliad should also see their next UPC deadline.
|
||||
|
||||
---
|
||||
|
||||
## 1. Integration strategy
|
||||
|
||||
### Decision: Full merge into Paliad's stack
|
||||
|
||||
Four options were considered:
|
||||
|
||||
| Option | Backend | Frontend | UI | Verdict |
|
||||
|---|---|---|---|---|
|
||||
| **A. Run both apps behind unified domain** | 2 binaries (Go + Go) | 2 stacks (Bun TSX + Next) | 2 sidebars, reverse-proxy split | Doubles ops; jarring UX seam between `/` and `/app/*` |
|
||||
| **B. Port frontend only, keep backends separate** | 2 binaries | 1 stack (Bun TSX) | 1 sidebar, 2 API clients | Eliminates UI seam but keeps deploy/schema/auth split |
|
||||
| **C. Full merge — port KanzlAI into Paliad** | 1 binary | 1 stack (Bun TSX) | 1 sidebar | Higher upfront cost, lowest ongoing cost ✅ |
|
||||
| **D. Incremental — start with deadlines, then add others** | Phased | Phased | Phased | This is the *implementation* of C, not an alternative |
|
||||
|
||||
**Pick C, execute as D.** Option C is the destination; the phased migration plan in §8 *is* the incremental path.
|
||||
|
||||
### Why not keep KanzlAI as-is and link to it?
|
||||
|
||||
- KanzlAI has unfixed critical security issues (`AUDIT.md` §1.1 tenant isolation bypass, §1.3 plaintext CalDAV creds, §1.4 no CORS, §1.6 race condition in HolidayService). Shipping it under the HLC name without fixing those is malpractice. Fixing them requires a full pass anyway — better to do the fixes inside Paliad than touch a deprecated stack.
|
||||
- React + Next.js + Tailwind v4 + react-query is too much machinery for Paliad's profile (small content + tools site). Maintaining two frontends is a tax we don't want.
|
||||
- KanzlAI has zero production users today (per audit). Now is the cheapest possible moment to consolidate.
|
||||
|
||||
### Trade-off accepted
|
||||
|
||||
- **~6 weeks of focused implementation** (estimated 52h coder time, see §8) to retire ~16,500 lines of KanzlAI code and rebuild ~4,000 lines on Paliad's stack.
|
||||
- **Lose React ecosystem** (react-query, react-day-picker, etc.). Paliad's stack handles these patterns with HTML-first forms + small per-page TS bundles. Acceptable.
|
||||
|
||||
---
|
||||
|
||||
## 2. Auth & multi-tenancy
|
||||
|
||||
### Decision: HLC is one tenant. Drop multi-tenancy entirely.
|
||||
|
||||
KanzlAI was built multi-tenant in case it became a SaaS product. That bet is now dead — Paliad serves one firm. Multi-tenancy adds:
|
||||
- A whole `tenants` + `user_tenants` table layer
|
||||
- Tenant resolution middleware (with the tenant isolation bypass bug)
|
||||
- `tenant_id` on every row + every query
|
||||
- RLS policies that key off tenant membership
|
||||
|
||||
For one firm with one matter pool, this is pure overhead. **Strip it.**
|
||||
|
||||
### Concrete model
|
||||
|
||||
- **Auth gate**: keep the email domain whitelist on registration. Accept `@hoganlovells.com` AND `@hlc.com` (any TLD HLC ends up using). Whitelist is a config slice, not a hardcoded constant.
|
||||
- **Single shared visibility**: every authenticated user sees every matter/deadline/appointment by default. No office-scoped or practice-group-scoped filtering at the schema level.
|
||||
- **`paliad.users` table**: extends `auth.users`, adds `office`, `practice_group`, `role` (`partner` | `associate` | `pa` | `admin`). Used for display, attribution, and future ACL — not for access control on day one.
|
||||
- **RLS posture**: enable RLS on all tables, but with permissive policies (`USING (auth.role() = 'authenticated')`). RLS-on with permissive policies is cheap insurance against future tenant requirements and against forgotten WHERE clauses.
|
||||
- **Audit trail**: every mutation writes a `case_event` (or equivalent) with `created_by = auth.uid()`. So we have *attribution* even though we don't have *isolation*.
|
||||
|
||||
### When the model needs to change
|
||||
|
||||
If HLC ever decides "Munich shouldn't see Düsseldorf matters" (unlikely — HLC is a single firm with cross-office teams), we add an `acl` JSONB column or a `matter_visibility` table and tighten the RLS policy. Don't build that now.
|
||||
|
||||
### Post-merger email transition
|
||||
|
||||
- Phase 1 (now → merger): whitelist `[@hoganlovells.com, @hlc.com, @hlc.de]`. Any new HLC subdomain TLD added to the slice in env config.
|
||||
- Phase 2 (post-merger): when HLC mandates a new email, existing `@hoganlovells.com` Supabase identities continue to work. New users register under `@hlc.*`. No data migration needed because Supabase keys on UUID, not email.
|
||||
- Phase 3 (cleanup, +1 year): optional script to update display emails in `paliad.users` if HLC actually retires the old domain.
|
||||
|
||||
---
|
||||
|
||||
## 3. Database / schema
|
||||
|
||||
### Decision: Single `paliad` schema in the youpc Supabase instance. Migrate `kanzlai` tables in.
|
||||
|
||||
KanzlAI's schema lives in `kanzlai.*` on the youpc Supabase instance. Paliad currently has 3 feedback tables (`link_suggestions`, `checklisten_feedback`, `gerichte_feedback`) that look like they're in the public schema (no schema prefix in `docs/migrations/00*.sql`).
|
||||
|
||||
**Plan:**
|
||||
1. Create `paliad` schema in the same youpc Supabase instance (no new project, no DB migration).
|
||||
2. Move Paliad's 3 existing feedback tables into `paliad.*` (CREATE in new schema, COPY data, drop old).
|
||||
3. Port KanzlAI's tables into `paliad.*`, dropping multi-tenancy fields:
|
||||
- `cases` → `paliad.matters` (rename: "matter" is universal across DE/EN; "Akte" stays the German display label)
|
||||
- `parties`, `deadlines`, `appointments`, `documents`, `case_events`, `notes` → `paliad.*`, drop `tenant_id`, keep `created_by` (FK to `auth.users.id`)
|
||||
- Reference tables: `proceeding_types`, `deadline_rules`, `holidays` → `paliad.*` (these are pure reference data, no tenancy concept needed)
|
||||
4. RLS enabled on all paliad tables with permissive `authenticated` policies (per §2).
|
||||
5. Retire `kanzlai.*` schema once cutover is verified (`DROP SCHEMA kanzlai CASCADE` in Phase J).
|
||||
|
||||
### Reuse vs. port
|
||||
|
||||
- **Reuse the data**: KanzlAI's `deadline_rules` table has 32 UPC + 4 ZPO rules (more than Paliad's hardcoded `internal/calc/deadline_rules.go`). Port the data, drop Paliad's hardcoded rules.
|
||||
- **Reuse the holiday data**: KanzlAI's `holidays` table has DE federal + UPC judicial vacations 2026. Port and extend.
|
||||
- **Don't reuse**: KanzlAI's `tenants`, `user_tenants`, `billing_rates`, `invoices`, `time_entries` (the latter three are aspirational — handler stubs exist but no real implementation). Drop entirely.
|
||||
|
||||
### Migration tooling
|
||||
|
||||
Paliad currently has SQL files in `docs/migrations/` with no runner. Pick a tool now while the schema is small:
|
||||
|
||||
- **Choice: `golang-migrate/migrate`** as a sidecar in the Docker entrypoint. Migrations live in `migrations/` (top-level, not `docs/migrations/` — that path is wrong; move them). Numbered `00N_description.up.sql` + `00N_description.down.sql`. Applied at server startup before the HTTP listener binds.
|
||||
- Why golang-migrate over Atlas/Goose: simplest, single binary, well-known, plays nicely with Supabase Postgres.
|
||||
|
||||
### Schema collisions
|
||||
|
||||
None. Paliad's existing 3 feedback tables don't overlap with anything in KanzlAI. The risk vector was `cases` vs. `matters` naming — resolved by picking `matters` for the merged schema.
|
||||
|
||||
---
|
||||
|
||||
## 4. Feature prioritization
|
||||
|
||||
KanzlAI shipped a lot in one session; not all of it was production-quality. Cherry-pick.
|
||||
|
||||
### Ports (P0 — first cutover)
|
||||
|
||||
- **Deadline rules + holidays + calculator** (KanzlAI: `holidays.go`, `deadline_calculator.go`, `deadline_rule_service.go`). Replaces Paliad's `internal/calc/deadline_rules.go` hardcoded data.
|
||||
- **Matters (Mandate) CRUD + parties** (KanzlAI: `case_service.go`, `parties.go`). Foundation for everything below.
|
||||
- **Deadline management UI** (KanzlAI: `DeadlineList`, `DeadlineCalendarView`, traffic light cards). Extends current Fristenrechner with persistence.
|
||||
|
||||
### Ports (P1 — fast follow)
|
||||
|
||||
- **Appointments + CalDAV sync** (KanzlAI: `appointment_service.go`, `caldav_service.go`). CalDAV is a real differentiator. **Mandatory fix during port:** encrypt CalDAV credentials at rest (audit §1.3).
|
||||
- **Dashboard** (KanzlAI: `dashboard_service.go`, dashboard widgets). Replaces Paliad's logged-in landing.
|
||||
|
||||
### Ports (P2 — once foundation is stable)
|
||||
|
||||
- **AI deadline extraction** (KanzlAI: `ai_service.go`, document upload). Dependency on Anthropic key in Dokploy.
|
||||
- **Notes** (polymorphic notes table, KanzlAI Phase A backend). Cross-cutting; useful but not blocking.
|
||||
|
||||
### Drop entirely
|
||||
|
||||
- **Billing / RVG (`billing_rates.go`, `invoices.go`, `time_entries.go`)** — KanzlAI has handler stubs, no real impl. The audit (§8.2) calls out beA + RVG as table-stakes for *competing with RA-MICRO*. We're not competing with RA-MICRO. HLC has its own firm-wide billing. Don't pretend.
|
||||
- **`reports.go`, `notifications.go`, `templates.go`, `tenant_handler.go`, `case_assignments.go`** — all aspirational stubs in KanzlAI. Drop.
|
||||
- **Multi-tenancy infrastructure** — see §2.
|
||||
- **Reports/audit endpoints** beyond the basic `case_events` audit log.
|
||||
|
||||
### Knowledge platform features that survive
|
||||
|
||||
Paliad's existing tools (Kostenrechner, Glossar, Gebührentabellen, Checklisten, Gerichte, Links, Downloads) all stay as-is. They sit alongside the new matter management features in the same sidebar.
|
||||
|
||||
### Knowledge platform features that change
|
||||
|
||||
- **Fristenrechner** evolves: same calculator UI, but now backed by the same DB as deadline management. Adds a "Save to matter" button that creates persistent deadlines.
|
||||
- **Roadmap §2.3 UPC Rechtsprechungsübersicht** — dropped entirely (per task brief). Replaced by a curated link in the Link Hub pointing to youpc.org.
|
||||
|
||||
---
|
||||
|
||||
## 5. Roadmap update
|
||||
|
||||
### Changes to `docs/feature-roadmap.md`
|
||||
|
||||
**Rewrite the "What patholo Is NOT" section (lines 527–534).** New text:
|
||||
|
||||
```markdown
|
||||
### What Paliad Is
|
||||
|
||||
Paliad is the all-in-one platform for HLC patent practice:
|
||||
|
||||
- **Knowledge platform** — curated content, practical tools, quick reference (Glossar, Gebührentabellen, Checklisten, Gerichtsverzeichnis, Leitfäden, Links).
|
||||
- **Matter management** — Mandate (cases), Fristen (deadlines), Termine (appointments), parties, documents, notes, audit trail. Personal calendar sync via CalDAV. AI-assisted deadline extraction from uploaded court documents.
|
||||
|
||||
What Paliad is *not*:
|
||||
|
||||
- Not a billing tool — HLC has firm-wide billing infrastructure.
|
||||
- Not a beA gateway — out of scope; lawyers use existing beA software.
|
||||
- Not a document management system — SharePoint/netDocuments stay in their lane.
|
||||
- Not a CMS — content lives in git, not a database with a CMS UI.
|
||||
```
|
||||
|
||||
**Drop §2.3 UPC Rechtsprechungsübersicht entirely.** Replace with one Link Hub entry under "Recherche" pointing to `youpc.org/upc/rechtsprechung` (or wherever the curated overview lives). Save 14h of work; defer to youpc.org which already has the data.
|
||||
|
||||
**Update §4.2 Fristenkalender.** Currently P3, "8h, High impact". Promote to P0 — it's the entry point to the matter-management features, not an optional add-on. Replace with: "Fristenkalender → see §8 Phase E (Deadline management UI), Phase F (CalDAV sync)."
|
||||
|
||||
**Add new sections** (after current §1.5 or as a new top-level "Phase 0: Matter Management Foundation"):
|
||||
|
||||
```markdown
|
||||
### 0.1 Mandate (Matter Management)
|
||||
### 0.2 Fristen (Persistent Deadline Management)
|
||||
### 0.3 Termine + CalDAV Sync
|
||||
### 0.4 Dashboard (Logged-in Landing)
|
||||
### 0.5 AI-assisted Deadline Extraction
|
||||
### 0.6 Notes
|
||||
```
|
||||
|
||||
Each section should reference the per-phase task in §8 of *this* design doc rather than duplicating estimates.
|
||||
|
||||
**Update prioritized backlog table** (lines 463–483): insert the 6 Phase 0 features at the top with P0 priority. Demote 1.5 (Kostenrechner enhancements) to P1 — matter management beats Kostenrechner polish.
|
||||
|
||||
**Update Architecture Notes "Data Strategy" (line 489).** Currently says "Most Phase 1 and 2 features use static JSON data." Add: "Phase 0 features (matter management) use Supabase tables with RLS. The split is: reference data + curated content = git/JSON; user-generated data = DB."
|
||||
|
||||
---
|
||||
|
||||
## 6. UI integration
|
||||
|
||||
### Decision: One sidebar, six grouped sections
|
||||
|
||||
Current Paliad sidebar is flat. KanzlAI has its own React sidebar with cases-first navigation. Merge into a single grouped sidebar:
|
||||
|
||||
```
|
||||
PALIAD (logo, lime green)
|
||||
|
||||
— ÜBERSICHT —
|
||||
Dashboard
|
||||
|
||||
— ARBEIT —
|
||||
Mandate
|
||||
Fristen
|
||||
Termine
|
||||
|
||||
— WERKZEUGE —
|
||||
Kostenrechner
|
||||
Fristenrechner ← stays as a quick-calc tool, distinct from /fristen
|
||||
Gebührentabellen
|
||||
|
||||
— WISSEN —
|
||||
Glossar
|
||||
Checklisten
|
||||
Gerichtsverzeichnis
|
||||
Leitfäden (future, per current roadmap §2.1)
|
||||
|
||||
— RESSOURCEN —
|
||||
Downloads
|
||||
Nützliche Links
|
||||
|
||||
— FOOTER —
|
||||
DE / EN
|
||||
user@example.com → Logout
|
||||
```
|
||||
|
||||
Rationale:
|
||||
- **"Übersicht"** (Dashboard) is the new home for logged-in users. The current `/` marketing-ish landing becomes the unauthenticated welcome screen.
|
||||
- **"Arbeit"** = the user's active workload. This is what KanzlAI provided; it's now the top of the new value stack.
|
||||
- **"Werkzeuge"** = stateless calculators / lookups. Including Fristenrechner here as a quick scratch tool, separate from `/fristen` which is the persistent deadline list. (Yes, two deadline-related routes — they serve different jobs.)
|
||||
- **"Wissen"** = curated content.
|
||||
- **"Ressourcen"** = files + external links.
|
||||
|
||||
### Navigation in detail
|
||||
|
||||
- `/` — unauthenticated landing OR redirects authenticated users to `/dashboard`.
|
||||
- `/dashboard` — Dashboard (KanzlAI port).
|
||||
- `/mandate`, `/mandate/neu`, `/mandate/[id]`, `/mandate/[id]/{verlauf|fristen|termine|dokumente|parteien|notizen}` — matter list + detail with sub-pages.
|
||||
- `/fristen`, `/fristen/neu`, `/fristen/[id]`, `/fristen/kalender` — persistent deadlines.
|
||||
- `/termine`, `/termine/neu`, `/termine/[id]`, `/termine/kalender` — appointments.
|
||||
- `/tools/fristenrechner` — stays at current path (don't break links). Adds "Save to matter" button.
|
||||
- All other knowledge routes unchanged.
|
||||
|
||||
### Visual language
|
||||
|
||||
- Lime accent (`#c6f41c`) stays — it's the brand. Used for active nav, primary CTAs, traffic-light "go", new-since-last-visit badges.
|
||||
- Traffic light colors for deadlines: red `#ef4444` (overdue), amber `#f59e0b` (this week), green `#22c55e` (OK). KanzlAI's choices, keep them.
|
||||
- Type colors for appointments: hearing=blue, meeting=violet, consultation=emerald, deadline_hearing=amber. KanzlAI's choices, keep them.
|
||||
- Sidebar: existing Paliad styling, with section headers (small caps, low contrast) added.
|
||||
|
||||
### Frontend rewrite scope
|
||||
|
||||
Pages to build in Bun TSX:
|
||||
- `dashboard.tsx` (1 page)
|
||||
- `mandate.tsx`, `mandate-detail.tsx`, `mandate-neu.tsx` (3 pages, plus sub-routes via Go mux)
|
||||
- `fristen.tsx`, `fristen-detail.tsx`, `fristen-neu.tsx`, `fristen-kalender.tsx` (4 pages)
|
||||
- `termine.tsx`, `termine-detail.tsx`, `termine-neu.tsx`, `termine-kalender.tsx` (4 pages)
|
||||
- `einstellungen-caldav.tsx` (1 page)
|
||||
|
||||
Per-page client-side TS bundles in `src/client/` for interactive bits (calendar nav, mark complete, modal forms). HTML-first: forms POST and reload by default; JS enhances where it improves UX (calendar pagination, optimistic mark-complete).
|
||||
|
||||
No react-query, no Tailwind v4. Use existing `global.css` patterns.
|
||||
|
||||
---
|
||||
|
||||
## 7. Deployment
|
||||
|
||||
### Decision: Single Dokploy compose, retire `kanzlai.msbls.de`
|
||||
|
||||
- **Production target**: existing Paliad compose `Zx147ycurfYagKRl_Zzyo`. No new infra.
|
||||
- **Domains on the compose**:
|
||||
- `paliad.de` — primary
|
||||
- `patholo.de` — legacy redirect (already configured)
|
||||
- `patholo.msbls.de` — internal preview (already configured)
|
||||
- **Add: `kanzlai.msbls.de` → 301 redirect to `paliad.de`** (Phase J)
|
||||
- **KanzlAI Dokploy app** — stop and delete after Phase J cutover. Free up the resources.
|
||||
- **KanzlAI Gitea repo** (`m/KanzlAI-mGMT`) — archive (set read-only) after cutover. Keep for git history reference.
|
||||
|
||||
### Build and deploy
|
||||
|
||||
- Existing flow stays: push to `main` on `mAi/paliad` → Gitea webhook → Dokploy auto-deploy.
|
||||
- Dockerfile changes: add migration step to entrypoint (run `migrate up` against `DATABASE_URL` before starting the HTTP server).
|
||||
- New env vars in Dokploy:
|
||||
- `DATABASE_URL` (youpc Supabase Postgres conn string)
|
||||
- `SUPABASE_SERVICE_KEY` (for storage access during AI extraction)
|
||||
- `ANTHROPIC_API_KEY` (for AI extraction; AI features stay disabled if unset)
|
||||
- `CALDAV_ENCRYPTION_KEY` (32-byte AES key for credential-at-rest encryption)
|
||||
- Existing env vars unchanged: `SUPABASE_URL`, `SUPABASE_ANON_KEY`, `GITEA_TOKEN`, `PORT`.
|
||||
|
||||
### Cutover sequence
|
||||
|
||||
Single deployment (no parallel run). Because KanzlAI has zero production users, the cutover is just:
|
||||
|
||||
1. Apply migrations (Phase A).
|
||||
2. Ship phases B–H to `paliad.de`.
|
||||
3. Verify functionality.
|
||||
4. Flip `kanzlai.msbls.de` to 301-redirect to `paliad.de` (Phase J).
|
||||
5. Stop KanzlAI Dokploy app.
|
||||
|
||||
If KanzlAI ever gets users before cutover, switch to a parallel-run-with-data-migration model. Right now, no users = no data migration.
|
||||
|
||||
---
|
||||
|
||||
## 8. Migration plan (phased task breakdown)
|
||||
|
||||
Each phase is sized for one focused coder session with clear acceptance criteria. Effort estimates assume Sonnet implementation with Opus design review where flagged.
|
||||
|
||||
### Phase A — Database foundation & migration tooling (~6h)
|
||||
|
||||
**Scope:**
|
||||
- Create `paliad` schema in youpc Supabase instance.
|
||||
- Add `golang-migrate` to the Docker entrypoint.
|
||||
- Move existing Paliad migrations (`docs/migrations/00{1,2,3}.sql`) → `migrations/00{1,2,3}_*.up.sql` + `.down.sql`. Re-namespace tables into `paliad.*`.
|
||||
- Port KanzlAI tables → `paliad.*` migrations: `users`, `proceeding_types`, `deadline_rules`, `holidays`, `matters` (renamed from `cases`), `parties`, `deadlines`, `appointments`, `documents`, `case_events`, `notes`. Drop `tenant_id` everywhere; add `created_by` FK to `auth.users.id`.
|
||||
- RLS: `ENABLE ROW LEVEL SECURITY` on all paliad tables with permissive `authenticated` policy. Document the rationale in a migration comment.
|
||||
- Indexes per audit §3.3: `(status, due_date)` on deadlines, `(start_at)` on appointments, `(matter_id, created_at)` on case_events, `(status)` on matters.
|
||||
- Seed: 6 proceeding types, 32 UPC + 4 ZPO rules, 68 holidays (port directly from KanzlAI seed).
|
||||
|
||||
**Acceptance:**
|
||||
- `\dt paliad.*` on the Supabase Postgres shows ≥11 tables.
|
||||
- RLS enabled on all (verified via `pg_class.relrowsecurity`).
|
||||
- Migrations run cleanly up + down.
|
||||
- Seed data present.
|
||||
- Server starts after `migrate up` runs in entrypoint.
|
||||
|
||||
**Owner suggestion:** coder. Opus review the RLS policy + index choices before merge.
|
||||
|
||||
---
|
||||
|
||||
### Phase B — Backend foundation (sqlx, services, no UI yet) (~8h)
|
||||
|
||||
**Scope:**
|
||||
- Add `internal/db/` package (sqlx connection pool with `search_path = paliad,public` qualified at table level — don't rely on session-level setting per audit §2.8).
|
||||
- Port `internal/services/holidays.go` from KanzlAI (with `sync.RWMutex` fix per audit §1.6).
|
||||
- Port `internal/services/deadline_calculator.go` (UUID-based; works from DB rules).
|
||||
- Port `internal/services/deadline_rule_service.go` (read from DB).
|
||||
- Add `internal/services/matter_service.go` (formerly `case_service.go`), simplified — no tenant filtering, all queries scoped only by `auth.uid()` for `created_by` attribution.
|
||||
- Port `internal/services/party_service.go`.
|
||||
- Tests: deadline calculator round-trip, holiday adjustment, matter CRUD basic happy-path.
|
||||
|
||||
**Acceptance:**
|
||||
- `GET /api/deadline-rules` returns rules from DB.
|
||||
- `POST /api/deadlines/calculate` works with DB rules (proves Phase A schema ↔ Phase B services wiring).
|
||||
- `GET/POST /api/matters` works (no UI yet — verified via curl).
|
||||
- All ported tests pass.
|
||||
- No regressions in existing Paliad endpoints.
|
||||
|
||||
**Owner suggestion:** coder.
|
||||
|
||||
---
|
||||
|
||||
### Phase C — Migrate existing Fristenrechner to DB-backed rules (~3h)
|
||||
|
||||
**Scope:**
|
||||
- Delete `internal/calc/deadline_rules.go` hardcoded data.
|
||||
- Update `internal/handlers/fristenrechner.go` to call DB-backed services from Phase B.
|
||||
- Verify existing `/tools/fristenrechner` UI is byte-identical (no UX regression).
|
||||
- Update `internal/calc/deadlines.go`: delete logic that's now in `services/deadline_calculator.go`; thin shim if needed for the existing JSON shape.
|
||||
|
||||
**Acceptance:**
|
||||
- `/tools/fristenrechner` page identical UX, same dropdowns, same calc results.
|
||||
- No hardcoded rules in `internal/calc/`.
|
||||
- Existing fristenrechner tests pass against new code path.
|
||||
|
||||
**Owner suggestion:** coder.
|
||||
|
||||
---
|
||||
|
||||
### Phase D — Matters (Mandate) CRUD UI (~6h)
|
||||
|
||||
**Scope:**
|
||||
- Backend: handlers for `GET/POST/PATCH/DELETE /api/matters`, `GET/POST /api/matters/{id}/parties`.
|
||||
- New TSX pages: `mandate.tsx` (list with filters), `mandate-neu.tsx` (form), `mandate-detail.tsx` (header + tabs).
|
||||
- Sub-routes via Go mux: `/mandate/{id}/verlauf`, `/mandate/{id}/parteien`, etc. (other tabs come in later phases — show "Coming soon" placeholder).
|
||||
- Sidebar: add "Arbeit" group with Mandate as the only entry initially.
|
||||
- Per-page client TS for inline edit, delete confirm.
|
||||
|
||||
**Acceptance:**
|
||||
- Lawyer logs in, clicks "Mandate", creates a new matter, adds parties, sees it in the list, opens detail page.
|
||||
- Audit trail: each mutation creates a `case_event` row.
|
||||
- Empty states are friendly (German Umlaute correct per audit §2.10).
|
||||
|
||||
**Owner suggestion:** coder.
|
||||
|
||||
---
|
||||
|
||||
### Phase E — Persistent deadline management UI (~6h)
|
||||
|
||||
**Scope:**
|
||||
- Backend: handlers for `GET /api/deadlines` (all), `GET/POST /api/matters/{id}/deadlines`, `PATCH /api/deadlines/{id}`, `PATCH /api/deadlines/{id}/complete`, `DELETE /api/deadlines/{id}`.
|
||||
- New TSX pages: `fristen.tsx` (list with status filter — overdue/this_week/all), `fristen-neu.tsx`, `fristen-detail.tsx`, `fristen-kalender.tsx` (month grid).
|
||||
- Traffic light cards on `fristen.tsx` + on `mandate-detail.tsx` (deadlines tab — replace placeholder from Phase D).
|
||||
- "Save to matter" button on existing Fristenrechner: select target matter from dropdown, persists calculated deadlines as draft.
|
||||
|
||||
**Acceptance:**
|
||||
- Lawyer creates matter, adds deadlines manually OR via Fristenrechner "Save to matter".
|
||||
- List filterable by status. Calendar view navigable by month.
|
||||
- Mark-complete works, persists, updates traffic light counts.
|
||||
|
||||
**Owner suggestion:** coder.
|
||||
|
||||
---
|
||||
|
||||
### Phase F — Appointments + CalDAV sync (~8h)
|
||||
|
||||
**Scope:**
|
||||
- Backend: `appointment_service.go`, handlers for CRUD.
|
||||
- Backend: `caldav_service.go` ported from KanzlAI **with credential encryption fix** (audit §1.3): use `CALDAV_ENCRYPTION_KEY` env var with AES-GCM at rest. Never return password in API responses.
|
||||
- New TSX pages: `termine.tsx`, `termine-neu.tsx`, `termine-detail.tsx`, `termine-kalender.tsx`, `einstellungen-caldav.tsx`.
|
||||
- Per-user CalDAV config (one entry per `auth.uid()`, stored in new `paliad.user_caldav_config` table).
|
||||
- Background sync goroutine per user with active config (pulled at server startup, refreshed when config changes).
|
||||
|
||||
**Acceptance:**
|
||||
- User configures CalDAV in settings, password is encrypted in DB.
|
||||
- User creates an appointment, it appears in their external calendar within 60s.
|
||||
- External calendar event edited → reflected in Paliad on next sync cycle.
|
||||
- Deleting CalDAV config purges credentials.
|
||||
|
||||
**Owner suggestion:** coder, Opus design review of the encryption scheme + sync conflict resolution.
|
||||
|
||||
---
|
||||
|
||||
### Phase G — Dashboard (~4h)
|
||||
|
||||
**Scope:**
|
||||
- Backend: `dashboard_service.go` ported and simplified (no tenant). Returns `deadline_summary`, `matter_summary`, `upcoming_deadlines` (7d), `upcoming_appointments` (7d), `recent_activity`.
|
||||
- New TSX page: `dashboard.tsx` — server-rendered initial paint (no client-side fetch + skeleton, per audit §2.3 critique).
|
||||
- Update `/` route: redirect authenticated users to `/dashboard`; serve current marketing-ish landing only for unauthenticated visitors (or remove entirely if HLC sees no value in the unauthenticated landing).
|
||||
- Update sidebar "Übersicht → Dashboard" entry.
|
||||
|
||||
**Acceptance:**
|
||||
- Authenticated user lands on `/dashboard` with zero client-side waterfall.
|
||||
- Traffic lights link to `/fristen?status=...`.
|
||||
- Upcoming items link to detail pages.
|
||||
- Recent activity shows last 10 case events with author + timestamp.
|
||||
|
||||
**Owner suggestion:** coder.
|
||||
|
||||
---
|
||||
|
||||
### Phase H — AI deadline extraction (~4h)
|
||||
|
||||
**Scope:**
|
||||
- Backend: `ai_service.go` ported from KanzlAI. Anthropic SDK call with PDF document blocks + tool-forced structured output.
|
||||
- Backend: document upload → Supabase Storage, then AI extraction triggered.
|
||||
- Frontend: document upload component on `mandate-detail.tsx` "Dokumente" tab.
|
||||
- Extraction result: review screen with confidence scores + source quotes; user confirms which deadlines to persist.
|
||||
- Feature flag: AI tab/buttons hidden if `ANTHROPIC_API_KEY` is unset.
|
||||
|
||||
**Acceptance:**
|
||||
- Upload a UPC Statement of Defence PDF → AI extracts deadlines with rule references → user confirms → persisted as draft deadlines on the matter.
|
||||
- No AI calls on uploads to other matters' documents (stays user-triggered).
|
||||
|
||||
**Owner suggestion:** coder.
|
||||
|
||||
---
|
||||
|
||||
### Phase I — Notes (polymorphic) (~4h)
|
||||
|
||||
**Scope:**
|
||||
- Backend: `notes.go` table migration (polymorphic FK with CHECK constraint per KanzlAI Phase A pattern), `note_service.go`, handlers.
|
||||
- Frontend: shared `NotesList` TSX component, embedded on matter / deadline / appointment / case-event detail pages.
|
||||
|
||||
**Acceptance:**
|
||||
- User adds note to a matter → visible on matter detail.
|
||||
- User adds note to a deadline → visible on deadline detail.
|
||||
- Edit/delete works.
|
||||
|
||||
**Owner suggestion:** coder.
|
||||
|
||||
---
|
||||
|
||||
### Phase J — Roadmap rewrite + KanzlAI retirement (~3h)
|
||||
|
||||
**Scope:**
|
||||
- Rewrite `docs/feature-roadmap.md` per §5 of this doc.
|
||||
- Add `kanzlai.msbls.de` domain to Paliad Dokploy compose with 301 redirect rule (Traefik label or middleware).
|
||||
- Stop and delete KanzlAI Dokploy app.
|
||||
- Archive `m/KanzlAI-mGMT` Gitea repo (set to read-only / archived).
|
||||
- Update `mai.projects`: optionally merge `kanzlai` project into `paliad` (decide: keep history separate or unify).
|
||||
- Memory: write episode in `paliad` group documenting the consolidation, supersede KanzlAI episodes.
|
||||
- Drop `kanzlai` schema from Supabase: `DROP SCHEMA kanzlai CASCADE` after final verification that no Paliad code references it.
|
||||
|
||||
**Acceptance:**
|
||||
- `kanzlai.msbls.de` redirects to `paliad.de` with 301.
|
||||
- KanzlAI Dokploy app gone.
|
||||
- KanzlAI repo archived.
|
||||
- Roadmap doc reflects the new strategy.
|
||||
- `kanzlai` schema dropped.
|
||||
|
||||
**Owner suggestion:** head + cronus together (mostly ops, a bit of writing).
|
||||
|
||||
---
|
||||
|
||||
### Total effort
|
||||
|
||||
**~52 hours of focused implementation across 10 phases**, plus this design review (~3h) = **~55h total**. At 1–2 phases per day with one coder, this completes in **2–3 working weeks**.
|
||||
|
||||
---
|
||||
|
||||
## 9. Risks & unknowns
|
||||
|
||||
### Risks
|
||||
|
||||
1. **KanzlAI's audit issues must not be ported as-is.** The audit identified 7 critical and 13 important issues. Each port phase is responsible for fixing the issues relevant to its code: Phase A fixes RLS policies (audit §2.13), Phase B fixes HolidayService race + search_path (§1.6, §2.8), Phase F fixes CalDAV plaintext (§1.3), every backend phase implements proper error handling (§1.5) + length limits + pagination (§2.1, §2.2). Track these in a checklist per phase.
|
||||
|
||||
2. **React → Bun TSX rewrite friction.** KanzlAI's frontend uses react-query, react-day-picker, modal libs, complex client state. Paliad's stack uses server-rendered TSX + small per-page client TS. Some patterns translate awkwardly. Mitigation: HTML-first design, JS only where it materially improves UX. If a feature *requires* a heavy interactive component (e.g., date picker), prefer native `<input type="date">` over importing a library.
|
||||
|
||||
3. **Single-tenant assumption locks us in.** If HLC ever wants per-office or per-practice-group matter visibility, schema needs ACL columns or a separate visibility table. Mitigation: every table has `created_by`; can add `visible_to JSONB` later without breaking changes. Don't build it now.
|
||||
|
||||
4. **Email domain transition risk.** Whitelist `[@hoganlovells.com, @hlc.com, @hlc.de]`. Risk: HLC picks a TLD we didn't anticipate (`@hlc.law`?). Mitigation: keep the whitelist in env config, not hardcoded. Trivial to update.
|
||||
|
||||
5. **CalDAV encryption key rotation.** If `CALDAV_ENCRYPTION_KEY` is ever lost or rotated, all stored credentials become unreadable. Mitigation: document the recovery posture (users re-enter CalDAV password from their existing software), commit to never rotating without a re-encrypt migration.
|
||||
|
||||
6. **KanzlAI-derived German Umlaut typos** in audit §2.10. Don't carry them forward. Style check during each frontend port phase.
|
||||
|
||||
7. **No load testing on KanzlAI's calc-heavy paths.** AI extraction in particular can be slow + expensive. Phase H should add per-user rate limits before going live.
|
||||
|
||||
### Unknowns
|
||||
|
||||
- **Does HLC actually want firm-wide matter visibility, or office-scoped?** Need product input from m. The §2 decision (firm-wide) is the simplest assumption; if wrong, Phase D needs an ACL design before shipping.
|
||||
- **AI extraction quality on real HLC documents.** KanzlAI's test fixtures were synthetic. Real UPC Statements of Defence are messier. Phase H should include a manual test pass on 3–5 real (anonymized) HLC documents before declaring done.
|
||||
- **CalDAV server compatibility.** KanzlAI tested against `dav.msbls.de` and Apple iCloud. HLC lawyers may use Outlook + Exchange, where CalDAV support is limited or off by default. Phase F should verify against the actual calendar systems HLC lawyers use.
|
||||
- **HLC IT approval for storing matter data on a Supabase instance hosted in Germany.** Out of scope for this design but blocking for going live with real client data. Flag to m.
|
||||
- **Naming: "Mandate" vs "Akten" vs "Matters"** in the German UI. Currently picking "Mandate" — confirm with a HLC partner before frontend strings are baked in.
|
||||
|
||||
---
|
||||
|
||||
## 10. Open question for the head
|
||||
|
||||
Once this design is approved, who implements?
|
||||
|
||||
- **Option 1: I (cronus) implement Phases A–J as coder.** I have the deepest context from this design work and the source repos. Single context, no handoff loss. Likely faster overall.
|
||||
- **Option 2: Hand off to a separate coder worker (e.g., pike, knuth, ritchie) per phase.** Better parallelism if multiple phases can run in parallel. But Phases A → B → C → D have hard dependencies; only F, G, H, I can run in any order after E.
|
||||
- **Option 3: Hybrid — I implement Phases A–C (foundation), hand off D–I to coder workers in parallel where possible, take J myself for the wrap-up.**
|
||||
|
||||
**My recommendation: Option 3.** Foundation needs design context (cronus is the carrier). Phases D–I are mechanical ports of well-defined services; coder workers can run them with the design doc + KanzlAI source as the brief. Phase J is the easy victory lap.
|
||||
|
||||
---
|
||||
|
||||
*End of design.*
|
||||
Reference in New Issue
Block a user