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.