Files
KanzlAI-mGMT/ROADMAP.md
m ac04930667 feat: comprehensive KanzlAI-mGMT system roadmap
Full system vision document covering 23 features across 4 priority tiers:
- P0 (must-have): audit trail, conflict checks, roles/permissions,
  notifications, time tracking, RVG calculator, invoicing, DATEV export
- P1 (should-have): document templates, beA integration, full-text search,
  Wiedervorlagen, email integration, reporting
- P2 (differentiator): patent family tracking, claim charts, UPC case law
  intelligence via mLex, AI document drafting, AI strategy analysis
- P3 (nice-to-have): client portal, PWA, multi-language, EDA

Includes data model designs (24 new tables), API specifications,
implementation phases, competitive analysis, and risk register.
2026-03-28 02:30:39 +01:00

54 KiB
Raw Permalink Blame History

KanzlAI-mGMT: Full System Roadmap

Author: cronus (inventor) Date: 2026-03-28 Status: Design proposal — awaiting review


Vision

KanzlAI-mGMT becomes the first Kanzleimanagement system built for UPC patent litigation — with full general-purpose law firm management that rivals RA-MICRO and Kleos, plus a UPC-specific intelligence layer that no competitor can match.

The competitive moat: m's existing infrastructure (youpcms scraper, 1,600+ structured UPC cases, legal knowledge graph, mLex research tools) feeds directly into KanzlAI, making it the only software that understands UPC proceedings — not just manages them.


What Exists (MVP — Shipped)

Area Status
Multi-tenant auth (Supabase Auth + RLS) Done
Case management (CRUD, parties, timeline) Done
Deadline management (CRUD, UPC+ZPO rules, calculator, holiday adjustment) Done
Appointments (CRUD, list + calendar view) Done
AI extraction (Claude API — PDF/text to deadlines) Done
Dashboard (traffic lights, timeline, AI summary, quick actions) Done
CalDAV bidirectional sync (deadlines as VTODO, appointments as VEVENT) Done
Document upload (Supabase Storage, 50MB max) Done
Notes system (polymorphic, attached to cases/deadlines/appointments/events) Done
Detail pages with breadcrumbs and URL-based navigation Done
Team management (invite by email, owner/admin/member roles) Done
Settings page (CalDAV config, tenant info) Done

Priority Tiers

  • P0 (Must-Have) — Without these, it's not a real Kanzleimanagement. Required for any law firm to consider adopting.
  • P1 (Should-Have) — Makes KanzlAI competitive with established players. Required to win against RA-MICRO/Advoware.
  • P2 (Differentiator) — UPC-specific intelligence and advanced AI. What makes a patent lawyer say "I need THIS, not RA-MICRO."
  • P3 (Nice-to-Have) — Polish, integrations, and extended capabilities.

P0: Must-Have Features

1. Audit Trail (Revisionsprotokoll)

Why: Legal requirement. § 50 BRAO mandates orderly file-keeping. DSGVO Art. 5(2) requires demonstrable compliance. Professional liability insurance (Berufshaftpflicht) expects change traceability. Without this, no serious firm will adopt the software.

Design:

The existing case_events table handles case-level events but is not a proper audit log. We need a dedicated, append-only audit log that captures every mutation across the system.

New table: kanzlai.audit_log

