07acf7b4a216e9e64c89fdb93e08dbf0fb6e8983
13 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 07acf7b4a2 |
feat(litigationplanner): Berufung unification — one upc.apl + 5 appeal_target chips (Slice B1, m/paliad#124 §18.1)
Collapses the 3 UPC appeal proceeding_types (upc.apl.merits 7 rules,
upc.apl.cost 2, upc.apl.order 7 = 16 total across 3 codes) into ONE
unified upc.apl proceeding type + a per-rule applies_to_target[]
discriminator. The verfahrensablauf picker now shows one "Berufung"
tile; after picking it, the user selects which decision the appeal is
directed AT via a 5-chip group (Endentscheidung / Kostenentscheidung /
Anordnung / Schadensbemessung / Bucheinsicht) and the engine filters
rules whose applies_to_target contains the picked slug.
m's 2026-05-26 decision: Schadensbemessung-as-appeal is a NEW first-
class target with its OWN rule set (no shared inheritance from
merits). The 5 enum values are all defined + addressable; for now
schadensbemessung and bucheinsicht return empty timelines until rules
are seeded in a follow-up slice (likely via /admin/rules or pairing
with t-paliad-193 orphan-concept-seed).
Migration 134 (additive only):
- ADD proceeding_types.appeal_target text (CHECK on 5 slugs OR NULL)
- ADD deadline_rules.applies_to_target text[] (CHECK each element
in the 5 slugs)
- INSERT the unified upc.apl row (inherits sort/color from
upc.apl.merits)
- Audit-first RAISE NOTICE pass listing every row about to be
touched + a post-migration sanity check
- Reassign rule rows: merits → applies_to_target={endentscheidung},
cost → {kostenentscheidung}, order → {anordnung}
- Archive (is_active=false, NOT DELETE) the 3 old proceeding_types
so historical FKs stay intact
- Down migration restores is_active=true on the 3 old types, points
rules back by their applies_to_target stamp, drops the unified
row, drops both columns. Safe.
Package additions (pkg/litigationplanner):
- AppealTarget* constants + AppealTargets[] ordered list +
IsValidAppealTarget(s) predicate (silent no-op on unknown slugs
so a stale frontend chip doesn't break the render)
- ProceedingType.AppealTarget *string field (top-level marker;
NULL on non-appeal proceedings)
- Rule.AppliesToTarget pq.StringArray field (per-row applies-to set)
- CalcOptions.AppealTarget string (engine filter — when set,
keeps only rules whose AppliesToTarget contains the slug)
Engine filter runs after ApplyRuleOverrides but before the rule walk
so the existing condition_expr / spawn / appellant-context machinery
operates on the filtered subset transparently.
paliad-side wiring:
- deadline_rule_service.go: ruleColumns + proceedingTypeColumns
extended to scan the new columns
- handlers/fristenrechner.go: AppealTarget JSON field on the
request payload, threaded into CalcOptions
Frontend (verfahrensablauf surface only):
- Single "Berufung" tile replaces the 3 separate Berufung tiles
- New 5-chip appeal-target row, shown only when upc.apl is picked
- URL state ?target=<slug>; default endentscheidung when none set
- APPELLANT_AXIS_PROCEEDINGS updated: upc.apl.* (3 entries) →
upc.apl (1 entry)
- i18n keys (DE + EN) for the new tile + the 5 chip labels +
the "Worauf richtet sich die Berufung?" / "Appeal against:" prompt
- calculateDeadlines threads appealTarget through to the API
Acceptance:
- go build clean, go test all green (existing test suite — no new
tests on the engine filter as a follow-up; the migration's
sanity-check DO block guards the rule-reassignment count)
- Live audit before drafting confirmed: 3 active UPC appeal
proceeding_types, 16 rules total, primary_party already conforms
to 4-value vocab on all proceeding-bound rules
|
|||
| 80883eaac5 |
feat(verfahrensablauf): re-surface hidden optional events — show-hidden toggle + un-hide chip (t-paliad-290)
m/paliad#122. atlas's #96 Slice A added per-card 'Überspringen' but no un-skip path — hidden cards just disappeared from the timeline. This adds the missing return path: - CalcOptions.IncludeHidden (default false) tells the calculator to re-surface skipRules entries as faded rows instead of dropping them. When true, the rule renders with UIDeadline.IsHidden=true and the descendant-suppression cascade is bypassed so children compute their dates off the un-suppressed parent. - UIResponse.HiddenCount always reflects the projection's hide count (gate-passed rules whose submission_code is in skipRules) so the "Ausgeblendete (N)" badge stays accurate regardless of toggle state. - /tools/verfahrensablauf gets a "Ausgeblendete anzeigen" checkbox next to the perspective + appellant selectors. URL-driven (?show_hidden=1) so the state is shareable and survives reload. The row hides itself on projections with zero hidden cards. - Hidden cards render via .timeline-item--hidden / .fr-col-item--hidden (opacity 0.55 + dotted border, mirroring the existing --skipped fade) and carry an inline "Wieder einblenden" chip. Clicking the chip removes the skip choice via the page's existing attachEventCardChoices remove callback (URL state + recalc included) and runs through a new delegated handler in event-card-choices.ts. - 3 new i18n keys (DE+EN): choices.show_hidden.label, choices.show_hidden.count, choices.unhide.chip. The skip-choice storage shape (paliad.project_event_choices, atlas's table) is unchanged — un-hide is just a delete of the skip row. Tests: 3 new bun-test cases pin the chip contract (emits on isHidden= true with submission_code, suppressed otherwise); go test ./internal/... + bun run build clean. |
|||
| bf60fc1400 |
feat(t-paliad-265): projection engine + HTTP handlers for per-card choices
m/paliad#96 — slice A engine + slice B engine wired together (per m's Q4 bundling decision in §11 of the design doc). Engine (internal/services/fristenrechner.go): - CalcOptions gains PerCardAppellant map, SkipRules set, IncludeCCRFor set. All three keyed by paliad.deadline_rules.submission_code (same key AnchorOverrides uses). - UIDeadline gains AppellantContext (per-decision pick that propagates to descendants via parent_id chain) + ChoicesOffered (passes the jsonb through to the frontend so the caret renders). - Calculate honours all three: * IncludeCCRFor non-empty → append with_ccr to flag set before gate evaluation (v1 simplification documented in CalcOptions comment; correct for single-CCR-entry-point proceedings). * SkipRules suppression via submission_code match AND parent_id cascade (descendants suppress too — one-pass walk in sequence_order). * AppellantContext: each rule with its own per-card pick stamps its UUID; descendants inherit via parent_id lookup; "" = no override. HTTP: - /api/projects/{id}/event-choices GET / PUT / DELETE — full CRUD with visibility gate, audit-logged via paliad.system_audit_log. - POST /api/tools/fristenrechner accepts either projectId (server pulls choices from project_event_choices) OR inline perCardChoices (unbound /tools/verfahrensablauf surface). Inline wins when both. Services wiring: - EventChoiceService instantiated in cmd/server/main.go; threaded into handlers.dbServices.eventChoice. |
|||
| 0099e2f28c |
feat(t-paliad-179): register /tools/verfahrensablauf + 302 legacy ?path=a
Backend half of Slice 1: a new dedicated route owns the abstract-browse intent that was previously emulated by /tools/fristenrechner?path=a + client-side fix-up. The page handler is a 1-liner that serves dist/verfahrensablauf.html (no DB dependency). A naked ?path=a on /tools/fristenrechner now 302s to the new URL so bookmarked legacy links survive. ?project=<uuid>&path=a still serves the fristenrechner shell because that's wizard state set by client- side history.replaceState during Akte-mode Pathway A — refreshing mid-wizard must not bounce away. Test covers all four query shapes: naked path=a → redirect, path=a with project → no redirect, no params → no redirect, path=b → no redirect. |
|||
|
|
733917aae2 |
feat(t-paliad-122): GET /api/tools/courts + Fristenrechner court picker
GET /api/tools/courts[?courtType=UPC-LD] returns the deadline- computation slice of paliad.courts (id, code, names, country, regime, court_type) — distinct from the rich Gerichtsverzeichnis at /api/courts. Optional courtType filter narrows to a single tier. POST /api/tools/fristenrechner and POST /api/tools/fristenrechner/ calculate-rule both accept an optional courtId field. When set, the calculator resolves the court's (country, regime) and uses that calendar; when omitted, the proceeding's existing jurisdiction column seeds a sensible default — preserves today's behaviour for callers that don't yet send a court. Frontend: court-picker-row added to step 2 of the Fristenrechner wizard. Visible only for proceeding types with multiple compatible courts (today: every UPC-flavoured proceeding — UPC LDs span 12 countries, plus UPC CD seats and the CoA). DE-only proceedings (BPatG nullity, BGH appeals, DPMA, EPA, EP grant) keep the form unchanged. Picker re-runs the calc on selection so the user sees the same deadlines shift to a different calendar without a manual click. i18n key deadlines.court.label added for both DE and EN. Default courts wired sensibly: UPC_INF / UPC_REV / UPC_PI etc. → UPC LD München (HLC's home venue); UPC_APP / UPC_APP_ORDERS / UPC_COST_APPEAL → UPC CoA Luxembourg; UPC_REV → UPC CD Paris. |
||
|
|
d72990ad1b |
feat(t-paliad-122): country+regime aware HolidayService + CourtService
Holiday struct gains Country (ISO-3166) + Regime ('UPC' | 'EPO' | "")
fields. AppliesTo(country, regime) is the matching rule the new lookup
methods filter through: a row matches when its Country equals the
court's country OR its Regime equals the court's regime. UPC LD München
(DE+UPC) sees DE federal + UPC vacations; LG München (DE+"") sees only
DE federal; UPC LD Paris (FR+UPC) sees FR + UPC. germanFederalHolidays
fallback now country-tagged 'DE' so the per-country filter applies it
only to DE-jurisdictional callers.
Public service methods (IsHoliday, IsNonWorkingDay, AdjustForNonWorking
Days, AdjustForNonWorkingDaysWithReason, findVacationBlock) all take
(country, regime). Cache stays year-keyed — single DB hit per year, all
courts touching that year share it.
New CourtService loads paliad.courts once + answers Lookup(id),
CountryRegime(id, defaultCountry, defaultRegime), All(), ByCourtType(t).
FristenrechnerService.CalcOptions / CalcRuleParams gain CourtID;
EventDeadlineService.Calculate gains courtID. When courtID is empty,
DefaultsForJurisdiction maps the proceeding's existing jurisdiction
column to a sensible (country, regime) default — UPC proceedings get
(DE, UPC), everything else gets DE-only — preserving today's behaviour
for callers that don't yet send a court.
Tests: new TestAppliesTo_CountryRegimeFilter + TestAppliesTo_Rules
cover the cross-product of (DE court / UPC LD München / UPC LD Paris /
LG München) × (DE federal / UPC vacation / FR holiday). Existing tests
threaded through with ('DE', 'UPC') to preserve behaviour they were
written to lock.
|
||
|
|
b54e938bdf |
feat(t-paliad-136): Phase B — card-click → calc panel → add to project
The v3 result cards were dead-ends: clicking a Klageerwiderung pill
showed no deadline; users had to switch to Pathway A's wizard, retype
the date, and read the deadline out of the timeline. v4 makes the card
the entry to a single-rule calculator + add-to-project flow per m's
2026-05-05 11:58 feedback.
Backend (single-rule calc, no parent walk):
- New POST /api/tools/fristenrechner/calculate-rule endpoint accepts
either ruleId OR (proceedingCode + ruleLocalCode), trigger date, and
optional condition flags. Returns rule metadata + computed dueDate +
originalDate + adjustment-reason chip data.
- FristenrechnerService.CalculateRule() reuses the existing addDuration
+ HolidayService.AdjustForNonWorkingDaysWithReason pipeline so
t-paliad-119's adjustment-reason explainer and t-paliad-121's UPC-
Sommerferien skip both apply automatically. Court-determined rules
(party='court' or event_type ∈ hearing/decision/order) return
IsCourtSet=true and an empty due date.
- Flag-conditional rules surface FlagsRequired even when the caller
hasn't supplied the flag — the UI uses this to render checkboxes;
toggling recomputes live. With all flags satisfied + alt_duration_*
present, the calc swaps to alt values (existing semantics).
- Live-DB integration test covers plain calc, court-set, flag handling,
and error paths (skipped without TEST_DATABASE_URL).
Frontend (inline calc panel):
- Click any card body or rule pill → expand inline panel inside the
card (only one open at a time). Pill picker (radio chips) appears
when the card has 2+ rule pills; first preselected. Trigger date
defaults to today (m's Q3). Flag checkboxes auto-render from the
rule's condition_flag.
- Result row shows due date, "(N units from triggerDate)", and a
shift chip when wasAdjusted ("⚠ Verschoben vom … wegen UPC-
Sommerferien (27.7.–28.8.)").
- "Zu Akte hinzufügen" CTA → inline project picker → POST to existing
/api/projects/{id}/deadlines/bulk with a single-element array using
source='fristenrechner' (m's Q2: existing tag, no new audit category).
- Modifier-key clicks (Cmd/Ctrl/Shift/middle) preserve the legacy
drill-to-Pathway-A semantics via <a href> anchors. Trigger pills
(Wiedereinsetzung, etc.) keep the trigger-event drill — they don't
have a single rule to compute.
- Escape collapses the open card.
CSS: lime accent border on hover/expanded; dashed top border for the
calc panel; mobile-friendly grid for the pill picker.
UPC R.221 cost-appeal sequence (m's Q5) is wired in Phase C's seed
already; Phase B's pill picker renders both pills (leave-to-appeal +
notice-of-appeal) when the user hits one of those leaves.
|
||
|
|
78966ec098 |
feat(t-paliad-131): Phase A — concept layer + AnchorOverrides + click-to-edit dates
PR-1 of the Unified Fristenrechner. Purely additive: new search-grouping layer + per-rule date override capability. No coverage changes yet (those land in PR-2 = Phase B1 UPC counterclaim cross-flows). Migrations: - 037: paliad.deadline_concepts (id, slug, name_de/en, aliases text[], party, category, sort_order). Trigram + GIN indexes for the search bar. - 038: deadline_rules.concept_id (uuid FK), legal_source (text); event_deadlines.legal_source; trigger_events.concept_id (text slug, soft-link — youpc imports keep their bigint PK). - 039: deadline_rules.condition_flag text → text[] (USING ARRAY[old]). Semantic: rule renders iff every element is in CalcOptions.Flags. Single-element arrays preserve the legacy with_ccr swap exactly. - 040: seed 30 concept rows + backfill all 74 fristenrechner deadline_rules with concept_id; backfill legal_source from existing rule_code (e.g. 'RoP.023' → 'UPC.RoP.23.1', '§ 276 ZPO' → 'DE.ZPO.276.1', 'Art. 108 EPÜ' → 'EU.EPÜ.108', 'R. 79(1) EPÜ' → 'EU.EPC-R.79.1'). Calculator (services/fristenrechner.go): - ConditionFlag is now pq.StringArray (matches text[] schema). New allFlagsSet() helper gates rule rendering; rules with multi-element flags require ALL of them set (prep for Phase B1 with_amend ∧ with_cci). - CalcOptions.AnchorOverrides map[string]string (rule_code → YYYY-MM-DD). The tree-walk consults overrideDates[parent.code] before reading the computed-date map; lets a downstream rule re-anchor on a user-set date. - IsCourtSet rows that get an override stop being placeholder and emit the user's date as a real anchor (so downstream cost_app etc. compute off it). New IsOverridden flag in UIDeadline so the UI can highlight user-edited rows. - LegalSource surfaced on UIDeadline for future search-card display. UI (frontend/src/client/fristenrechner.ts + global.css + i18n): - Each timeline / column rule date is click-to-edit. Click → inline date input → blur or Enter → POST with anchorOverrides → re-render. Empty value clears the override. Escape cancels. Root-event rows (the trigger anchor) stay non-editable — that's the trigger-date input. - Override map cleared on proceeding switch / reset; persists across trigger-date / flag toggle changes within the same proceeding. - New CSS: subtle hover underline on .frist-date-edit; lime border on .timeline-date--overridden + .frist-date-edit-input. - New i18n key deadlines.date.edit.hint (DE + EN). Handler (handlers/fristenrechner.go): - POST body gains optional anchorOverrides map<string,string>; passed through to CalcOptions. Tests: - TestAllFlagsSet covers single-flag legacy semantic, two-flag AND semantic, empty-required unconditional, extra-flags-no-effect. - Existing TestIsCourtDeterminedRule unchanged. Phase A ships standalone — Phase B1 (UPC counterclaim cross-flows) and Phase C/D (search backend + concept-card UI) follow. |
||
|
|
460736ad1e |
refactor(t-paliad-092): rename Go module path patholo → paliad
F-6 from t-paliad-074 architecture audit. The Gitea repo was renamed m/patholo → mAi/paliad → m/paliad, but go.mod still declared `mgit.msbls.de/m/patholo` and every internal import echoed the pre-rebrand name. Sweep: - go.mod: module path → mgit.msbls.de/m/paliad - All *.go files: imports rewritten via sed - README.md, docs/design-kanzlai-integration.md: mAi/paliad → m/paliad - Frontend issue-reference comments (mAi/paliad#N → m/paliad#N) in i18n.ts, theme.ts, sidebar.ts, app.ts, Sidebar.tsx, PWAHead.tsx, global.css Verified: go build/vet/test ./... clean, bun run build clean, no remaining mgit.msbls.de/m/patholo or mAi/paliad references outside docs that intentionally describe the rename history. |
||
|
|
d00974424f |
fix(t-paliad-086): Tier 1 Fristenrechner bug fixes — PR-3
Implements the four audit recommendations from §6.1 of docs/audit-fristenrechner-completeness-2026-04-30.md plus a holiday- adjustment cap fix surfaced by PR-2's smoke test. (1) UPC_INF CCR-conditional rejoinder Public Fristenrechner now flips inf.reply (RoP.029.b → RoP.029.a) and inf.rejoin (1mo / RoP.029.c → 2mo / RoP.029.d) when the user ticks "Mit Widerklage auf Nichtigkeit." Implemented via a new `condition_flag` column on paliad.deadline_rules: when the rule names a flag and the API request's flags array contains it, the calculator substitutes alt_duration_value/unit and alt_rule_code. Independent of the existing `condition_rule_id` mechanism (which references a real rule in the same proceeding tree — only useful for matter-attached trees that already seed the CCR rule). (2) UPC_APP / internal APP grounds anchoring `app.grounds` is now anchored on the trigger date (the appealed decision) with a 4-month duration, not chained 2mo after `app.notice`. Per RoP 220.1 the legal rule is "4 months from notification of the decision," independent of when the notice itself was filed. The chain only happened to give the right answer when both legs landed on a working day; under holiday rollover (e.g. notice deadline pushed to Monday) the grounds deadline drifted off the 4mo legal target. (3) EP_GRANT publish anchor on priority date New `anchor_alt` column on paliad.deadline_rules. ep_grant.publish carries `anchor_alt='priority_date'`. The Fristenrechner UI surfaces an optional "Prioritätstag" input (visible only when EP_GRANT is selected) that, when populated, anchors the publish-A1 calculation on the priority date instead of the filing. Falls back to filing date when the priority field is empty (the case for purely-EP applications with no foreign priority claim). (4) Rule-code format normalisation Migration 029 normalises 'RoP 23' → 'RoP.023', 'RoP 29b' / 'RoP.029b' → 'RoP.029.b', 'RoP 220.1' → 'RoP.220.1', etc. across deadline_rules. Matches the canonical youpc format already used by the PR-1 imported event-deadline rule codes. (+) AdjustForNonWorkingDays cap bumped 30 → 60 Surfaced by the PR-2 smoke test: SoD on 2026-04-30 (3mo from trigger) landed on Sat 2026-08-29 instead of Mon 2026-08-31. The 30-iteration safety bound on AdjustForNonWorkingDays cannot walk past the 33-day UPC summer vacation plus flanking weekends. Bumped to 60. Pure-Go one-liner, locked by a follow-up production smoke (real paliad.holidays seed has the UPC vacation). Schema (migration 029): two new nullable text columns on paliad.deadline_rules — `condition_flag` and `anchor_alt`. Both ignored by every existing rule; only the rows updated above carry values. Models: DeadlineRule gains ConditionFlag + AnchorAlt (nilable strings). Service: FristenrechnerService.Calculate now takes a CalcOptions struct (PriorityDateStr, Flags). API handler accepts optional priorityDate and flags fields on POST /api/tools/fristenrechner. Frontend: TSX surfaces the priority-date row + CCR checkbox conditionally on selectedType (only EP_GRANT / UPC_INF respectively). Client TS reads them and threads through the API call. New i18n keys for both DE+EN. Migration 029 dry-run validated on prod Supabase (BEGIN/ROLLBACK): schema + UPDATEs apply cleanly, rule states match expected post-fix shape. Tests + go build/vet + bun build all clean. |
||
|
|
d78f20be8a |
feat(t-paliad-086): trigger-event Fristenrechner mode + working_days primitive — PR-2
Adds the second Fristenrechner mode (mirrored from youpc.org's deadline
calc): pick a UPC trigger event + date, see all deadlines that flow
from it. Coexists with the existing course-of-proceedings timeline mode
via a tab toggle on /tools/fristenrechner.
Backend:
- internal/services/event_deadline_service.go — EventDeadlineService.
ListTriggerEvents (alphabetical), Calculate (resolves all deadlines
flowing from a trigger). Routes through HolidayService for weekend +
holiday rollover. Honours the new working_days unit. Resolves
composite rules (alt_* + combine_op) by computing both legs and
picking max/min. Used by R.198/R.213 ("31d OR 20wd, whichever is
longer") imported in PR-1.
- internal/services/event_deadline_service_test.go — covers
addWorkingDays (forward, backward, zero, holiday-skip), composite
rule semantics, before-timing.
- internal/handlers/fristenrechner.go — two new endpoints:
GET /api/tools/trigger-events, POST /api/tools/event-deadlines.
- handlers.Services / dbServices: new EventDeadline / eventDeadline
field; wired in cmd/server/main.go from the same HolidayService.
Frontend:
- frontend/src/fristenrechner.tsx — tab strip + second wizard panel
(3 steps: trigger picker → date → flat result list).
- frontend/src/client/fristenrechner.ts — initEventMode wiring,
typeahead filter over the 102 trigger events, Calculate flow,
bilingual rendering, composite-rule labels, lang-change refresh.
- frontend/src/client/i18n.ts — 27 new keys (DE+EN) under
deadlines.mode.* and deadlines.event.* (incl. units, timing).
- frontend/src/styles/global.css — fristen-mode-tabs, mode-panel,
event-list, event-result-row visual style.
Working-day arithmetic detail: the new addWorkingDays helper steps
one day at a time and skips runs of non-working days (Sat/Sun + DE
federal + UPC vacations seeded via paliad.holidays). Day-zero is the
caller's job — addWorkingDays(0) returns the input unchanged so
callers can decide whether to roll forward via AdjustForNonWorkingDays.
Composite-rule resolution: when a row carries alt_duration_value +
alt_duration_unit + combine_op, Calculate computes both legs,
picks max/min, and surfaces a compositeNote like
"max(31 days, 20 working_days) → working_days leg" so the UI can
explain which leg won.
PR-3 will land Tier 1 bug fixes from the audit (CCR adaptive,
UPC_APP grounds anchoring, EP_GRANT priority, rule-code normalisation).
|
||
|
|
d1909c766e |
feat: Phase C — Fristenrechner → DB-backed via FristenrechnerService
- Delete internal/calc/deadlines.go/deadline_rules.go/holidays.go (ported to services) - fristenrechner handler routes through FristenrechnerService when pool present - Returns 503 with German message when DATABASE_URL unset (page still renders) - Migration 012: add name_en columns + seed 9 UI-facing proceeding types - Commit captures cronus's work after session termination |
||
|
|
d94f8e7e25 |
feat: implement Fristenrechner (patent deadline calculator)
Go deadline engine (internal/calc/):
- 9 proceeding types: UPC (INF/REV/PI/APP), DE (INF/NULL), EPA (OPP/APP/GRANT)
- ~50 deadline rules with durations, parties, rule references
- German federal holiday computation (Easter via Anonymous Gregorian)
- Weekend/holiday adjustment with transparency (original vs adjusted dates)
- 8 unit tests covering holidays, adjustment, and full deadline chains
Frontend (Bun/TSX):
- 3-step wizard: select proceeding → enter date → view timeline
- Visual timeline with party badges, rule references, adjustment warnings
- Print-friendly layout
API: POST /api/tools/fristenrechner (protected, JSON)
GET /api/tools/proceeding-types (protected, JSON)
Route: GET /tools/fristenrechner (protected page)
Home page: Added "Werkzeuge" section with cards linking to both tools
|