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.