CREATE TABLE kanzlai.audit_log (
    id          BIGSERIAL PRIMARY KEY,
    tenant_id   UUID NOT NULL REFERENCES kanzlai.tenants(id),
    user_id     UUID,                          -- auth.users, NULL for system actions
    action      TEXT NOT NULL,                  -- 'create', 'update', 'delete', 'view', 'export'
    entity_type TEXT NOT NULL,                  -- 'case', 'deadline', 'appointment', 'document', ...
    entity_id   UUID,                          -- ID of affected entity
    old_values  JSONB,                         -- Previous state (for updates/deletes)
    new_values  JSONB,                         -- New state (for creates/updates)
    ip_address  INET,                          -- Client IP
    user_agent  TEXT,                           -- Browser/client info
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Append-only: NO UPDATE or DELETE policies
-- Partitioned by month for performance at scale
-- Index on (tenant_id, entity_type, entity_id) for entity history
-- Index on (tenant_id, user_id, created_at) for user activity
-- Index on (tenant_id, created_at) for chronological browsing

What to log:

Event entity_type Details
Case create/update/archive case Full old/new state diff
Deadline create/update/complete/delete deadline Including calculated vs manual source
Appointment CRUD appointment Time changes especially
Document upload/download/delete document File name, size, who accessed
Party add/remove/update party Name changes, role changes
Note create/update/delete note Content diff
Conflict check performed conflict_check Query, results, clearance decision
beA message sent/received bea_message Sender, recipient, subject
Invoice generated/sent invoice Amount, recipient
User login/logout auth IP, success/failure
Data export (DATEV etc.) export What was exported
Settings change settings CalDAV config, tenant settings
Team member invite/remove membership Who, role

Backend implementation:

  • Middleware-based: wrap all handlers with an audit logger that captures before/after state
  • AuditService.Log(ctx, action, entityType, entityID, oldValues, newValues) — used by all services
  • GET /api/audit-log?entity_type=X&entity_id=Y&from=DATE&to=DATE — paginated query
  • GET /api/audit-log/entity/{type}/{id} — full history of one entity

Frontend:

  • "Verlauf" (History) tab on every detail page showing change log
  • Global audit log page at /protokoll for admins/owners
  • Each entry shows: timestamp, user, action, human-readable diff

Retention: 6 years per § 50 BRAO, with automated cleanup jobs for expired data.


2. Conflict of Interest Checks (Interessenkollisionsprüfung)

Why: Statutory obligation under § 43a Abs. 4 BRAO. Failure to check = professional misconduct. Every competitor has this. A law firm cannot ethically operate without it.

Design:

Trigger points:

  1. New case creation — check all party names against the database
  2. New party added to existing case — re-run check
  3. Manual check from a search interface

New table: kanzlai.conflict_checks

CREATE TABLE kanzlai.conflict_checks (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL REFERENCES kanzlai.tenants(id),
    case_id     UUID REFERENCES kanzlai.cases(id),      -- which case triggered the check
    query_names TEXT[] NOT NULL,                          -- names that were checked
    results     JSONB NOT NULL,                           -- matched parties with scores
    status      TEXT NOT NULL DEFAULT 'pending',          -- 'pending', 'cleared', 'conflict_found'
    cleared_by  UUID,                                     -- user who approved
    cleared_at  TIMESTAMPTZ,
    notes       TEXT,                                     -- explanation for clearance
    created_by  UUID NOT NULL,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

Search algorithm (phased):

Phase Method Coverage
1 Exact name match (case-insensitive) Direct hits
2 Trigram similarity (pg_trgm extension, threshold 0.3) Typos, variations
3 Cologne phonetic algorithm Sound-alike German names
4 Corporate relationship graph Subsidiaries, affiliated entities

PostgreSQL pg_trgm is already available in Supabase. Cologne phonetic can be implemented as a PL/pgSQL function or in Go.

API:

  • POST /api/conflict-check — run check against names, return matches with confidence scores
  • POST /api/conflict-check/{id}/clear — mark as cleared (partner/admin only)
  • GET /api/conflict-checks?case_id=X — list checks for a case

Frontend:

  • Automatic check on case creation: after saving, show results in a modal before the case can be worked on
  • Manual check page at /kollision with a multi-name search form
  • Results display: matched party name, matched case (case number + title), role on that case, match confidence, match method (exact/fuzzy/phonetic)
  • "Freigeben" (clear) button for authorized users with mandatory notes field

Corporate relationships (Phase 4 — later):

New table for tracking corporate structures:

CREATE TABLE kanzlai.party_relationships (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID NOT NULL,
    party_id        UUID NOT NULL REFERENCES kanzlai.parties(id),
    related_party_id UUID NOT NULL REFERENCES kanzlai.parties(id),
    relationship    TEXT NOT NULL,  -- 'subsidiary', 'parent', 'affiliate', 'successor'
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

3. Role-Based Permissions (Rollenverwaltung)

Why: Law firms have strict hierarchies. A Sekretärin should not modify fee agreements. A Referendar should not clear conflict checks. The current system has owner/admin/member, but no functional role differentiation.

Design:

Extend the existing user_tenants.role from simple owner/admin/member to a proper permission system:

Roles:

Role German Permissions
partner Partner/Sozius Full access, clear conflicts, approve invoices, manage team, view audit log
associate Anwalt/Anwältin Case work, create/edit cases/deadlines/documents, time tracking, view own cases
paralegal Rechtsanwaltsfachangestellte/r Create/edit cases, manage deadlines, upload documents, no billing access
secretary Sekretär/in Manage appointments, deadlines, upload documents, no case creation
trainee Referendar/in View access, add notes, limited editing (supervised)
extern Externer Zugang Read-only view of assigned cases (for client portal)

New table: kanzlai.permissions

CREATE TABLE kanzlai.permissions (
    role        TEXT NOT NULL,
    resource    TEXT NOT NULL,      -- 'cases', 'deadlines', 'billing', 'audit', 'team', 'conflicts'
    action      TEXT NOT NULL,      -- 'view', 'create', 'edit', 'delete', 'approve'
    allowed     BOOLEAN NOT NULL DEFAULT false,
    PRIMARY KEY (role, resource, action)
);

Seed with a sensible default permission matrix. Tenants can override per-role if needed (stored in tenants.settings.permissions JSONB).

Backend changes:

  • Authorization middleware checks (role, resource, action) before handler execution
  • Replace simple role == "owner" || role == "admin" checks with permission lookups
  • GET /api/permissions — list permissions for current user
  • PUT /api/permissions — update role permissions (owner only)

Frontend changes:

  • Settings page gets a "Rollen & Berechtigungen" section
  • UI elements hidden/disabled based on user permissions
  • Permission-aware API client that prevents unauthorized requests

4. Email Notifications & Reminders (Benachrichtigungen)

Why: A deadline management system without reminders is a liability. If a Frist is missed because nobody was notified, the software is actively harmful. This is the most dangerous gap in the current MVP.

Design:

Notification types:

Trigger Recipients Channel Timing
Deadline warning date reached Case assigned users Email + in-app Morning of warning_date
Deadline due tomorrow Case assigned users Email + in-app Morning before due_date
Deadline overdue Case assigned users + partners Email + in-app Morning after due_date
Appointment in 1 hour Appointment attendees In-app push 1 hour before
New case event Case assigned users In-app Immediate
Conflict check needed Partners Email + in-app On case creation
beA message received Assigned lawyer Email + in-app Immediate
Team member invited Invitee Email Immediate

New tables:

-- Notification preferences per user
CREATE TABLE kanzlai.notification_preferences (
    user_id     UUID NOT NULL,
    tenant_id   UUID NOT NULL,
    channel     TEXT NOT NULL,           -- 'email', 'in_app', 'push'
    event_type  TEXT NOT NULL,           -- 'deadline_warning', 'deadline_due', 'deadline_overdue', ...
    enabled     BOOLEAN NOT NULL DEFAULT true,
    PRIMARY KEY (user_id, tenant_id, channel, event_type)
);

-- In-app notification inbox
CREATE TABLE kanzlai.notifications (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    user_id     UUID NOT NULL,           -- recipient
    title       TEXT NOT NULL,
    body        TEXT,
    link        TEXT,                    -- deep link to relevant page
    event_type  TEXT NOT NULL,
    entity_type TEXT,
    entity_id   UUID,
    read_at     TIMESTAMPTZ,             -- NULL = unread
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Case user assignments (who works on which case)
CREATE TABLE kanzlai.case_assignments (
    case_id     UUID NOT NULL REFERENCES kanzlai.cases(id) ON DELETE CASCADE,
    user_id     UUID NOT NULL,
    role        TEXT NOT NULL DEFAULT 'assigned',  -- 'lead', 'assigned', 'observer'
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    PRIMARY KEY (case_id, user_id)
);

Backend:

  • Notification worker: Background goroutine (like CalDAV sync) that runs every minute:
    1. Query deadlines where warning_date = today or due_date = tomorrow or due_date < today AND status != 'completed'
    2. Look up case assignments to find recipients
    3. Create in-app notifications + send emails via m mail send
    4. Track sent notifications to avoid duplicates (dedup by entity_id + event_type + date)
  • API: GET /api/notifications (paginated, unread first), PATCH /api/notifications/{id}/read, PATCH /api/notifications/read-all
  • Email service: Uses m mail send for reliable delivery with Sent folder copy

Frontend:

  • Bell icon in header with unread count badge
  • Notification dropdown/panel showing recent notifications
  • Notification preferences page in settings
  • Case detail: "Zugewiesene Personen" section for managing case assignments

5. Time Tracking (Zeiterfassung)

Why: Every Kanzleimanagement system has this. Lawyers bill by the hour. Without time tracking, they need a separate system, breaking the workflow.

Design:

New table: kanzlai.time_entries

CREATE TABLE kanzlai.time_entries (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL REFERENCES kanzlai.tenants(id),
    case_id     UUID NOT NULL REFERENCES kanzlai.cases(id),
    user_id     UUID NOT NULL,
    date        DATE NOT NULL,
    duration    INTEGER NOT NULL,                  -- minutes
    description TEXT NOT NULL,
    activity    TEXT,                               -- 'research', 'drafting', 'hearing', 'call', 'review', 'travel'
    billable    BOOLEAN NOT NULL DEFAULT true,
    billed      BOOLEAN NOT NULL DEFAULT false,     -- linked to invoice
    invoice_id  UUID,                               -- set when billed
    hourly_rate NUMERIC(10,2),                      -- rate at time of entry (snapshot)
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- User hourly rates (can change over time)
CREATE TABLE kanzlai.hourly_rates (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    user_id     UUID NOT NULL,
    rate        NUMERIC(10,2) NOT NULL,            -- EUR per hour
    valid_from  DATE NOT NULL,
    valid_to    DATE,                               -- NULL = current
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

API:

  • GET /api/time-entries?case_id=X&user_id=Y&from=DATE&to=DATE
  • POST /api/time-entries — create entry
  • PUT /api/time-entries/{id} — update (only if not billed)
  • DELETE /api/time-entries/{id} — delete (only if not billed)
  • GET /api/time-entries/summary?group_by=case|user|month — aggregated view

Frontend:

  • Timer widget in header — click to start/stop, auto-fills duration
  • Case detail: "Zeiterfassung" tab showing time entries for that case
  • Global time entry page at /zeit with weekly view
  • Quick entry: case selector + duration + description (one-line form)
  • Activity type dropdown for categorization
  • Monthly summary view grouped by case

Running timer:

  • Store active timer in localStorage (case_id, start_time)
  • Show elapsed time in header
  • On stop: pre-fill a time entry creation form

6. RVG Fee Calculator (Gebuehrenrechner)

Why: Table stakes for any German law firm software. The RVG Gebührenrechner is what turns time tracking into invoices.

Design:

New tables:

-- RVG fee table (Anlage 2 zu § 13 RVG) — versioned for updates
CREATE TABLE kanzlai.rvg_fee_table (
    id              SERIAL PRIMARY KEY,
    version         TEXT NOT NULL,                  -- '2025-06-01' (KostBRAeG 2025)
    value_up_to     NUMERIC(12,2) NOT NULL,         -- Gegenstandswert bis
    base_fee        NUMERIC(10,2) NOT NULL,         -- Gebühr
    valid_from      DATE NOT NULL,
    valid_to        DATE                            -- NULL = current
);

-- Fee type catalog (VV-Nummern)
CREATE TABLE kanzlai.rvg_fee_types (
    id              SERIAL PRIMARY KEY,
    vv_number       TEXT NOT NULL,                  -- 'VV 3100', 'VV 3104', etc.
    name            TEXT NOT NULL,                  -- 'Verfahrensgebühr 1. Instanz'
    rate            NUMERIC(4,2) NOT NULL,          -- Gebührensatz (e.g., 1.3)
    rate_min        NUMERIC(4,2),                   -- For Rahmengebühren (range fees)
    rate_max        NUMERIC(4,2),
    context         TEXT,                           -- 'civil_first_instance', 'out_of_court', etc.
    category        TEXT NOT NULL                   -- 'procedural', 'hearing', 'settlement', 'business'
);

-- Invoices
CREATE TABLE kanzlai.invoices (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID NOT NULL,
    case_id         UUID NOT NULL REFERENCES kanzlai.cases(id),
    invoice_number  TEXT NOT NULL,                  -- Sequential: RE-2026-001
    recipient_party_id UUID REFERENCES kanzlai.parties(id),
    invoice_date    DATE NOT NULL,
    due_date        DATE,
    gegenstandswert NUMERIC(12,2),                  -- Dispute value
    fee_items       JSONB NOT NULL,                 -- Array of {vv_number, description, rate, base_fee, amount}
    expenses        JSONB,                          -- Array of {description, amount}
    subtotal        NUMERIC(10,2) NOT NULL,
    vat_rate        NUMERIC(4,2) NOT NULL DEFAULT 19.00,
    vat_amount      NUMERIC(10,2) NOT NULL,
    total           NUMERIC(10,2) NOT NULL,
    status          TEXT NOT NULL DEFAULT 'draft',  -- 'draft', 'sent', 'paid', 'cancelled'
    paid_at         TIMESTAMPTZ,
    notes           TEXT,
    created_by      UUID NOT NULL,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

RVG calculation logic (Go service):

Input: Gegenstandswert, []VV-Nummer
Output: fee breakdown with individual items + total

1. Look up base_fee for Gegenstandswert in rvg_fee_table
   - Linear interpolation for values between steps
   - Above 500,000: +192 EUR per 50,000 EUR increment
2. For each VV-Nummer:
   a. Look up rate from rvg_fee_types
   b. Calculate: base_fee × rate = fee amount
3. Apply Anrechnungsvorschriften:
   - VV 2300 (Geschäftsgebühr) credited against VV 3100 (Verfahrensgebühr) up to 0.75×
4. Add Auslagen:
   - Postpauschale: 20% of total fees, max 20 EUR (VV 7002)
   - Dokumentenpauschale: optional per-page calculation
   - Custom expenses (travel, etc.)
5. Calculate 19% USt on everything
6. Return structured breakdown

API:

  • POST /api/rvg/calculate — calculate fees from Gegenstandswert + VV-Nummern
  • GET /api/invoices?case_id=X&status=Y — list invoices
  • POST /api/invoices — create invoice (from RVG calculation or manual)
  • PUT /api/invoices/{id} — update draft invoice
  • POST /api/invoices/{id}/send — mark as sent (+ email to client if portal enabled)
  • POST /api/invoices/{id}/pay — mark as paid

Frontend:

  • Fee calculator page at /abrechnung/rechner — input Gegenstandswert, select VV numbers, see breakdown
  • Invoice list at /abrechnung with status filters
  • Invoice creation wizard: select case → choose billing method (RVG or hourly) → review → save
  • Invoice PDF generation (server-side, using Go HTML→PDF or LaTeX template)
  • Case detail: "Abrechnung" tab showing invoices + unbilled time entries

7. DATEV Export (DATEV-Schnittstelle)

Why: Every German law firm has a Steuerberater who needs DATEV-format data. Without this export, the firm needs to manually re-enter financial data — a dealbreaker.

Design:

EXTF format export: Generate Buchungsstapel CSV files from invoices.

Tenant settings additions:

{
  "datev": {
    "beraternummer": "12345",
    "mandantennummer": "67890",
    "kontenrahmen": "SKR04",
    "wirtschaftsjahr_beginn": "01.01",
    "erloes_konto": "4400",
    "forderungen_konto": "1200"
  }
}

API:

  • POST /api/export/datev — generate EXTF file for a date range
    • Input: { from: "2026-01-01", to: "2026-03-31" }
    • Output: CSV file download
  • Each invoice maps to one Buchungssatz:
    • Umsatz = invoice total
    • Soll/Haben = 'S'
    • Konto = Forderungskonto (1200)
    • Gegenkonto = Erlöskonto (4400)
    • Belegdatum = invoice_date
    • Belegfeld 1 = invoice_number
    • Buchungstext = case_number + " " + party name

Frontend:

  • Export button in /abrechnung page
  • Date range picker + download button
  • Settings page: DATEV configuration section

P1: Should-Have Features

8. Document Templates (Schriftsatz-Vorlagen)

Why: Lawyers write the same document types repeatedly. Templates with auto-fill from case data save hours per case.

Design:

New table: kanzlai.document_templates

CREATE TABLE kanzlai.document_templates (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    name        TEXT NOT NULL,                  -- 'Klageerwiderung UPC', 'Mahnschreiben'
    category    TEXT NOT NULL,                  -- 'schriftsatz', 'brief', 'intern', 'vertrag'
    content     TEXT NOT NULL,                  -- Markdown/HTML with {{variable}} placeholders
    variables   JSONB NOT NULL DEFAULT '[]',    -- [{name: "mandant", label: "Mandant", source: "party.plaintiff"}]
    language    TEXT NOT NULL DEFAULT 'de',
    is_active   BOOLEAN NOT NULL DEFAULT true,
    created_by  UUID,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

Template variables (auto-filled from case data):

Variable Source Example
{{mandant}} Party with role 'plaintiff' or 'defendant' (depending on side) "TechCorp GmbH"
{{gegner}} Opposing party "PatentTroll AG"
{{az}} Case number "UPC_CFI_123/2026"
{{gericht}} Court "UPC München Local Division"
{{gericht_az}} Court reference "ACT_789456/2026"
{{anwalt}} Current user's name "RA Dr. Max Mustermann"
{{datum}} Today's date "28. März 2026"
{{kanzlei}} Tenant name "Mustermann & Partner"
{{naechste_frist}} Next deadline title + date "Statement of Defence — 15.04.2026"

API:

  • GET /api/templates — list templates
  • POST /api/templates — create template
  • PUT /api/templates/{id} — update template
  • POST /api/templates/{id}/render?case_id=X — render template with case data, return filled document
  • POST /api/templates/{id}/render-pdf?case_id=X — render and convert to PDF

Frontend:

  • Template management page at /vorlagen
  • Template editor with live preview and variable insertion toolbar
  • "Schriftsatz erstellen" button on case detail page → select template → review filled version → download as PDF or save as document

9. beA Integration (Elektronisches Anwaltspostfach)

Why: Since January 2022, German lawyers must use beA for all court filings. It's the primary communication channel with courts. Without beA, the firm still needs RA-MICRO just for filing.

Design:

Integration path: Use the bea.expert REST API — a third-party wrapper that provides ~40 REST endpoints, avoiding the BRAK Java KSW toolkit dependency.

New tables:

-- beA account configuration (per-user, per-tenant)
CREATE TABLE kanzlai.bea_accounts (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    user_id     UUID NOT NULL,
    safe_id     TEXT NOT NULL,              -- SAFE-ID of the lawyer's mailbox
    display_name TEXT NOT NULL,             -- "RA Dr. Max Mustermann"
    config      JSONB NOT NULL DEFAULT '{}', -- API endpoint, auth config (encrypted)
    is_active   BOOLEAN NOT NULL DEFAULT true,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- beA messages (cached locally for search and case linking)
CREATE TABLE kanzlai.bea_messages (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID NOT NULL,
    bea_account_id  UUID NOT NULL REFERENCES kanzlai.bea_accounts(id),
    bea_message_id  TEXT NOT NULL,          -- ID from beA system
    direction       TEXT NOT NULL,          -- 'inbound', 'outbound'
    sender_safe_id  TEXT NOT NULL,
    recipient_safe_id TEXT NOT NULL,
    subject         TEXT NOT NULL,
    body            TEXT,
    attachments     JSONB,                  -- [{name, size, mime_type, storage_path}]
    case_id         UUID REFERENCES kanzlai.cases(id),  -- linked case
    status          TEXT NOT NULL DEFAULT 'new', -- 'new', 'read', 'processed', 'filed'
    received_at     TIMESTAMPTZ NOT NULL,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

Workflow:

  1. Configuration: User enters beA credentials in settings, system validates via bea.expert API
  2. Inbox sync: Background job polls PostboxOverview every N minutes, caches new messages
  3. Message view: User sees beA inbox within KanzlAI, can read messages and download attachments
  4. Case linking: User can link a beA message to a case (drag-and-drop or dropdown). Attachments auto-import as case documents.
  5. Sending: Compose a message (or use a template), attach documents from case file, send via bea.expert API. Only PDF/TIFF attachments per ERVV.
  6. eEB (elektronische Empfangsbekenntnis): Handle structured acknowledgments of receipt (XJustiz format)

API:

  • GET /api/bea/inbox — list cached beA messages
  • GET /api/bea/messages/{id} — get message with attachments
  • POST /api/bea/messages/{id}/link — link message to case
  • POST /api/bea/send — send message via beA
  • POST /api/bea/sync — trigger manual sync

Frontend:

  • beA inbox page at /bea — email-style list with case assignment
  • Compose view with template selection and PDF-only attachment validation
  • Case detail: "beA" tab showing linked messages
  • Settings: beA account configuration

Licensing: bea.expert requires a commercial license. Budget for this.


10. Full-Text Search (Volltextsuche)

Why: Law firms accumulate thousands of documents and notes. Without search, finding information means clicking through cases one by one.

Design:

PostgreSQL tsvector approach (no external search engine needed):

-- Search index materialized from multiple tables
CREATE MATERIALIZED VIEW kanzlai.search_index AS
SELECT
    c.tenant_id,
    'case' AS entity_type,
    c.id AS entity_id,
    c.case_number || ' ' || c.title || ' ' || COALESCE(c.ai_summary, '') AS content,
    to_tsvector('german', c.case_number || ' ' || c.title || ' ' || COALESCE(c.ai_summary, '')) AS search_vector
FROM kanzlai.cases c
UNION ALL
SELECT
    p.tenant_id, 'party', p.id,
    p.name || ' ' || COALESCE(p.representative, ''),
    to_tsvector('german', p.name || ' ' || COALESCE(p.representative, ''))
FROM kanzlai.parties p
UNION ALL
SELECT
    d.tenant_id, 'deadline', d.id,
    d.title || ' ' || COALESCE(d.description, '') || ' ' || COALESCE(d.notes, ''),
    to_tsvector('german', d.title || ' ' || COALESCE(d.description, '') || ' ' || COALESCE(d.notes, ''))
FROM kanzlai.deadlines d
UNION ALL
SELECT
    n.tenant_id, 'note', n.id,
    n.content,
    to_tsvector('german', n.content)
FROM kanzlai.notes n;

CREATE INDEX idx_search_vector ON kanzlai.search_index USING gin(search_vector);
CREATE INDEX idx_search_tenant ON kanzlai.search_index(tenant_id);

Refresh the materialized view periodically or on relevant mutations.

API:

  • GET /api/search?q=TERM&type=case,party,deadline — cross-entity search with type filtering
  • Returns: entity type, entity ID, title/name, highlighted snippet, relevance score

Frontend:

  • Global search bar in header (Ctrl+K shortcut)
  • Results grouped by entity type
  • Click result to navigate to detail page
  • Search suggestions from recent searches

11. Reporting & Analytics (Berichte)

Why: Partners need to see: Are we meeting deadlines? How is workload distributed? Which cases are profitable? Without reporting, the software is a filing cabinet, not a management tool.

Design:

Report types:

Report Description Audience
Fristeneinhaltung Deadline compliance rate — % completed on time vs overdue Partners
Arbeitsbelastung Hours by user, by case, by period — who's overloaded? Partners
Fallstatistik Cases by status, type, court — pipeline view Partners
Umsatzbericht Revenue by case, client, period — from invoices Partners
Aktivitätsbericht Actions per user per period — productivity view Partners
Fälligkeitsvorschau Upcoming deadlines across all cases — planning view All

API:

  • GET /api/reports/deadline-compliance?from=DATE&to=DATE — deadline stats
  • GET /api/reports/workload?from=DATE&to=DATE&group_by=user|case — time entry aggregation
  • GET /api/reports/case-stats?from=DATE&to=DATE — case pipeline metrics
  • GET /api/reports/revenue?from=DATE&to=DATE&group_by=case|month — invoice aggregation

Frontend:

  • Reports page at /berichte — dashboard-style with charts
  • Date range filter + grouping options
  • Charts: bar charts for workload, line charts for trends, pie charts for distribution
  • Export to CSV/PDF for offline sharing
  • Chart library: lightweight — Recharts or Chart.js (via react-chartjs-2)

12. Wiedervorlagen (Follow-Up System)

Why: A fundamental pattern in German law firm workflow — "remind me about this case/document on date X." Different from deadlines: Wiedervorlagen are internal reminders, not procedural deadlines.

Design:

New table: kanzlai.follow_ups

CREATE TABLE kanzlai.follow_ups (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    case_id     UUID REFERENCES kanzlai.cases(id),
    user_id     UUID NOT NULL,              -- who should be reminded
    title       TEXT NOT NULL,
    description TEXT,
    due_date    DATE NOT NULL,
    priority    TEXT NOT NULL DEFAULT 'normal', -- 'low', 'normal', 'high', 'urgent'
    status      TEXT NOT NULL DEFAULT 'pending', -- 'pending', 'done', 'snoozed'
    snoozed_to  DATE,                        -- if snoozed, new date
    created_by  UUID NOT NULL,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    completed_at TIMESTAMPTZ
);

Integration with notifications: The notification worker checks follow_ups alongside deadlines.

Frontend:

  • Quick-create button on any entity detail page: "Wiedervorlage"
  • Follow-up list at /wiedervorlagen with date filtering
  • Morning email digest: today's follow-ups alongside today's deadlines

13. Email Integration (E-Mail-Anbindung)

Why: Most client and opponent communication happens via email. Without email integration, the firm operates in two separate worlds — the email client and KanzlAI.

Design:

Approach: IMAP-based email import (not a full email client — that's scope creep).

New tables:

CREATE TABLE kanzlai.email_accounts (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    user_id     UUID NOT NULL,
    email       TEXT NOT NULL,
    imap_host   TEXT NOT NULL,
    imap_port   INTEGER NOT NULL DEFAULT 993,
    smtp_host   TEXT,
    smtp_port   INTEGER DEFAULT 587,
    username    TEXT NOT NULL,
    password    TEXT NOT NULL,               -- encrypted at rest
    is_active   BOOLEAN NOT NULL DEFAULT true,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE kanzlai.emails (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID NOT NULL,
    email_account_id UUID NOT NULL REFERENCES kanzlai.email_accounts(id),
    message_id      TEXT NOT NULL,           -- RFC 2822 Message-ID
    direction       TEXT NOT NULL,           -- 'inbound', 'outbound'
    from_address    TEXT NOT NULL,
    to_addresses    TEXT[] NOT NULL,
    cc_addresses    TEXT[],
    subject         TEXT NOT NULL,
    body_text       TEXT,
    body_html       TEXT,
    attachments     JSONB,
    case_id         UUID REFERENCES kanzlai.cases(id),
    received_at     TIMESTAMPTZ NOT NULL,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

Workflow:

  1. Configure IMAP account in settings
  2. Background sync fetches new emails periodically
  3. User views emails in KanzlAI and links them to cases
  4. Auto-suggestion: match sender/recipient against party contacts to suggest case assignment
  5. Email attachments can be saved as case documents

Frontend:

  • Email page at /email — inbox-style list
  • Case detail: "E-Mail" tab showing linked emails
  • "Zur Akte" button on each email to link to a case
  • Settings: email account configuration

P2: Differentiator Features (UPC-Specific Intelligence)

These features leverage m's existing infrastructure (youpcms, mLex, youpc.org data) to create an unbeatable competitive advantage for UPC patent litigation.

14. Patent Family Tracking

Why: UPC cases revolve around patents. A case management system for patent litigation that doesn't track patents is like a hospital system that doesn't track patients.

Design:

New tables:

CREATE TABLE kanzlai.patents (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID NOT NULL,
    patent_number   TEXT NOT NULL,           -- 'EP1234567'
    title           TEXT,
    applicant       TEXT,
    filing_date     DATE,
    grant_date      DATE,
    expiry_date     DATE,
    status          TEXT NOT NULL DEFAULT 'active', -- 'active', 'expired', 'withdrawn', 'revoked'
    patent_type     TEXT,                    -- 'EP', 'UP', 'DE', 'US', 'WO'
    opt_out_status  TEXT,                    -- 'opted_out', 'not_opted_out', 'opt_out_withdrawn'
    opt_out_date    DATE,
    claims_count    INTEGER,
    abstract        TEXT,
    metadata        JSONB,                   -- Classifications, designations, priority data
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Patent family relationships
CREATE TABLE kanzlai.patent_family (
    parent_patent_id UUID NOT NULL REFERENCES kanzlai.patents(id),
    child_patent_id  UUID NOT NULL REFERENCES kanzlai.patents(id),
    relationship     TEXT NOT NULL,          -- 'national_validation', 'divisional', 'continuation', 'priority'
    PRIMARY KEY (parent_patent_id, child_patent_id)
);

-- Link patents to cases
CREATE TABLE kanzlai.case_patents (
    case_id     UUID NOT NULL REFERENCES kanzlai.cases(id) ON DELETE CASCADE,
    patent_id   UUID NOT NULL REFERENCES kanzlai.patents(id),
    role        TEXT NOT NULL,              -- 'asserted', 'challenged', 'prior_art'
    PRIMARY KEY (case_id, patent_id)
);

Integration with youpc.org/EPO:

  • Auto-lookup patent data from EPO Open Patent Services (OPS) API by patent number
  • Import patent family tree from EPO
  • Track opt-out status from UPC opt-out register

Frontend:

  • Case detail: "Patente" tab showing linked patents with family tree visualization
  • Patent detail page showing full patent info + all cases where it appears
  • Auto-fill case data when a patent number is entered

15. Claim Chart Management

Why: Claim charts (Merkmalsgliederung) are the core analytical tool in patent litigation — mapping claim elements to accused products or prior art. No existing Kanzleimanagement system handles this.

Design:

New tables:

CREATE TABLE kanzlai.claim_charts (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    case_id     UUID NOT NULL REFERENCES kanzlai.cases(id),
    patent_id   UUID REFERENCES kanzlai.patents(id),
    title       TEXT NOT NULL,              -- 'Claim 1 Infringement Analysis'
    chart_type  TEXT NOT NULL,              -- 'infringement', 'invalidity', 'comparison'
    status      TEXT NOT NULL DEFAULT 'draft',
    created_by  UUID,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE kanzlai.claim_chart_entries (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    chart_id        UUID NOT NULL REFERENCES kanzlai.claim_charts(id) ON DELETE CASCADE,
    claim_element   TEXT NOT NULL,          -- 'a mounting bracket configured to...'
    element_number  TEXT,                   -- '1a', '1b', '1c'
    evidence        TEXT,                   -- 'Accused product uses X-shaped bracket (see Exhibit 3, p.12)'
    assessment      TEXT,                   -- 'literally_met', 'equivalent', 'not_met', 'unclear'
    documents       JSONB,                  -- [{document_id, page, highlight}]
    sort_order      INTEGER NOT NULL,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

Frontend:

  • Case detail: "Claim Charts" tab
  • Two-column view: claim element (left) | evidence (right)
  • Color-coded assessment: green (met), yellow (equivalent/unclear), red (not met)
  • Drag-drop reordering of elements
  • Link evidence to uploaded documents with page references
  • Export to Word/PDF for court submissions

16. UPC Case Intelligence (mLex Integration)

Why: m has 1,600+ structured UPC decisions with holdings, legal principles, citation networks, and division analytics. No competitor has this data. This turns KanzlAI from "case file management" into "case strategy intelligence."

Design:

Integration approach: Connect to the existing youpc.org/mLex Supabase database (same flexsiebels instance) via cross-schema queries or a dedicated API endpoint.

Features:

A) Similar Case Finder

  • Input: case type, court, patent classification, key issues
  • Output: relevant UPC decisions from the mLex database, ranked by similarity
  • Uses pgvector embeddings on holdings + legal principles for semantic search
  • "Wie hat die Münchner Lokalkammer bisher über Softwarepatente entschieden?"

B) Division Analytics

  • Per-division statistics: success rates, average timeline, judge panel compositions
  • "München grants PI in 40% of cases, median time to hearing: 8 months"
  • Updated automatically as new decisions are scraped

C) Legal Principle Lookup

  • Search legal principles extracted from UPC case law
  • "Show me all decisions on claim construction of functional features"
  • Returns structured holdings with citations

D) Precedent Suggestions

  • When drafting a Schriftsatz, suggest relevant precedents based on case type and issues
  • AI-powered: Claude analyzes the case context and retrieves relevant mLex decisions

API:

  • GET /api/upc/similar-cases?case_id=X — find similar cases
  • GET /api/upc/division-stats?division=MUNICH — division analytics
  • GET /api/upc/principles?query=TERM — search legal principles
  • POST /api/upc/suggest-precedents — AI-powered precedent suggestion

Frontend:

  • Case detail: "UPC Intelligence" tab (only for UPC-type cases)
  • Global UPC research page at /upc-recherche
  • Division comparison tool (already partially exists in youpc.org)

17. AI Document Drafting

Why: Combine case data + templates + legal intelligence to generate first drafts. This is the "wow factor" that gets users to switch from RA-MICRO.

Design:

Workflow:

  1. User selects a document type (e.g., "Statement of Defence") and a case
  2. System loads: case data, parties, patent info, relevant deadlines, linked documents, relevant precedents from mLex
  3. Claude generates a structured first draft using the template + case context
  4. User reviews and edits in a rich text editor
  5. Export to PDF for filing

API:

  • POST /api/ai/draft — generate document draft
    • Input: { case_id, template_id, instructions, include_precedents: true }
    • Output: structured document in Markdown/HTML
  • Rate-limited: 3 requests/minute (more expensive than extraction)

Frontend:

  • "KI-Entwurf" button on the template rendering page
  • Split view: generated draft (left) | case context (right)
  • Edit capabilities before saving as document

Privacy consideration: Case data sent to Claude API. Tenants must opt-in, with clear data processing agreement. Consider self-hosted Claude via AWS Bedrock for sensitive firms.


18. AI Case Strategy Analysis

Why: Beyond document management — help lawyers think about their cases. "What are the strengths and weaknesses of our position?"

Design:

AI analysis types:

Analysis Input Output
Strengths/Weaknesses Case data + claim charts Bullet-point analysis with confidence
Timeline Prediction Case type + division Estimated timeline based on historical data
Cost Estimate Case type + Gegenstandswert Estimated legal costs (own + opponent)
Strategy Options Full case data Recommended approaches with pros/cons

API:

  • POST /api/ai/analyze-strategy — comprehensive case analysis
    • Input: { case_id, analysis_type }
    • Output: structured analysis with sections

Frontend:

  • Case detail: "KI-Analyse" tab
  • Analysis results cached and versioned (re-run as case evolves)
  • Each analysis shows the date it was generated and what data it was based on

P3: Nice-to-Have Features

19. Client Portal (Mandantenportal)

External-facing view where clients can see the status of their cases.

Design:

  • Separate authentication (email + password, no Supabase Auth dependency)
  • Read-only access to assigned cases: status, upcoming deadlines, recent events
  • Secure document exchange: client can upload documents, lawyer can share documents
  • Invoice viewing and payment status
  • Accessible via subdomain: portal.kanzlai.msbls.de

Data model: Use the extern role from the permissions system. Client portal users are linked to specific cases via case_assignments.


20. Mobile Optimization / PWA

Design:

  • Progressive Web App with offline capability for viewing case data
  • Service worker for push notifications (deadline reminders)
  • Optimized touch targets and mobile layouts (already partially done with responsive Tailwind)
  • Add manifest.json with installable app metadata
  • Minimal — not a native app, but installable from browser

21. Multi-Language Support (Mehrsprachigkeit)

Design:

  • next-intl for frontend internationalization
  • German primary, English for UPC work
  • Language selector in settings
  • Template system already supports per-template language
  • Backend API responses use language-neutral keys; frontend maps to locale

22. EDA Integration (Elektronisches Mahnverfahren)

Design:

  • Generate EDA-format files for electronic dunning proceedings
  • Submit to the relevant Mahngericht
  • Track Mahnbescheid and Vollstreckungsbescheid status
  • Lower priority — UPC firms rarely do Mahnverfahren

23. Advanced Integrations

Integration Priority Effort Notes
Microsoft 365 Calendar P3 Medium Alternative to CalDAV for firms using Outlook
Google Workspace P3 Medium Alternative auth + calendar
Schufa/Creditreform P3 High Credit checks on parties — requires contracts
EPO OPS API P2 Medium Auto-fetch patent data by number
DPMA DPMAregister P3 Medium German patent/trademark status
Slack/Teams webhooks P3 Low Notification delivery alternative

Data Model Summary: New Tables

Table Priority Purpose
audit_log P0 Append-only change history
conflict_checks P0 Conflict of interest check results
permissions P0 Role-action permission matrix
notifications P0 In-app notification inbox
notification_preferences P0 Per-user notification settings
case_assignments P0 Who works on which case
time_entries P0 Billable time tracking
hourly_rates P0 Per-user billing rates
rvg_fee_table P0 RVG Gebührentabelle (reference)
rvg_fee_types P0 VV-Nummern catalog (reference)
invoices P0 Fee invoices
follow_ups P1 Wiedervorlagen
document_templates P1 Schriftsatz templates
bea_accounts P1 beA mailbox config
bea_messages P1 Cached beA messages
email_accounts P1 IMAP email config
emails P1 Cached emails linked to cases
patents P2 Patent tracking
patent_family P2 Patent family relationships
case_patents P2 Case-patent linking
claim_charts P2 Patent claim analysis
claim_chart_entries P2 Claim chart rows
party_relationships P1 Corporate structures for conflict checks
search_index P1 Full-text search materialized view

Total new tables: 24 (on top of existing 12)


Implementation Phases

Phase 1: Foundation (P0 core — audit, permissions, notifications)

Goal: Make the existing MVP production-safe.

Task Effort Dependencies
Audit trail table + middleware 3-4 days None
Role-based permissions (extend existing roles) 2-3 days None
Case assignments table + UI 1-2 days None
Notification system (in-app + email) 3-4 days Case assignments
Notification preferences UI 1 day Notifications

Deliverable: Every change is logged. Users have proper roles. Deadlines trigger reminders.

Phase 2: Money (P0 — time tracking, RVG, invoicing, DATEV)

Goal: The billing pipeline — from time entry to DATEV export.

Task Effort Dependencies
Time entries table + CRUD + API 2-3 days None
Timer UI (header widget + case tab) 2 days Time entries API
Hourly rates configuration 1 day None
RVG fee table + calculator service 2-3 days None
RVG calculator UI 1-2 days Calculator service
Invoice model + CRUD + API 2-3 days RVG calculator
Invoice creation wizard UI 2-3 days Invoice API
Invoice PDF generation 1-2 days Invoice model
DATEV EXTF export 1-2 days Invoices

Deliverable: Complete billing pipeline. Lawyers can track time, calculate fees, generate invoices, export to DATEV.

Phase 3: Compliance (P0 — conflict checks)

Goal: Statutory compliance for conflict of interest.

Task Effort Dependencies
pg_trgm extension + party search index 1 day None
Conflict check service (exact + fuzzy) 2-3 days None
Conflict check on case creation (auto-trigger) 1 day Conflict service
Conflict check UI (results + clearance) 2 days Conflict API
Cologne phonetic matching 1-2 days Conflict service

Deliverable: Automatic conflict checking on every new case/party. Auditable clearance workflow.

Phase 4: Communication (P1 — beA, email, templates)

Goal: KanzlAI becomes the central communication hub.

Task Effort Dependencies
Document template engine 3-4 days None
Template editor UI 2-3 days Template engine
Full-text search (materialized view + API) 2-3 days None
Search UI (global search bar) 1-2 days Search API
Follow-up (Wiedervorlagen) system 2 days Notifications
beA integration (bea.expert API) 5-7 days License agreement
Email integration (IMAP sync) 4-5 days None

Deliverable: Templates, search, beA, email — all communication flows through KanzlAI.

Phase 5: UPC Intelligence (P2 — the differentiator)

Goal: No competitor can match this.

Task Effort Dependencies
Patent model + family tracking 2-3 days None
EPO OPS API integration (auto-fetch patent data) 2-3 days Patent model
Claim chart management 3-4 days Patent model
mLex cross-schema queries 2-3 days Existing mLex data
Similar case finder 2-3 days mLex integration
Division analytics dashboard 2-3 days mLex integration
AI document drafting 3-4 days Templates + mLex
AI strategy analysis 2-3 days mLex + case data

Deliverable: UPC-specific intelligence layer. Patent tracking, claim charts, case law search, AI strategy.

Phase 6: Polish (P3)

Goal: Professional finish.

Task Effort Dependencies
Client portal 5-7 days Invoices, case assignments
PWA + push notifications 2-3 days Notification system
Multi-language (DE/EN) 3-4 days All UI pages
Reporting dashboard 3-4 days Time entries, invoices
EDA Mahnverfahren 3-4 days Invoices

New Frontend Routes (Complete)

Existing:
  /dashboard
  /cases, /cases/new, /cases/[id]/*
  /fristen, /fristen/neu, /fristen/rechner, /fristen/[id]
  /termine, /termine/neu, /termine/[id]
  /ai/extract
  /einstellungen, /einstellungen/team

New (P0):
  /protokoll                    Audit log viewer (admin)
  /zeit                         Time tracking overview (weekly view)
  /abrechnung                   Invoice list
  /abrechnung/rechner           RVG fee calculator
  /abrechnung/[id]              Invoice detail
  /kollision                    Manual conflict check
  /benachrichtigungen           Notification center

New (P1):
  /vorlagen                     Document template management
  /vorlagen/[id]                Template editor
  /bea                          beA inbox
  /bea/compose                  beA compose message
  /email                        Email inbox
  /wiedervorlagen               Follow-up list
  /suche                        Full search results page

New (P2):
  /upc-recherche                UPC case law research
  /upc-recherche/divisionen     Division analytics
  /patente                      Patent list
  /patente/[id]                 Patent detail + family tree

New (P3):
  /berichte                     Reporting dashboard
  portal.*                      Client portal (separate app/subdomain)

Architecture Decisions

1. Stay on Go + Next.js

The current stack is solid. No framework migration needed. Go handles the backend complexity well (concurrent CalDAV sync, notification workers, beA polling). Next.js App Router is mature enough for the frontend.

2. Stay on Supabase

All new tables go in the kanzlai schema. The flexsiebels instance already has the mLex data in another schema — cross-schema queries enable the UPC intelligence layer without data duplication.

3. Background Workers as Goroutines

The CalDAV sync pattern (background goroutine with interval) extends to: notification dispatch, beA polling, email sync, search index refresh. Keep them in the same Go binary — no need for a separate worker process yet.

4. PDF Generation

Use Go's html/template → HTML → wkhtmltopdf (or chromedp headless) for invoice and document PDF generation. Keep it server-side.

5. beA via bea.expert

The bea.expert REST API is the only sane integration path. BRAK's Java KSW toolkit is not viable for a modern web app.

6. No External Search Engine

PostgreSQL full-text search with tsvector + pg_trgm is sufficient for the data volumes a single law firm generates. No Elasticsearch/Meilisearch needed — reduces operational complexity.


Competitive Positioning

Feature RA-MICRO Advoware Kleos KanzlAI
General Kanzleimanagement Full Full Full Full (after P0-P1)
beA Integration Yes Yes Yes Yes (P1)
RVG Calculator Yes Yes Yes Yes (P0)
DATEV Export Yes Yes Yes Yes (P0)
Cloud-Native Partial Partial Yes Yes
UPC Deadline Rules No No No Yes (already shipped)
UPC Case Law Database No No No Yes (P2)
AI Deadline Extraction No No No Yes (already shipped)
AI Document Drafting Partial No No Yes (P2)
Patent Family Tracking No No No Yes (P2)
Claim Chart Management No No No Yes (P2)
Division Analytics No No No Yes (P2)
Multi-Tenant SaaS No No Yes Yes (already shipped)

Tagline: "Die einzige Kanzleisoftware, die UPC-Patentprozesse versteht."


Risk Register

Risk Impact Mitigation
beA API changes / bea.expert discontinuation High Abstract beA behind an interface; monitor BRAK announcements
RVG table updates (new Kostenrechtsänderungsgesetz) Medium Versioned fee table with effective dates; easy to update
DSGVO compliance for AI features (data sent to Claude) High Offer self-hosted Claude via Bedrock; explicit tenant opt-in
Scope creep on P2 features Medium Ship P0+P1 first; P2 only after core is solid
Single-developer bottleneck High Clean architecture, good docs, modular services
UPC procedural rules change Low Rules in database, not hardcoded; already have update pattern

Success Metrics

Metric Target How to Measure
Deadline compliance rate > 99% No overdue deadlines that weren't caught by reminders
Time from case creation to first deadline < 5 min AI extraction + deadline calculator
Invoice generation time < 2 min RVG calculator + template
Conflict check time < 3 sec Automated on case creation
User adoption 1 firm within 3 months of P0 Direct outreach to patent firms
Feature parity with competitors P0+P1 complete Checklist comparison

Appendix: Prior Design Documents

  • DESIGN-dashboard-redesign.md — t-kz-060: Dashboard interactivity + detail pages (implemented)
  • This document (ROADMAP.md) — t-kz-070: Full system vision

This roadmap is a living document. Features will be re-prioritized as user feedback comes in. The UPC intelligence layer (P2) is the strategic differentiator — but P0 must ship first to establish credibility as a serious Kanzleimanagement system.