mAi 94310ba498
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
feat(submissions): Composer Slice E — specialist bases + base-swap content survival (m/paliad#141)
Two new firm-agnostic base templates + the generic generator that
produced them + a regression test pinning Q10's base-swap-content-
survival contract.

Mig 150: seeds two `submission_bases` rows with firm=NULL.

- lg-duesseldorf — proceeding_family='de.inf.lg'. Conservative
  German legal style: Times New Roman 11pt; plain black headings.
  Stylemap targets LG-Body / LG-Heading1..3 / LG-ListBullet /
  LG-ListNumber / LG-Quote.

- upc-formal — proceeding_family='upc.inf.cfi'. UPC court style:
  Calibri 11pt body; UPC-blue (#1F3864) headings; Cambria italic
  for blockquotes. Stylemap targets UPC-Body / UPC-Heading1..3 / …

Both rows ship the same 10-section spec.defaults shape as the Slice A
bases (letterhead → signature) with their own seed Markdown.

scripts/gen-submission-base/main.go (NEW, ~240 LoC):

- Generic generator with -preset flag. Two presets baked in
  (lg-duesseldorf + upc-formal). Each preset hard-codes typography
  (font, sizes, colour) so the lawyer can swap between bases and
  see chrome change while section content carries through unchanged.
- Output is byte-reproducible (zip mtime pinned to 2026-05-26 UTC).
- Emits a minimal Composer-mode .docx: [Content_Types].xml,
  _rels/.rels, word/_rels/document.xml.rels (empty envelope so the
  composer's hyperlink-rels patch from Slice D has somewhere to land),
  word/styles.xml (preset's full named-style block + "Hyperlink"
  character style for Slice D link runs), word/document.xml (anchor-
  only body in §6.1 default section order).

Gitea uploads (via mAi):

- 6 - material/Templates/Word/Paliad/Composer/lg-duesseldorf.docx
  blob SHA: 82f57b3cb3b54c755fc5ab36862bfd61b8aaa73e
- 6 - material/Templates/Word/Paliad/Composer/upc-formal.docx
  blob SHA: 41b9a388263ccc43ddc28b55caab301a4cf74fe8

These live under Composer/ (not under HLC/) so a future non-HLC
deployment serves the same cross-firm files.

Backend wiring:

- internal/handlers/files.go: two new fileRegistry entries
  (composerBaseLGDuesseldorfSlug, composerBaseUPCFormalSlug) +
  matching slugs in composerBaseSlugMap so fetchComposerBaseBytes
  routes the new catalog rows to the new Gitea objects.

Tests:

- TestComposer_BaseSwapPreservesContent — composes the same draft
  against an HLC-style stylemap AND an LG-style stylemap; asserts
  (a) content survives both ways, (b) each output carries the
  correct stylemap-entry stylenames, (c) neither output leaks the
  other's stylenames. Pins Q10's base-swap-survives-content
  contract.

Build hygiene: go build/vet/test -short clean (all packages);
bun run build clean.

NOT in scope (Slice E's brief was specialist bases + survival test):

- Generator coverage for HL Patents Style bases — gen-hl-skeleton-
  template stays as the per-firm path (it needs the proprietary
  .dotm source). gen-submission-base is for firm-agnostic bases.
- LG-Düsseldorf-court-style-guide deep fidelity — the LG preset is
  a conservative starting point; admin refines via the admin editor
  in a later slice if needed.
- numbering.xml carrying numId=1/2 — Slice D's MD walker emits
  visible "• " / "N. " prefixes that don't need numbering.xml;
  honours stylemap entry for indentation.

Hard rules honoured:

- Migration purely additive (`ON CONFLICT (slug) DO NOTHING`).
- NO behavior change for pre-Composer drafts.
- NO behavior change for existing hlc-letterhead + neutral seed
  rows.
- {{rule.X}} aliases preserved (walker passes placeholders through;
  v1 SubmissionRenderer pass substitutes).
- Q10 base-swap-content-survival pinned by new test.

t-paliad-317 Slice E
2026-05-26 20:21:12 +02:00

paliad

Paliad — all-in-one patent practice platform for HLC (formerly Hogan Lovells). Knowledge tools and Aktenverwaltung behind one sidebar.

  • Aktenverwaltung: Akten (matters), Fristen (deadlines), Termine (appointments) with CalDAV sync, Parteien, Dashboard. Office-scoped visibility with explicit collaborators.
  • Knowledge tools: Prozesskostenrechner (DE / UPC / EPA), Fristenrechner, Gebührentabellen, Patentglossar, Gerichtsverzeichnis, Checklisten, Link Hub, Downloads.

Domain: paliad.de (legacy: patholo.de, patholo.msbls.de). Repo: m/paliad on mgit.msbls.de.

Stack

  • Frontend: Bun + custom JSX/TSX renderer (no React), per-page client TS bundles, HTML-first forms
  • Backend: Go (net/http), sqlx for DB access
  • Migrations: golang-migrate/migrate/v4 with SQL files embedded via embed.FS; applied at server startup before the HTTP listener binds
  • Database: youpc Supabase Postgres, paliad schema. Office-scoped RLS (paliad.can_see_akte(akte_id)) — see docs/design-kanzlai-integration.md §2
  • Auth: Supabase password (cookie session, @hoganlovells.com / @hlc.* email gate)
  • CalDAV: hand-rolled iCal + minimal WebDAV client in internal/services/caldav_*.go; AES-GCM at rest for stored passwords
  • Hosting: Dokploy compose Zx147ycurfYagKRl_Zzyo on mlake

Database migrations

Migrations live in internal/db/migrations/ as NNN_description.up.sql + .down.sql pairs. They are embedded into the Go binary via embed.FS and applied automatically at server startup (before the HTTP listener binds) when DATABASE_URL is set.

The migration tracker is paliad.paliad_schema_migrations (not the default public.schema_migrations). This avoids a collision with other apps on the shared youpc Supabase instance — see the memory episode "paliad migration bootstrap collision with shared Postgres" for the incident that drove the change.

Current migrations (as of April 2026):

001_paliad_schema        schema + extensions
002_users                paliad.users (office, role, practice_group)
003_reference_tables     proceeding_types, deadline_rules, holidays
004_akten                paliad.akten with visibility columns
005_akten_children       parteien, fristen, termine, dokumente, akten_events, notizen
006_visibility           paliad.can_see_akte() function
007_rls_policies         RLS on every paliad table
008_seed_proceeding_types
009_seed_deadline_rules  32 UPC + 4 ZPO rules
010_seed_holidays        DE federal + UPC judicial vacations
011_feedback_tables      link_suggestions, checklisten_feedback, gerichte_feedback
012_fristenrechner_rules DB-backed rule set for /tools/fristenrechner
013_user_caldav_config   per-user CalDAV (encrypted) + sync log
014_checklist_instances  persisted checklist instances linkable to Akten

Add a new migration:

internal/db/migrations/015_<description>.up.sql
internal/db/migrations/015_<description>.down.sql

The down file is required and must reverse the up cleanly (verified by adding a one-off down test before merge).

To run migrations against a local Postgres:

docker run -d --name paliad-pg -e POSTGRES_PASSWORD=test -p 5432:5432 postgres:16-alpine
# bootstrap a mock auth schema (auth.users + auth.uid()) — required because
# the migrations reference Supabase-provided objects:
psql postgres://postgres:test@localhost:5432/postgres -f internal/db/devtools/mock_supabase_auth.sql
DATABASE_URL='postgres://postgres:test@localhost:5432/postgres?sslmode=disable' \
SUPABASE_URL=stub SUPABASE_ANON_KEY=stub \
go run ./cmd/server

Environment

Variable Required Purpose
PORT no (default 8080) HTTP listen port
SUPABASE_URL yes Supabase project URL (auth)
SUPABASE_ANON_KEY yes Supabase anon key (auth)
DATABASE_URL for Aktenverwaltung Direct Postgres conn for migrations + Akten/Fristen/Termine services. Knowledge-platform endpoints (Kostenrechner, Glossar, Links, Gebührentabellen, Checklisten, Gerichte, Downloads) don't use the pool and work without it. Aktenverwaltung endpoints return 503 if unset.
CALDAV_ENCRYPTION_KEY for CalDAV sync 32-byte AES-256 key, base64-encoded. Encrypts CalDAV passwords at rest (AES-GCM). Server fails fast on malformed key; if unset, CalDAV is silently disabled (/api/caldav-config returns 501). Generate with openssl rand -base64 32.
GITEA_TOKEN optional Gitea API token for the private file proxy (Downloads)
ANTHROPIC_API_KEY not used today Reserved for Phase H (AI Frist-Extraktion). Currently deferred — do not set.

Development

make build       # compile backend + frontend
make test        # run Go tests + frontend tests
go build ./...   # backend only
go vet ./...     # static checks
go test ./...    # Go tests
bun run build    # frontend only (produces frontend/dist/)

Project layout:

cmd/server/           # main entry point
internal/db/          # sqlx pool + embedded migrations
internal/services/    # AkteService, FristService, TerminService, CalDAV, ...
internal/handlers/    # HTTP handlers (pages + API)
internal/calc/        # Kostenrechner / Fristenrechner logic
frontend/             # Bun + TSX source; static HTML output to frontend/dist/
docs/                 # design docs + this roadmap

Deploy

Push to main → Gitea webhook → Dokploy auto-deploy on mlake.

Project status (April 2026)

Phases AG, I and J of the KanzlAI integration are shipped: schema, services, Akten, Fristen, Termine + CalDAV, Dashboard, Notizen service + UI (commit 5a9f8e5, 2026-04-17), and instanceable Checklisten (migration 014). Phase H (AI Frist extraction) is deferred pending a reversal of the "no Anthropic API" decision; the Dokumente tab on Akten detail is hidden until that lands. KanzlAI infra retirement (Dokploy shutdown, kanzlai schema drop, Gitea archive) is still pending.

See docs/feature-roadmap.md for the full backlog and docs/design-kanzlai-integration.md for the integration design.

Description
patholo.de — Patent knowledge sharing platform for HL colleagues
Readme 14 MiB
Languages
Go 49.9%
TypeScript 38%
CSS 6.7%
PLpgSQL 4.7%
HTML 0.3%
Other 0.4%