Rules anchored on uncertain triggers (R.109 backward-anchor without oral-hearing date; R.118(4) without validity decision; R.262(2) without recorded Vertraulichkeitsantrag) previously rendered concrete dates fabricated off the trigger date. Add IsConditional projection flag so the SmartTimeline + Verfahrensablauf surfaces "abhängig von <parent>" instead of a misleading date. Backend (fristenrechner.go): - Add IsConditional + ParentRuleCode/Name/NameEN to UIDeadline. - Pre-pass populates courtSet from rule.is_court_set=true BEFORE the main loop, so order-of-evaluation in sequence_order no longer matters for the parent-court-set check. Fixes R.109(1) "Antrag auf Simultanübersetzung" (sequence_order=45 < Mündliche Verhandlung's sequence_order=50): the timing='before' backward arithmetic was computing 1 month before the trigger date because the court-set parent hadn't been classified yet. - Set IsConditional=true on every IsCourtSetIndirect branch (catches R.109 backward + R.118(4) cons_orders chain off the decision). - Set IsConditional=true for priority='optional' + primary_party='both' rules whose data-model parent is the trigger anchor (covers R.262(2) confidentiality_response: the data anchors on SoC, but the real trigger is the opposing party's confidentiality motion which may never happen). Suppressed by IsOverridden so user anchors win. Backend (projection_service.go): - Add IsConditional to TimelineEvent + propagate from UIDeadline. - New Status="conditional" for projected rows; clears Date, populates DependsOnRuleCode/Name from UIDeadline.ParentRule* so the row carries the "abhängig von <parent>" payload even when the parent has no computed date for annotateDependsOn to discover. Frontend (verfahrensablauf-core.ts + CSS + i18n): - CalculatedDeadline gains isConditional + parentRule* fields. - deadlineCardHtml renders "abhängig von <parent>" chip with click-to-edit affordance in place of the date column when isConditional=true. IsConditional wins over IsCourtSet for the date column (they overlap; "abhängig von <parent>" names the specific blocker). - .timeline-item--conditional / .fr-col-item--conditional CSS: dotted border + faded text so the conditional state reads at glance. - Replaced escHtml's DOM-backed implementation with a pure-JS regex escape so the module is testable in bun test without jsdom (the old form forced fixtures to leave several fields empty just to avoid the DOM dependency). Tests: - TestApplyLookaheadCap_ConditionalRowsPassThrough: pure-function lock that conditional rows pass through applyLookaheadCap untouched (don't count against ProjectedTotal/Shown, don't get capped). - TestUIDeadline_IsConditional_UncertainAnchors (TEST_DATABASE_URL): asserts R.109(1)/(4), R.118(4) chain, and R.262(2) all render IsConditional=true with empty DueDate + populated ParentRule*; SoD stays non-conditional; override on the oral hearing flips R.109(1) back to concrete date. - 4 new bun tests for the conditional rendering branches in deadlineCardHtml. UX path verified by tests + manual review of the live rule corpus: opening a UPC inf project without oral-hearing date now surfaces R.109(1) + R.109(4) as conditional; recording the Vertraulichkeitsantrag (anchoring R.262(2) via the existing "Datum setzen" flow) flips it back to a concrete date. go build / go test / bun test / bun run build all clean.
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),sqlxfor DB access - Migrations:
golang-migrate/migrate/v4with SQL files embedded viaembed.FS; applied at server startup before the HTTP listener binds - Database: youpc Supabase Postgres,
paliadschema. Office-scoped RLS (paliad.can_see_akte(akte_id)) — seedocs/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_Zzyoon 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 A–G, 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.