t-paliad-214. Covers scope definitions, format choices (xlsx + JSON + CSV
in one zip, deterministic, schema_version 1), authorization model
(global_admin / project-team-with-associate-floor / authenticated-self),
trigger model (sync personal+project, async org), storage on
PALIAD_EXPORT_DIR with 7-day retention, PII/GDPR posture, 3-slice plan,
and 9 open questions for m. No code touches — design only.
t-paliad-213 — six-layer pyramid (migration dry-run, Go/frontend unit,
frontend DOM, service live-DB, handler integration, Playwright E2E),
audit of current coverage (323 test funcs, 24 untested services, 53
untested handlers, 4/90 frontend modules), eight-slice tracer-bullet
roll-out, six open questions for m.
Read-only design phase per CLAUDE.md inventor gate — no test files,
make targets or CI configs touched. Awaiting m go/no-go on §5 slice
plan + §6 open questions before any coder shift.
Inventor design for letting users connect Paliad's CalDAV sync to N
external calendars per user, with scope filters (master / personal /
per-project / per-client / per-litigation / per-patent / per-case)
rather than today's single-target push. Splits credentials (per user,
unchanged) from bindings (new join table). Adds a per-target join for
push state so the same Appointment can live in multiple calendars at
once. Includes per-provider limit research (iCloud 100, Google ~100,
Fastmail no cap, Nextcloud 30 default), a 4-slice rollout plan, and 6
open questions for m. READ-ONLY design — no schema or code changes.
Researcher draft for Workstream A — per-rule proposals for rule_code +
legal_source on the 130 active+published deadline_rules with rule_code IS
NULL. Grouped by proceeding (53 PT rows) and orphan-bucket (77 rows with
proceeding_type_id IS NULL).
~75 HIGH/MED proposals, ~47 FLAG entries pending m's call (court-set
event-markers, combined-pleading rows, ambiguous orphans, RoP
sub-paragraph spot-checks). Profiles the field convention from the 83
already-populated rows. READ-ONLY phase: no DB writes, no migration yet
— mig 097 follows once m signs off.
Side-fix candidate: normalize the one outlier RoP.49.1 -> RoP.049.1 on
rev.defence as part of mig 097.
Captures m's 2026-05-18 ratification of the new fristenrechner
proceeding-code convention `<jurisdiction>.<X>.<Y>` and the 5
sub-decisions: ccr.cfi is an illustrative peer that routes back to
inf.cfi with with_ccr; damages-appeal stays bundled into
upc.apl.merits; NZB at BGH is a flag, not a separate proceeding;
DPMA appeals stay generic with source differentiation at rule level.
This document is the source of truth for mig 096 (lands next) and the
post-mig proceeding_mapping.go.
m + paliadin walked the open questions; new §0.3 records the calls so
the proposal doc reflects the final shape before m ingests via
/admin/rules. Net stays at 4 new rules (2 PO + 2 always-fire merits-
appeal spawns). de_inf.erwidg flips to court-set per ZPO §276(1) S.2.
No ccr-defendant PO, no ccr.appeal duplication, no R.263 deadline.
Drafts the 4 coverage questions the mig 093 commit body left open for
legal review (t-paliad-200 closeout):
1. Preliminary Objection (RoP 19) on UPC_INF + UPC_REV — 2 new rules,
party=defendant, 1 month from SoC/SfR served, flag-gated with_po.
2. Cross-proceeding APP spawn (RoP 220.1(a)) from UPC_INF + UPC_REV
into the UPC merits-appeal proceeding — 2 spawn rules, party=both,
2 months from R.118 decision, flag-gated with_appeal. Third
Pipeline-A relic (ccr.appeal) recommended not seeded — CCR appeal
is structurally absorbed into inf.appeal_spawn because one R.118
decision = one appeal window in the unified UPC_INF (CCR-as-flag)
model.
3. ccr.amend / rev.amend — claim "safe to drop" verified for patent
amendment (fully covered by inf.app_to_amend / rev.app_to_amend
chains under with_ccr+with_amend / with_amend flags). R.263 case-
amendment is court-discretionary; recommended NOT modelled.
4. zpo.* family — klage / vertanz / berufung redundancy verified
(de_inf.klage, de_inf.anzeige, de_inf.berufung / de_inf_olg.berufung
cover them). klageerw exposes a discrepancy on de_inf.erwidg
(6-week heuristic vs ZPO §276.1 S.2 court-set 2-week floor) — flagged
as a PATCH on the existing row, not a new rule. Task brief's mention
of "Vertagungsantrag" is a misread of zpo.vertanz (= Verteidigungs-
anzeige, not Vertagungsantrag); §227 itself recommended NOT modelled.
Net: 4 new rules drafted in Track B, 3 optional PATCHes in Track A, 12
FLAGs surfaced for m's decision before /admin/rules ingest. Appeal target
referenced by ROLE (not code) pending t-paliad-204 proceeding-code
rename — m picks final spawn_proceeding_type_id at ingest.
Per-rule template matches docs/proposals/orphan-concepts-2026-05-15.md.
Read-only research; no DB writes, no migration files. The spawn_proceeding_type_id
column is unused in live data today — these spawn rules will be the
first real consumer.
Phase 1 audit (AUDIT ONLY, no implementation). 799 lines, mai/pauli/fristen-logic-audit.
Headline findings:
- THREE parallel deadline-generation systems coexist with overlapping
intent:
- Pipeline A (proceeding-driven) — paliad.deadline_rules (172 rows),
FristenrechnerService.Calculate, drives /tools/fristenrechner +
SmartTimeline.
- Pipeline B (single-rule subset of A) — Pathway B cascade click.
- Pipeline C (event-driven, youpc legacy) — paliad.trigger_events
(110) + paliad.event_deadlines (77), EventDeadlineService.Calculate,
drives "Was kommt nach…" tab. Disjoint corpus from A.
- Rule corpus is RICHER than the brief implied: 32 columns, 172 rules
across 27 proceeding_types (132 fristenrechner + 40 litigation). The
dual-corpus is a latent footgun: paliad.projects.proceeding_type_id
accepts both categories with no CHECK constraint, so a project's
SmartTimeline depends on which code lands first.
- Data model already encodes most of m's mental model:
multi-deadline triggers via parent_id chains (deepest live: 3
levels in UPC_INF), conditional via condition_flag (AND-only),
flag-swap via alt_duration_value / alt_rule_code, court-set via
heuristic + 4-bucket classification, holiday adjustment via
HolidayService+CourtService.
- Real gaps (§6, 13 of them):
- Pipeline A/C redundancy (different capabilities, disjoint data).
- Litigation vs fristenrechner corpus drift (no contract).
- is_mandatory + is_optional overlap.
- deadline_concept_event_types is config layer, NOT trigger model.
- No real event-driven trigger endpoint.
- AND-only condition_flag (no OR/NOT/compound).
- Cross-proceeding spawn half-wired.
- 9 orphan concepts with rule_count=0 (incl wiedereinsetzung,
schriftsatznachreichung, weiterbehandlung).
- condition_rule_id dead column.
- Instance dimension (LG/OLG/BGH) not on paliad.projects.
- 1/26 deadlines linked to rule_id (anchor-from-actuals barely
used).
- Court-set is heuristic, not first-class column.
- Pipeline A lacks before / working_days / combine_op.
- The big m's-question: "all in the Rules so we should be able to
manage" is FALSE today. Rules edits = SQL migrations only. §8
proposes a 3-step ladder: status-quo / read-only admin / full
editor with audit log.
- §7 has concrete extension proposal for each §6 gap (migration size
costed).
- §9 has 15 open questions for m to call before Phase 2 starts.
- Live data sparse: 11/11 projects NULL proceeding_type_id, 1/26
deadlines with rule_id — demand-side mostly empty even though
supply-side (rules) is rich.
NOT cronus per memory directive 2026-05-06. NOT self-merged. Awaiting
m's go/no-go.
Inventor pass for t-paliad-178. Two intents (deadline determination vs
abstract procedural shape browse) get two dedicated routes:
- /tools/fristenrechner — keeps deadline-determination, gains Step 0
("Abstrakt oder Akte?") above today's Step 1.
- /tools/verfahrensablauf — new dedicated abstract-browse surface with
variant chips (with_ccr / with_cci / with_amend), consolidated-vs-lane
view, and side-by-side compare.
§0 premise audit corrects three things the task brief got wrong:
1. projects.court is free-text, not FK — no silent court_id auto-pick.
2. projects.proceeding_type_id points at litigation-category rows, not
fristenrechner-category — a mapping helper (litigation × jurisdiction
→ fristenrechner code) is required.
3. condition_flag variants only exist on UPC_INF + UPC_REV; every other
proceeding renders a single canonical timeline. Variant chips honour
this — no dead chips on DE_INF / EPA_OPP / DPMA_*.
Sliced into 4 independent merges: Slice 1 (route + shell split) is the
structural foundation; Slices 2-4 layer Step 0 / variant chips / compare.
DESIGN ONLY — no implementation. Awaiting m's go/no-go before coder shift.
Inventor design for replacing the project-page Verlauf with a SmartTimeline that
composes past actuals (deadlines, appointments, structural project_events),
present, future-projected (deadline_rules calculator at read time), and
off-script events into one project-scoped vertical timeline.
Key calls:
- virtual view, no new top-level table; single optional column
paliad.project_events.timeline_kind so a subset of audit rows surface as
timeline content
- counterclaim = sub-project (new paliad.projects.counterclaim_of FK), parent
renders parallel tracks; default our_side flips on creation
- date-anchoring reuses fristenrechner CalcOptions.AnchorOverrides — actuals
anchor downstream projections automatically
- new ProjectionService.For(projectID) thin adapter over FristenrechnerService
- 3 new FilterBar axes (timeline_kind, timeline_status, timeline_track) +
reuse of time, personal_only, deadline_event_type
- per-level aggregation rule: each level removes one tier of detail and adds
one tier of grouping (Case → Patent → Litigation → Client)
- 4-slice phasing: skeleton, projection+anchor, counterclaim sub-project,
parent-node aggregation
12 open questions for m before slice 1 PR opens. Inventor parks per gate
protocol; coder shift only after m's go-ahead.
Builds on t-paliad-159's UPC RoP audit. Drives from paliad's own corpus
outward: every active rule, every firm-wide event_type, every cascade
leaf — and asks whether a Determinator user can actually reach the row.
Headline finding: 71/76 (93%) of true Fristenrechner deadlines are
reachable from the cascade. The 5 unreachable cluster into one fix:
EP_GRANT (4 rules) plus UPC_INF.inf.app_to_amend lack cascade entry.
Adding an `ich-moechte-einreichen.ep-erteilung` subtree lifts coverage
to 100%.
Per-jurisdiction inventory (UPC, DE, EPO, DPMA) plus a §2.6 cross-cutting
table for the procedural-order categories m flagged (Hinweisbeschluss,
Beweisbeschluss, Streitwertbeschluss, Versäumnisurteil, R.71(3),
Beanstandungsbescheid, etc.).
§4 frames the smart-navigation choice: recommends P2 (persistent escape
button with capture) + P1 (free-text search per cascade level), defers
P3 (flatten deeper levels) until telemetry justifies it. The captured
"Mein Ereignis ist nicht dabei" texts feed both the gap-fill roadmap
and P1's ranking corpus.
No code changes; one markdown doc, 394 lines.
m/paliad#23. Recommends a single <FilterBar> client component on top of
the existing Custom Views substrate (t-paliad-144) — FilterSpec +
RenderSpec + ViewService + 5 code-resident SystemViews + ad-hoc
/api/views/run already cover every axis the issue lists.
Position: m's "halfway there without custom views" is exactly right.
Lift the substrate from /views/{slug} up to "the bar that every list-
shaped page reads from", with one schema bump (RenderSpec.list.row_action)
to keep entity-table row-click contracts intact.
Migrate one surface per PR: /inbox first (lowest blast radius, no filter
today), /events last (proof point, richest filter). /projects stays
bespoke per t-paliad-149 lock-in.
12 open questions (Q1-Q12) for m before lock-in. No hour estimates.
Verified premises: the issue body's `paliad.user_view_layouts` is a
typo — actual table is `paliad.user_views` (056). `/api/views/run` and
`/api/views/{slug}/run` confirmed live in internal/handlers/views.go.
Two intertwined Paliadin upgrades, scoped together because the chat
surface is where the write path is triggered and the write path is what
makes the chat non-trivial:
1. Inline slide-out modal reachable from every authenticated paliad
page, with structured page-context payload (route_name +
primary_entity + selection text) and per-route starter prompts.
2. Agent-suggested write path that drafts deadlines/appointments/notes
into the existing pending_create lifecycle (t-paliad-160) with new
provenance columns on approval_requests (requester_kind + agent_turn_id);
approved-from-agent rows render alongside 👀 with a sparkle ✨.
Hard call: keep the existing tmux relay for v1; recommend (but do not
commit) the Anthropic API cutover as a prerequisite for opening beyond
owner-only. Single Paliadin persona — no scope-bouncer pre-design.
Inventor parked. DESIGN READY FOR REVIEW. Awaiting m's go/no-go before
any coder shift.
Refs: m/paliad#20, t-paliad-146, t-paliad-160, t-paliad-138.
Consultant analysis of paliad's deadline data model per m's framing
(court system → proceeding → ordered event types → conditional
trigger edges). Maps current 5-table fragmentation, identifies gaps
G1–G7, locks 5 structural decisions via AskUserQuestion, proposes
target shape with mermaid example, sketches 4-phase additive→cutover
migration. Pure design — no code or schema changes in this branch.
Locked decisions (verbatim):
- Q1: Reuse courts.court_type as court-system identity
- Q2: Project IS proceeding instance (sub-projects when needed)
- Q3: Separate proceeding_event_edges table (multi-parent natural)
- Q4: Typed if_flags/unless_flags/requires_event_id columns
- Q5: Subsume deadline_concepts into event_types.concept_slug
8 RoP sections cross-referenced against paliad's deadline_rules library
via the youpc data.laws_contents authoritative text.
Two high-impact duration bugs found:
- rev.defence: 3 months seeded, RoP R.49.1 says 2 months
- rev.rejoin: 2 months seeded, RoP R.52 says 1 month
Both UPC_REV pleadings rules — every active Nichtigkeitsverfahren
tracked in paliad has miscalibrated reminders today. Single-row
UPDATEs fix both.
Plus rule_code drift on UPC_APP (R.220.1 used where R.224.1.a /
R.224.2.a / R.235.2 should be cited), R.51 / R.52 NULLs on REV
chain, and 25 missing rules ordered by frequency (R.19, R.262.2,
R.224.2.b, R.235.1, R.333.2, R.353, registry-correction family,
saisie + PI gaps, R.109 oral-hearing prep, R.245 rehearing, etc).
Plus an anchoring nuance on UPC_APP_ORDERS.app_ord.discretion
(R.220.3) — Pathway A may compute up to 15d too early because
the rule anchors on order, not on leave-refusal event.
Wave 0 (duration bugs) is the recommended first migration.
Wave 1+ orderings, tooling-blocked rules (R.198/R.213/R.245.2),
and m's open questions (proceeding-code naming, R.245 scope,
DNI scope) listed in §6, §7.
Inventor pass for m/paliad#13. Surfaces the dormant t-138 4-eye system
(zero policies in DB → silent bypass) by adding /admin/approval-policies
with project-picker → 8-cell matrix + partner-unit-defaults section.
12 design questions surfaced sequentially via AskUserQuestion (per dogma)
and locked in §2 of the doc:
1. Surface: /admin/approval-policies only (admin page card on /admin index)
2. Defaults concept: per-partner-unit defaults
3. Multi-unit conflict: most-restrictive wins
4. Tree inheritance: yes (ancestors contribute candidates)
5. Cross-source precedence: most-restrictive across project+ancestor+unit;
project row overrides outright
6. Suppression sentinel: 'none' value in required_role enum
7. Soft-disable: no, delete-only
8. Audit emission: /admin/audit-log only, not project verlauf
9. Empty-state: admin-only nudge card on /inbox when zero pending+policies
10. Bulk-apply: per-project "Auf Unterprojekte anwenden" button
11. Seed defaults: yes — conservative associate baseline for all partner units
12. Mobile shape: stacked sections per entity_type
13. Form hint: yes, above Speichern button on deadline/appointment new+edit
Migration 062 adds partner_unit_id (XOR with project_id),
'none' to required_role enum, paliad.approval_policy_effective() resolver,
and seeds 8 rows × N partner_units. ApprovalService.LookupPolicy delegates
to the resolver while preserving its calling contract (existing submit/
decide chain unchanged). New admin endpoints for unit-defaults, matrix
view, bulk-apply, and form-time effective lookup. ~3500-4500 LoC, single
PR, 5 commits.
Inventor parked. NOT cronus per memory directive. Awaiting m go/no-go.
Phase A.0 revealed Tailscale SSH on mRiver intercepts :22 from tailnet
peers and bypasses OpenSSH's authorized_keys entirely (banner
"SSH-2.0-Tailscale", auth method "none", command= restriction never
fires). The fix is port 22022 via a systemd ssh.socket drop-in:
Tailscale SSH only intercepts :22, so :22022 hits real OpenSSH where
the design's command=/from= shim restriction works as specified.
Updated:
- §3 locked decisions: row 5 added (port 22022, m's call 23:35)
- §4.5 new subsection: Tailscale SSH bypass via socket drop-in
+ records the "Address already in use" first-attempt failure as a
"don't retry without cleaning sshd_config Port directives first"
lesson
- §5.2/5.3: ssh-keyscan now uses -p 22022; known_hosts is host:port
keyed for non-22 ports
- §6.1/6.2/6.3: SSHPort field on RemotePaliadinService config, -p
flag in callShim, PALIADIN_REMOTE_PORT env (default 22022)
- §7 phasing: A.0 completion checked off step-by-step with concrete
fingerprints; A.5/A.6/A.7 split out as m-driven
- §8 security: Tailscale-SSH-on-:22 risk explicitly tabled with
port-22022 mitigation
- §10 deliverables: mRiver host-setup artifacts noted
- §12 new Phase A.0 completion summary with the three secrets m
needs to register in Dokploy
Phase A.0 verified end-to-end:
- ssh -p 22022 paliad-prod-key m@mriver health → ok
- run-turn UUID base64msg → 3.4 s including a real Claude response
- from="100.99.98.201" correctly rejects connections from mRiver
itself
mRiver host state in place (not in repo): authorized_keys with
restrictions, /home/m/.local/bin/paliadin-shim, ssh.socket drop-in.
Three secrets staged at ~/.paliad-staging/ on mRiver for m to copy
into Dokploy: paliad-prod-key (PALIADIN_SSH_PRIVATE_KEY),
known_hosts (PALIADIN_KNOWN_HOSTS), and the three plain env vars.
Refs m/paliad#12
Inventor design for routing Paliadin from paliad.de's Dokploy container
on mLake to mRiver via Tailscale + SSH, preserving m's Claude Code
subscription instead of paying Anthropic API tokens.
Three sub-designs covering m's four locked decisions (2026-05-07 22:35):
- network_mode: host on paliad (m overrode the sidecar recommendation;
Phase A explicitly tests traefik compatibility under host mode)
- server-side paliadin-shim with one RPC per turn (run-turn / reset /
health / bootstrap), authorized_keys command= restriction, from=mlake
- env-var routing trigger (PALIADIN_REMOTE_HOST) + Paliadin interface
split: LocalPaliadinService keeps the laptop PoC, RemotePaliadinService
shells out to ssh m@mriver paliadin-shim
- ed25519 keypair via Dokploy secret PALIADIN_SSH_PRIVATE_KEY, written
to a chmod 600 tmpfile at startup; pinned host key via
PALIADIN_KNOWN_HOSTS
Verified live before designing: mRiver tmux+claude present, mLake
Tailscale active and sees mRiver, paliad Dockerfile is alpine-minimal,
no authorized_keys on mRiver yet. No assumptions left from CLAUDE.md.
Includes: friendly error code mriver_unreachable extending t-paliad-150,
single-flight rate limit, security review (defence-in-depth via
command=/from= restrictions), three-phase rollout (manual proof →
Dockerfile bake → polish), file-level deliverables for the coder shift.
Inventor stops here — no code shipped. Awaiting m's go/no-go.
Refs m/paliad#12
058 = paliadin_poc (t-146), 059 = profession_vs_responsibility (t-148), both shipped on main 2026-05-07. Next available is 060.
Per maria's coder-shift instruction.
Three view modes (Tree default | Cards | Flat), chip filter row, pinning,
search-as-tree-filter, mobile drill-in. Cards view (m's addition) has
configurable content + per-user prefs in localStorage v1.
Q15 decision (delegated to inventor): bespoke /projects, NOT Custom Views.
Custom Views is event-shaped; projects are scope, not events. Adding
SourceProject + ShapeTree to t-144's substrate would break shape ⊥ source
orthogonality. Reversible if a unifying abstraction emerges.
Two-PR phasing: PR 1 = tree + chips + pin + search (~1100-1400 LoC,
migration 058). PR 2 = Cards view + customisation modal + cards-preview
endpoint (~700-900 LoC, additive on PR 1).
4 surfaced questions for m via AskUserQuestion: default landing + view mode,
cards default content, cards customisation scope, search shape. Other 17
questions answered with recommendations + rationale per the dogma (make it
easy for m).
Awaiting m's go on §12 questions before locking. NO coder shift until lock.
m's reframing 2026-05-07 20:56: Paliadin is "mostly for myself now
but can be expanded — monitoring use." Two-stage shape replaces the
single-PR production-v1:
- Phase 0 (PoC): tmux-Claude pattern lifted from goldi/mVoice
(mVoice/server.py:250-380). Claude Code window in a long-lived
tmux session, prompts via tmux send-keys -l, response via
/tmp/paliadin/{turn_id}.txt tail-f → SSE relay. Single user (m),
m's laptop only (PALIADIN_ENABLED=false on prod). ~600-900 LoC,
~1 day. Migration 057 (PoC variant) stores full prompt + response
for monitoring — no redaction at this scope.
- Phase 1 (production v1): the original §2-§6 Anthropic API design,
GATED on PoC success per §0.5.7 expansion criteria (≥3 turns/wd,
≥50% tool-use rate, 4 weeks).
§0.5 (new) inserted as the load-bearing PoC spec. §7 leads with the
two-stage frame. §8.5 questions split into PoC-relevant (Q-PoC-1..6)
and production-v1-deferred. youpc case-law lookup promoted to
Q-PoC-6: m himself does case-law research, so include it from day
one (cross-schema SELECT into data.judgments is technically trivial
since paliad and youpc share the same Postgres).
What we drop for PoC: Anthropic API client, BYO-AI, rate limit,
token caps, multi-user RLS edge cases, /admin cost dashboard,
compliance disclosure, most i18n keys.
What we keep: system prompt voice, citation discipline (best-effort),
visibility gate (Claude is required to use paliad.can_see_project()
in queries), /paliadin surface, SSE shape, audit table.
The two-stage shape protects against the t-145 pattern: ship cheap,
observe, decide. No 4500-LoC investment based on m's gut feel about
adoption.
Inventor design pass for the Paliadin: a Claude-backed conversational
assistant grounded in the user's own paliad data + paliad's static
reference (courts, glossary, deadline rules, Fristenrechner concept
tree). Long-lived in-process Go service that calls Anthropic's
Messages API directly with tool use; every tool is a thin shim over
an existing service (Dashboard / Project / Deadline / Appointment /
Court / Glossary / DeadlineRule). RLS / visibility inherited from
those services — Paliadin literally cannot see what the caller cannot.
Five coordinated sub-designs answer the issue's 20 open questions:
A. LLM architecture + tool-use + prompts (§2)
B. Data access + RLS + PII (§3)
C. UX (§4)
D. Token budget + cost + audit (§5)
E. Phasing (§7)
Phase 1 v1: /paliadin full page + sidebar entry, SSE stream of
Anthropic, 7 read-only tools, session-only history, 30/hour user cap
+ 1000/hour global cap, audit row per turn (metadata only — no
transcript), 4k input + 2k output token caps, no avatar/mascot, no
proactive onboarding. Migration 057 introduces paliadin_turns +
paliadin_rate_limit. Single PR, ~3500-4500 LoC.
mlex / /lex-* reuse: shape (system-prompt voice, tool-catalog idea,
citation style) — NOT code. mLex is a workspace, not a Go/TS repo;
the /lex-* skills drive Claude against youpc's MCP and cannot be
embedded in a paliad service.
Premise verifications surfaced one CLAUDE.md doc-bug (the
ANTHROPIC_API_KEY "Reserved for Phase H — do not set" row needs to
flip in the implementation PR — Paliadin un-defers it).
12 open questions for m in §8.5 — Anthropic key choice (personal vs
HLC enterprise), default model (Sonnet vs Haiku), surface
(/paliadin page vs drawer), mascot phase, 2-PA sanity check before
locking scope, etc. Same adoption-risk concern that just parked
t-paliad-145 — Paliadin's edge over open-Claude-in-another-tab is
data grounding, which only works if v1 makes it visible (citation
chips + tool-call evidence + tagline).
STOP after design. Awaiting m go/no-go before coder shift.
Inventor design doc (kepler) for issue m/paliad#6. Splits the conflated
project_teams.role column into two axes:
- paliad.users.profession (firm-wide, drives t-138 approval ladder)
- paliad.project_teams.responsibility (per-project, lead/member/observer/external)
Approval ladder evaluated as tuple: profession_level if responsibility
opens the gate (lead/member), else 0. Policy grammar from t-138 stays
single-valued.
Verified live state: project_teams=3 rows (all 'lead'), partner_unit_members=20
rows (all default 'attorney'). Backfill is essentially trivial; risk is the
SQL rewiring (4 sites in approval_service.go, 2 in derivation_service.go,
2 in reminder_service.go) — all mechanical.
12 open questions from issue body answered with recommendations + rationale +
alternatives. Awaits m's go before any coder shift.
DESIGN READY FOR REVIEW.
m's lock-in 2026-05-07: agree with all recommendations on Q1-Q18 and §10
Q19-Q27, with one correction on Q4: "activity" is a content selection
(sources + filters), not a render shape. Folded into `list` shape with
density: "compact" + actor/time columns. Shape ⊥ source — any source can
render in any shape.
Render shapes for v1: list / cards / calendar (3, was 4).
PR split decision (delegated to inventor): A1 backend substrate + API
(no UI change, ~1800 LoC, smoke via curl) → main → A2 frontend Custom
Views UI (~1600 LoC, additive on A1) → main.
Status flipped DRAFT → LOCKED. Inventor → coder transition initiated.
m's go/no-go pass at 2026-05-06 15:58: "I agree with all your recommendations
- go." All 19 questions in §6 lock as the recommended answers verbatim.
§0 status flipped from READY-FOR-REVIEW to LOCKED. New "Locked m decisions
on §6" subsection captures the highlights inline so future readers don't
have to scan the whole table to know what's pinned.
§13 end-of-design line updated to reflect the lock.
Implementation phasing (§7) unchanged:
- Phase 1: bug fix on the 3 narrow service methods (no schema, ~400 LoC,
ships standalone, closes the user-visible /projects/{id} "Keine Fristen"
bug).
- Phase 2: migration 055 (partner_unit_members.unit_role,
project_partner_units, extended can_see_project()) + DerivationService +
frontend Team-tab subsections + /admin/partner-units unit_role tagging
+ project /settings/team Partner Units section. Independent of t-138.
- Phase 3: approval extension — canApprove + inbox SQL widening for
derived_peer decision_kind. Gates on cronus's t-138 (currently on
mai/cronus/inventor-dual-control @ b3401ec) landing on main.
Inventor parked. Awaiting head's coder-shift assignment.
Three coordinated sub-designs in one doc, scoped to m's locked constraints
(2026-05-06):
1. Surface-by-surface aggregation policy. Bug surface fix:
/projects/{client_id} renders "Keine Fristen" because
DeadlineService.ListForProject + AppointmentService.ListForProject +
ProjectService.ListProjectEvents all WHERE project_id=$1 exact-match
instead of walking paliad.projects.path descendants. The shipped t-124
contract (projectDescendantPredicate, deadline_service.go:133 etc.)
already aggregates correctly on the union endpoints — three legacy
narrow paths just bypass it. Per-surface decision table for events /
deadlines / termine / Verlauf / project tree counts / dashboard /
CalDAV / email / search.
2. Effective-team semantics. Three structural gaps in the issue's
premise (verified against schema):
- No project↔unit junction (partner_unit involvement on a project).
- No PA/lawyer distinction in partner_unit_members (no role column).
- No lawyer↔PA pairing anywhere — Q11's "where is it stored" → nowhere.
Proposes:
- paliad.partner_unit_members.unit_role (lead|attorney|senior_pa|pa|paralegal),
unit-scoped not firm-wide so 3-axis principle holds.
- paliad.project_partner_units junction with derive_unit_roles[]
(default {pa, senior_pa}) + derive_grants_authority bool.
- Compute-on-read derivation via extended can_see_project() — no
materialised state, no drift.
- Display-effective vs visibility-effective team are different sets;
rename ListEffectiveMembers to ListVisibilityEffectiveMembers + add
ListSubtreeMembers.
3. Approval policy × hierarchy × derivation. Coordinates with t-138
(cronus, mai/cronus/inventor-dual-control @ 7d1ddb9):
- Q10: keep cronus's no-auto-inheritance, harden UX with a "Eltern-
Politik (zur Information)" panel showing parent rules without
applying.
- Q12: derived members visibility-only by default; per-(project, unit)
opt-in flag derive_grants_authority. When opted in, decision_kind
extends with derived_peer for honest audit chronology.
- canApprove + inbox SQL extension shape spec'd; coordinates with
cronus's t-138 §3.4 / §7.4.
Locked m decisions surfaced in §0:
- Behaviour is surface-specific.
- Effective Team of a Client = direct ∪ descendants ∪ partner-unit-derived.
- PA derivation = unit-on-project trigger.
- Derivation honesty: annotated everywhere.
- paliad-only scope.
19 design questions with proposed answers in §6 for m to lock. Migration
055 specced (§5). Implementation phased into 3 PRs (§7) — Phase 1 bug fix
ships standalone if m wants quick win.
Inventor parked. Awaiting m go/no-go before coder shift.
Locked design for 4-Augen-Prüfung on Fristen + Termine. m-confirmed
decisions on all 11 open questions:
- Qualification gate reuses paliad.project_teams.role per-project
(no new firm-wide axis). Adds new value `senior_pa` to the enum.
- Strict ladder: lead > of_counsel > associate > senior_pa > pa.
Default required_role = associate. Per-project override allows pa-
approves-pa or senior_pa-tier escalation.
- Per-(project, entity_type, lifecycle_event) policy grammar — up to
8 settable rows per project in paliad.approval_policies.
- Edit-trigger allowlist = date-bearing fields only (Frist due_date /
original_due_date / warning_date; Termin start_at / end_at).
- Write-then-approve: row mutates immediately, approval_status flips
between approved/pending/legacy. Delete is the one stage-then-write
exception (hard-delete on approve, restore on reject).
- Refuse + global_admin override on single-qualified-approver deadlock.
- Pending state visualised everywhere — list views, agenda, dashboard
traffic-light, project detail, CalDAV-synced calendars (`[PENDING] `
title prefix), email reminders.
- Bell + /inbox page with two tabs (zur Genehmigung / meine Anfragen).
- Operational paliad.approval_requests + audit lifecycle written to
existing paliad.project_events (4 new event_types per entity).
- RLS = same can_see_project predicate; service layer enforces the
approve/reject action gate. CHECK constraint blocks self-approval.
- Mark-legacy backfill: approval_status='legacy' on existing rows;
next mutation flows through the gate.
Implementation phasing: single migration 054 + 8-commit PR plan
covering schema, service, wiring, policy authoring page, inbox,
pending pills, CalDAV/email integration, Verlauf rendering.
Inventor parked. Awaiting m go/no-go before any coder shift.
Archives m's locked design call (2026-05-05 18:51) plus live-codebase
verification: paliad.holidays.country exists per-country; paliad.courts
does not (must create); proceeding_types.jurisdiction is regime not
country (do not remove); 41 hand-curated courts already in
internal/handlers/courts.go ready to seed; HolidayService.loadYear is
country-blind today (latent bug); germanFederalHolidays merge is
hardcoded (must become country-conditional). Task stays ON-HOLD until a
non-DE forum or EPO closure-day calendar comes into scope.
v4 addresses three concerns from m on 2026-05-05 in priority order:
1. Card-click → compute deadline → add-to-project (v3 cards were dead-ends).
2. Filter narrowing bug — slug → concept_id allow-list dropped per-leaf
proceeding_type_code, so picking "UPC infringement opposing party"
leaked DE/EPA/DPMA pills. Confirmed via DB query: 25+ leaves overbroad.
3. RoP-rigorous tree audit: 6 confirmed seed errors (Hinweisbeschluss
DE_INF mismap, notice-of-defence-intention UPC_INF mismap, three
cost-appeal notice-of-appeal mismaps, request-for-discretionary-review
needs UPC_APP_ORDERS narrowing), plus reply-to-cross-appeal coverage
gap and bescheid-mit-frist orphan.
Plan splits into three independent phases (A: filter fix, no schema; B:
card-click flow + new calculate-rule endpoint; C: taxonomy migration 052
without RAISE EXCEPTION coverage gates per last night's outage lesson).
Inventor → coder gate held: no production code in this commit.
m approved all 12 open questions in one batch. Locked spec:
1. Legacy tabs RETIRED in Phase E.
2. Decision-tree depth UNLIMITED (was: 4 max). Property of
event_categories data, not hard-coded.
3. Clickable breadcrumb for navigation.
4. Partial-path bookmarks (?b1=...).
5. Multi-select forum filter, default 1 selected.
6. Path-matching cards at each step. Renamed "Pfad lockern" →
"Schritt zurück".
7. Emojis only, no separate colour treatment.
8. Forum buckets simplified to 10: UPC CFI + UPC CoA + DE LG/OLG/
BGH/BPatG + EPA Erteilung/Einspruchsabt./Beschwerdek. + DPMA.
m collapsed UPC LD/CD into UPC CFI (rules identical).
9. B1↔B2 share filter state.
10. Single branch / sequential commits / one final merge.
11. Party perspective default Claimant/Proactive; localStorage
remembers last-used. URL ?my_side= + ?appeal_filed_by=.
12. Bilateral rules tagged via new is_bilateral column on
deadline_rules; mirroring only when flagged.
Maria's two scope additions folded in:
- Court-system granularity for forum filter (clarification).
- Party-perspective selector absorbing t-paliad-132.
Implementation now starting on this branch.
m's 2026-05-05 brief restructures the page surface that v2 (t-paliad-131)
shipped. The current Fristenrechner stacks three blurred entrypoints —
Phase D search bar, Verfahrensablauf tile grid, "Was kommt nach…" tab.
v3 forks the page so each mental model has its own entry:
- Pathway A — Verfahrensablauf informieren (Browse): existing wizard.
- Pathway B — Frist eintragen aufgrund Ereignis (Event → Deadline),
subdivided into:
- B1 Entscheidungsbaum: data-driven button cascade (CMS-Eingang →
Vom Gericht → Hinweisbeschluss → cards), max 4 deep, back +
breadcrumb + bookmark URLs.
- B2 Filter / Suche: Phase D concept-card search PLUS new
Gericht/System multi-select chip filter (Q8 reversal). All filters
AND-narrow.
Adds two new tables (Phase A — purely additive):
- paliad.event_categories — recursive taxonomy tree, with step
questions on non-leaf nodes.
- paliad.event_category_concepts — leaf → concept junction with
optional proceeding_type_code narrowing.
Existing data layer (deadline_concepts, deadline_rules, trigger_events,
deadline_search matview) untouched. Phase D search handler gains
?event_category_slug= and ?forum= query params; forum-bucket map lives
in Go (UPC / DE LG / DE OLG / DE BGH / DE BPatG / EPA / DPMA).
Phasing: A (data) → B (landing fork) → C (B1 tree) → D (B2 forum
filter) → E (retire legacy tabs, gate-gated). Each phase independently
shippable.
Open questions for m at §10: retire legacy tabs, decision-tree depth,
back/breadcrumb, partial-path bookmarks, multi vs single-select forum,
all-vs-path-matching cards per step, austere icons, 7 forum buckets,
B1↔B2 state-sharing, PR phasing.
Inventor parked. Next: m's go/no-go before coder shift.
Cross-references docs/plans/unified-fristenrechner.md (v2, shipped) for
concept-layer / search-backend / coverage details v3 inherits unchanged.
m's revisions (23:36):
- Q1 corrected: EN slug for shared concepts too (klageerwiderung →
statement-of-defence, replik → reply-to-defence, berufungsfrist →
notice-of-appeal, einspruchsfrist → opposition, wiedereinsetzung →
re-establishment-of-rights). DE slug only for German-law-only
concepts (nichtzulassungsbeschwerde, versaeumnisurteil-einspruch,
hinweisbeschluss-stellungnahme).
- Q4 simplified: drop the customizable-extension flag_param mechanism.
Replace with a generalised "user can override any computed date,
downstream re-anchors off it" capability. CalcOptions gains
AnchorOverrides map[string]string; tree-walk consults it before the
computed-date map. UI gives each row a click-to-edit date affordance
(also unlocks court-set decision dates being entered post-hoc, which
the existing IsCourtSet placeholder UX has been hinting at). PatG §82
seed stays at 2 months static; user-set extensions handled by inline
date override, not by a flag_param mechanism.
Cleaner. No new DB column. Generalises beyond extensions to any case
where the user knows the real date better than the calculator's
projection.
- Q1 concept slug naming: mixed convention. EN slug for UPC/EPC-native
concepts (application-to-amend, request-for-discretionary-review).
DE slug for German-only concepts (nichtzulassungsbeschwerde,
versaeumnisurteil-einspruch). DE slug for SHARED concepts that exist
in both DE and UPC/EPC (klageerwiderung, replik, berufungsfrist,
einspruchsfrist, wiedereinsetzung) because m works primarily in
German and the slug is internal/maintenance-facing only.
- Q2 EU.EPÜ confirmed for EPÜ namespace.
- Q3 PatG §111(1) 1mo→3mo confirmed for Phase B3.
- Q4 PatG §82(1): shape (b) — 1mo base + with_extension flag with
CUSTOMIZABLE extension duration (default 1mo). New flag_param
mechanism on flag-conditioned rules: CalcOptions.Flags becomes
map[string]int; rules with flag_param_code add caller's param to
duration. UI shows number input next to checkbox. Generalises to
PatG §75 etc. Phase A5 picks up the calculator extension; Phase B3
hooks PatG §82.
- Q5 Full Appeal Chain: multiple date inputs per stage, no inter-stage
gap guessing. Stage N's downstream deadlines render as IsCourtSet
placeholders until user enters Stage N-1's terminal decision date.
- Q6/Q7/Q8 confirmed as drafted.
§5.2.2 PatG §82 row updated to reflect flag-based shape. §4.4 concept
slug examples expanded with the mixed-convention rule rendered
explicitly. §7 Phase A5 added for the flag_param calculator change.
Significant restructure after m's 10 answers (relayed via head 23:10):
- Augment, not replace — search bar at top + existing tile grid stays as
browse fallback. Both existing tabs stay live. Phase E (subsumption)
dropped.
- Unifier shape: new paliad.deadline_concepts layer above existing
deadline_rules; deadline_rules gains concept_id FK + structured
legal_source. condition_flag scalar→array (Q3) for AND-of-flags
semantics on UPC_REV (with_amend ∥ with_cci).
- Search hits as ONE card per concept with proceeding pills inside (NOT
a flat list of one-per-proceeding hits). Card body: pills [UPC R.23.1
3mo] [LG §276.1 6w] [BPatG §82.1 1mo] [EPA R.79.1 4mo] etc.
- Structured legal_source codes: UPC.RoP.23.1, DE.ZPO.276.1,
EU.EPÜ.108, DE.PatG.111.1 — parseable, filterable, indexed.
- "Vollständige Instanzenkette" checkbox synthesises LG→OLG→BGH (or
BPatG→BGH) timeline as one tree at render-time; data stays per-
instance.
- Forum filter dropped (Q8). Filters now: Verfahrensart / Partei /
Rechtsquelle.
- Court-set placeholders ("Verhandlung", "Entscheidung",
"Zwischenverfügung") surface as searchable triggers (Q10).
- Columns-view sequence preservation (Q9) flagged but punted to a
separate follow-up task — t-paliad-129 column renderer must respect
sequence_order even on undated court-set events.
8 remaining open questions for m (concept slug convention, EPÜ
namespace, PatG §82(1) modeling, Full Appeal Chain anchor handoff,
quick-pick chip seed, etc.).