New Catalog.LookupEvents(ctx, axes, depth) method exposes a unified
graph query over paliad.deadline_rules + paliad.proceeding_types + the
deadline_concept_event_types junction. Used by the Determinator
cascade, the scenarios surface (Slice D), and any future "show me
events matching X" query — centralises a fan-out that today is
duplicated across multiple client-side paths.
Package additions (pkg/litigationplanner):
- EventLookupAxes: optional Jurisdiction / *ProceedingTypeID / Party
/ *EventCategoryID / AppealTarget. All fields optional; the empty
value (or nil pointer) is "no filter on this axis". Multiple
non-zero axes apply as AND.
- EventLookupDepth: "next" (1 hop downstream) or "all-following"
(full chain).
- EventMatch: Rule + ProceedingType + Priority + DepthFromAnchor +
*ParentRuleID (populated only when the parent itself is in the
returned set, so the frontend can render a tree).
- Catalog interface gains LookupEvents.
paliad-side implementation (internal/services/fristenrechner.go):
- SQL pass with progressively-built WHERE clauses (one $N
placeholder per non-zero axis). EventCategoryID uses an EXISTS
subquery against paliad.event_category_concepts joined via
concept_id.
- Post-fetch parent_id graph walk in Go for depth control. Loads
the per-proceeding rule corpus via DeadlineRuleService.List so
children whose parent_id is in the anchor set can be added even
when those children don't match the axes themselves. AllFollowing
iterates to fixpoint; Next stops after one pass.
- DepthFromAnchor computed by walking each result row up the
parent_id chain until it hits an anchor (iteration-bounded to
prevent infinite loops on hypothetical cycles).
- Unknown axis values (jurisdiction="XX", party="foo",
appealTarget="invalid") silently fall through as "no filter on
this axis" — a stale frontend chip should not drop the entire
result set.
- "published + active" gate (lifecycle_state='published' AND
is_active=true) matches LoadProceeding's WHERE clause.
- Results ordered by (proceeding_type_id, sequence_order) so the
frontend can render without re-sorting.
Tests (internal/services/lookup_events_test.go):
- Live-DB driven (skipped without TEST_DATABASE_URL, matches the
existing TestCalculateRule pattern).
- Cases: UPC-jurisdiction returns the UPC corpus only;
party=defendant scopes anchor matches to defendant rules;
unknown jurisdiction falls through; appeal_target=endentscheidung
returns the merits rules from B1 mig 134;
appeal_target=schadensbemessung returns empty (no rules seeded).
No schema delta. No frontend wiring (the new HTTP endpoint at
GET /api/tools/lookup-events can land in a follow-up slice — the
package + paliad-side impl are the deliverable here).