feat: add PlanningDurationService for estimating planning duration

- Implemented PlanningDurationService to provide an indicative wait time before calculation based on user input and calendar settings.
- Introduced duration estimation logic that considers horizon dates, balances, and rules without triggering a calculation.
- Enhanced user input component to display estimated duration using the new service.
- Updated translation files to include new duration estimate strings in multiple languages.
- Added tests for PlanningDurationService to ensure accurate duration estimation and calibration.
- Refactored leave planner service to remove hardcoded deadlines and improve worker responsiveness.
This commit is contained in:
nessar committed 2026-10-05 17:06:17 +02:00
1 parent 2edf2004f5
commit bc8570d8ca
29 files changed
+1064 -123

No files matched your search

+147 -9
View File
@@ -1,7 +1,7 @@
# Mobile leave-planner benchmark
Open the deployed application at `/benchmark` on the reference phone. Tap
**Run benchmark**, wait for the five scenarios to finish, then tap **Copy
**Run benchmark**, wait for the seven scenarios to finish, then tap **Copy
report** and save the tab-separated output with the date and build identifier.
The harness measures preparation, planning-to-outcome latency, later trace
@@ -22,18 +22,18 @@ stage and incumbent are retained.
warm-up, then records three measured repetitions per scenario. Do not interact
with the page while a run is active. The first measured result is the
determinism baseline; later repetitions report whether they match it.
- The original target was below five seconds for representative and loaded
scenarios, with a ten-second planning ceiling for every scenario. The owner
accepted six to seven seconds for the loaded scenario on 2026-09-25. These
are release measurements, not CI timing assertions.
- The scenarios include two and three calendar years with separately dated
annual CP and RTT balances. Let these runs finish or cancel explicitly;
there is no automatic calculation cutoff and no validated duration ceiling
for multi-year horizons. The historical annual targets and release exception
below do not establish multi-year performance.
- Keep the copied report unchanged, including any incomplete outcomes or
hardware limitations. No mobile-device measurement is claimed by the source
tree until a human records it here.
The browser page uses the same `createPlanningCalculation(request)` context as
the calendar and adds no solver or backend dependency. The ten-second release
ceiling applies to planning work; trace delivery is reported separately and
cannot replace a valid planning outcome.
the calendar and adds no solver or backend dependency. It omits `deadlineMs`.
Trace delivery is reported separately and cannot replace a valid planning outcome.
`planning_ms` is the observed latency until the outcome is usable;
`planning_work_ms` excludes trace capture. `trace_capture_ms` accounts for
@@ -42,7 +42,24 @@ building the stable explanation and finishing the trace after the outcome.
`trace_transfer_ms` measures worker transfer and receiver assembly work;
`trace_delivery_ms` measures elapsed time until the complete trace is ready. `max_ui_delay_ms` includes
planning, trace delivery, and report preparation. Trace readiness has no
ten-second ceiling and never permits truncating actual candidate evaluations.
elapsed-time ceiling and never permits truncating actual candidate evaluations.
`peak_main_thread_heap_bytes` is Chromium's available `performance.memory`
sample, taken alongside the 50 ms UI heartbeat. A blank value means this API is
unavailable. This measures the page's JS heap, excludes the worker heap, and is
not a claim about total application memory. For a browser profile, record the
isolated Chrome process-tree RSS separately during planning and subsequent
trace delivery, including the worker and renderer. State the sampling interval,
CPU throttling, viewport, and whether a warm-up preceded each measurement.
For cancellation measurements, use the normal calendar with the same saved
two- or three-year dates and balances. Cancel during preparation and an active
optimization phase, using the keyboard in one run. Record request-to-outcome
latency separately from request-to-artifacts latency; verify the partial-plan
status or absence of a plan, one planning request, no placements before the
outcome, and continued acknowledged delivery of the original trace. Confirm
that editing an input remains possible during active worker CPU work. These
measurements describe that device and workload; they impose no latency limit.
## Hardware status
@@ -52,6 +69,127 @@ six-to-seven-second loaded planning time and the observed post-result trace UI
pause. This is an explicit release exception, not a reference-phone measurement
or proof of physical-device responsiveness.
## Long-horizon calendar verification — 2026-10-05
Working implementation of issue #59 based on
`2edf2004f56f8a453a691846f3c0e6d960e27911`, using the production browser build,
native Chromium 149.0.7827.55 Web Workers, and an AMD Ryzen 7 7800X3D desktop.
The host ran Debian 13 under WSL2 (kernel 6.18.33.2-microsoft-standard-WSL2).
Headless browser emulation used 412×915 pixels, scale 2.625, touch enabled,
reduced motion, and 4× CPU throttling. Each scenario used a fresh browser and
one cold run, with no warm-up. Other validation commands were kept out of these
runs. This is desktop evidence, not a reference-phone performance claim.
The saved dates covered 2024-01-01 through 2025-12-31 or 2026-12-31 inclusive.
Each year supplied 20 CP and 10 RTT days available January 1 and expiring
December 31, matching the new benchmark scenarios. No technical deadline was
supplied. Both completed plans finished through the `dates` objective, consumed
all balances, and displayed no intermediate placements. The calendar appeared
before the full trace; its placement dates were identical after trace readiness,
and each flow sent exactly one planning request. All 1,949 and 3,956 trace
packets respectively were acknowledged, carrying 122,621 and 249,234 candidate
records without truncation. Heartbeats continued during the producing runs.
| Horizon | Calendar usable | Later trace delivery | Placements | Maximum UI timer delay before calendar / during trace | Peak page heap before calendar / during trace | Peak Chrome process-tree RSS before calendar / during trace |
| --- | --- | --- | --- | --- | --- | --- |
| Two years | 5.08 s | 2.55 s | 60 | 1.47 s / 0.70 s | 22.7 / 314.6 MiB | 1,143.2 / 1,606.8 MiB |
| Three years | 10.86 s | 4.14 s | 90 | 2.11 s / 1.48 s | 22.9 / 597.6 MiB | 1,386.8 / 2,217.8 MiB |
A 50 ms page timer measured extra event-loop delay and sampled available
main-thread JS heap. RSS was sampled every 500 ms across that isolated Chrome
process tree, including browser, renderer, worker, and auxiliary processes;
it is resident process memory, not worker JS heap. The timer delays include
initial multi-year calendar rendering and later artifact work. These pauses
and memory costs remain visible limitations; no duration or responsiveness
threshold is inferred from these samples.
Two additional cold three-year runs used the same balances in dark mode.
Cancellation during preparation returned no plan and no obligation conflict.
Keyboard cancellation during the holiday-week pass retained a valid 90-placement
incumbent marked **Calcul annulé — plan partiel**, with `at-risk` as the last
completed stage. An editable input accepted focus while the worker remained
active. Both flows kept a 44 px Cancel control, fit the viewport without
horizontal overflow, and delivered the original trace after their outcome.
| Cancellation point | Request to outcome | Request to complete artifacts | Retained placements |
| --- | --- | --- | --- |
| Calendar preparation | 21.9 ms | 874.8 ms | 0 |
| Active holiday-week optimization, keyboard | 21.5 ms | 1,431.7 ms | 90 |
No browser errors occurred. Cancellation issued one cancel request and no
additional calculation. Separate tests cover late artifact errors and stale
outcomes, explanations, and traces, preserving the latest displayed plan.
Elapsed-time tests advance clocks beyond ten seconds without waiting in CI,
including inside the real bundled worker. The seven-scenario benchmark also
checks complete traces and repeated outcome determinism for both longer horizons.
## Indicative wait and complete horizon flow — 2026-10-05
Working implementation of issue #60, preserving the existing #59 worktree
changes, based on `2edf2004f56f8a453a691846f3c0e6d960e27911`. Production build,
native Chromium 149.0.7827.55 workers, AMD Ryzen 7 7800X3D, Debian 13.6 under
WSL2 (kernel 6.18.33.2-microsoft-standard-WSL2). Headless emulation used 412×915
pixels, scale 2.625, touch, reduced motion and 4× CPU throttling. External font
downloads were blocked. No other build or test workload ran during measurement.
These are desktop samples, not physical-phone results or accuracy guarantees.
Each scenario used a fresh browser. The application's initial calculation
covered one saved day with no balances or rules; that small completed plan was
the session's sole prior calibration sample. No target-scenario warm-up was
performed. Through the actual settings and balance controls, the run then
selected January 1, 2024 through the relevant December 31 inclusive and supplied
20 CP plus 10 RTT days per year, with annual availability and expiry dates.
The saved dates and balances were checked. All edits retained the initial
calendar and issued no calculation; one explicit Calculate click submitted the
complete target configuration.
Calendar wait was measured from the captured Calculate click to DOM observation
of the displayed final placements with the loading overlay removed. This
includes request scheduling, preparation and rendering, unlike a core-work
measurement. The later trace interval starts at that calendar endpoint and ends
at the worker's terminal artifact delivery. The application's own calibration
uses its request timestamp and `afterNextRender` endpoint as described in
[planning-duration.md](../planning-duration.md).
| Horizon | Range before Calculate (with one local sample) | Observed calendar wait | Additional trace delivery | Range after completion | Placements |
| --- | --- | --- | --- | --- | --- |
| One year | 1–4 s | 1.74 s | 1.23 s | 1–5 s | 30 |
| Two years | 2–16 s | 5.08 s | 2.88 s | 2–16 s | 60 |
| Three years | 7–44 s | 11.03 s | 5.60 s | 6–39 s | 90 |
All three outcomes completed through `dates`, with no intermediate placements,
implicit deadline, extra request or date movement. The three-year result
finished after ten seconds. Calendars and estimates remained identical during
the later trace interval. All 674, 1,948 and 3,959 trace packets respectively
were acknowledged. These isolated samples establish behavior, not a validated
numerical prediction error or duration ceiling. The rough model before any
local sample is separately documented; it does not assume linear growth with
the number of years.
Two dark-mode native-worker cancellation flows covered the same manually
submitted three-year configuration. Holding the new worker's script download
until the explicit Cancel click made cancellation before any valid plan
reproducible: no plan or conflict was presented, and the indicative 7–46 s range
was unchanged. Its empty original trace was already available when the calendar
rendered. A keyboard cancellation in active holiday-week optimization retained
90 placements, displayed **Calculation cancelled — partial plan**, and left its
7–44 s range unchanged through later trace delivery. Both Cancel controls were
44 px high, and balance inputs accepted focus while the calculation was active.
The controlled startup download is not cancellation-latency evidence.
A separate three-year run completed in 10.96 s while native trace-update events
were held back. The calendar was usable with an explicitly loading trace;
injecting a subsequent worker transport error preserved all 90 placements and
the newly refined 6–38 s estimate. Controlled-clock tests additionally keep a
trace unresolved for five minutes after a twenty-second calendar wait, verifying
that subsequent delivery/error time contributes no calibration sample.
No browser errors or horizontal overflow occurred. The annual flow also fit
375, 768 and 1440 px widths. The estimate uses the existing secondary-text
theme token and is associated with Calculate through `aria-describedby`.
Settings restoration, stale calculations, rule/balance edits, long-worker
liveness, and cancellation history are also exercised by the integration suite.
## Emulated browser run — 2026-09-25
Chromium 149.0.7827.55 on an AMD Ryzen 7 7800X3D (x86_64), headless at