refactor(planning): remove the original decision trace
CI / policy (push) Successful in 7s
CI / validate (push) Successful in 1m15s
CI / test (push) Successful in 3m18s
CI / benchmark (push) Failing after 5m53s
CI / build (push) Successful in 1m23s
CI / security (push) Successful in 7m6s
CI / policy (pull_request) Successful in 7s
CI / validate (pull_request) Successful in 1m9s
CI / build (pull_request) Successful in 43s
CI / test (pull_request) Successful in 3m19s
CI / images (push) Successful in 3m7s
CI / required (push) Successful in 2s
CI / benchmark (pull_request) Failing after 5m28s
CI / security (pull_request) Successful in 6m37s
CI / images (pull_request) Successful in 3m18s
CI / required (pull_request) Successful in 1s
CI / policy (push) Successful in 7s
CI / validate (push) Successful in 1m15s
CI / test (push) Successful in 3m18s
CI / benchmark (push) Failing after 5m53s
CI / build (push) Successful in 1m23s
CI / security (push) Successful in 7m6s
CI / policy (pull_request) Successful in 7s
CI / validate (pull_request) Successful in 1m9s
CI / build (pull_request) Successful in 43s
CI / test (pull_request) Successful in 3m19s
CI / images (push) Successful in 3m7s
CI / required (push) Successful in 2s
CI / benchmark (pull_request) Failing after 5m28s
CI / security (pull_request) Successful in 6m37s
CI / images (pull_request) Successful in 3m18s
CI / required (pull_request) Successful in 1s
This commit is contained in:
1 parent
ea4256107f
commit
d9d9befa12
43 files changed
+729
-8880
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# Cancelling a planning calculation
|
||||
|
||||
Issue [#58](https://gitea.nessar.fr/nessar/vac-optimizer/issues/58) implements the cancellation portion of ADR 0006. Issue #59 removes the automatic deadline while keeping the same cancellation and original-trace protocol. The duration estimate remains in issue #60.
|
||||
Issue [#58](https://gitea.nessar.fr/nessar/vac-optimizer/issues/58) implements the cancellation portion of ADR 0006. Issue #59 removes the automatic deadline. Issue #76 removes decision tracing while preserving cancellation and valid-plan recovery. The duration estimate remains in issue #60.
|
||||
|
||||
`PlanningCalculationContext.cancel()` requests user cancellation once while its outcome is pending. `CalendarYear` keeps an accessible Cancel button visible during preparation, obligation search, and optimization. It announces the request, disables duplicate cancellation, and moves keyboard focus to the result heading when the outcome arrives. Cancellation never triggers Calculate. A subsequent explicit Calculate creates a fresh immutable calculation snapshot.
|
||||
|
||||
@@ -10,25 +10,25 @@ The core exposes resumable generator steps. `calculatePlanningAsyncInternal` dri
|
||||
|
||||
The synchronous `calculatePlanningInternal` adapter drains the same steps without pauses for existing direct core callers. The public non-browser calculation context uses the asynchronous driver so its cancellation method also works.
|
||||
|
||||
User cancellation is distinct from replacement by another calculation (`stale-calculation`) and isolated worker failure (`worker-error` or `worker-unavailable`). Replacement retains the existing termination and stale-response protection. User cancellation leaves the producing worker alive until its explanation and trace have been delivered.
|
||||
User cancellation is distinct from replacement by another calculation (`stale-calculation`) and isolated worker failure (`worker-error` or `worker-unavailable`). Replacement retains the existing termination and stale-response protection. User cancellation leaves the producing worker alive until its outcome and explanation data have been delivered.
|
||||
|
||||
## Retaining a valid plan
|
||||
|
||||
The asynchronous calculation retains a copied incumbent as soon as the obligation search finds a jointly valid solution, before that entire stage has completed. It also considers accepted placements and every feasible evaluated quality candidate, including candidates rejected by a stage's narrower comparator. Each candidate is reconciled for earned entitlement availability and checked against all obligation rules before retention. The existing full lexicographic comparator chooses the best available incumbent; its comparisons are recorded in the original trace.
|
||||
The asynchronous calculation retains a copied incumbent as soon as the obligation search finds a jointly valid solution, before that entire stage has completed. It also considers accepted placements and every feasible evaluated quality candidate, including candidates rejected by a stage's narrower comparator. Each candidate is reconciled for earned entitlement availability and checked against all obligation rules before retention. The existing full lexicographic comparator chooses the best available incumbent.
|
||||
|
||||
Retention reuses quality metrics already computed for an evaluated candidate. An intermediate state with more unplaced at-risk leave, or worse preferences at equal unplaced leave, is rejected using that objective prefix alone, with the actual short-circuited comparison recorded in the trace. This avoids rescoring dominated states during large obligation previews without skipping any produced history or a candidate that could improve the retained plan.
|
||||
Retention reuses quality metrics already computed for an evaluated candidate. An intermediate state with more unplaced at-risk leave, or worse preferences at equal unplaced leave, is rejected using that objective prefix alone. This avoids rescoring dominated states during large obligation previews without skipping a candidate that could improve the retained plan.
|
||||
|
||||
Preference occurrence dates and patterns are calculated once for the run's immutable calendar and rules. Each comparison still checks the candidate's actual placements against those patterns; candidate scores and trace records are not cached.
|
||||
Preference occurrence dates and patterns are calculated once for the run's immutable calendar and rules. Each comparison still checks the candidate's actual placements against those patterns; candidate scores are not cached.
|
||||
|
||||
On cancellation, the core materializes the retained state with consistent placements, balances, periods, rule provenance, and preference satisfaction. It returns `PlanningIncomplete` with reason `user-cancelled`. With an incumbent the French status is **Calcul annulé — plan partiel**; without one it is **Calcul annulé**, without a plan or an obligation conflict. Temporary search states never become the displayed incumbent.
|
||||
|
||||
## Preserving the original trace
|
||||
## Delivering the outcome and explanation
|
||||
|
||||
Cancellation follows the core's normal finish path. Quality evaluations are recorded before their incumbent comparisons, and each greedy placement attempt starts its own atomic decision. The recorder closes interrupted and unexecuted phases and drains every remaining operation from that run. The worker retains staged updates, including ones not yet posted, and continues its bounded, acknowledged trace delivery after optimization has stopped. It sends `artifacts-complete` only when all queued updates have been sent and acknowledged. The facade drains its own received update queue before resolving the trace. Neither layer replays planning to reconstruct this history.
|
||||
Cancellation follows the normal finish path. The stable explanation is derived from the retained state. Outcome and explanation have separate promises: delayed or failed explanation delivery cannot replace or remove an already settled valid partial plan. Repeated and late outcome messages cannot change that outcome, and calculation identities protect the UI against obsolete results and explanations.
|
||||
|
||||
The stable explanation is derived from the retained state. The outcome, explanation, and trace have separate promises: delayed or failed artifact delivery cannot replace or remove an already settled valid partial plan. Repeated and late outcome messages cannot change that outcome, and calculation identities protect the UI against obsolete results and artifacts.
|
||||
Immutable `PlanningStateSnapshot` values preserve exact entitlement metadata, earned activations, declarations and placements for checkpoints and the final state. A worker failure retains the latest delivered valid checkpoint and can recover its stable explanation in a separate worker without rerunning planning. Displayed-placement alternatives use the captured configuration and state; no original decisions or historical baselines are retained.
|
||||
|
||||
If Cancel is accepted just before a queued natural result reaches the facade, the pending outcome still becomes a user cancellation. An already computed valid plan can be retained, while a queued conflict cannot become the cancellation result. Its existing explanation receives interruption metadata, and the original phase history is preserved even when the worker had already completed those phases.
|
||||
If Cancel is accepted just before a queued natural result reaches the facade, the pending outcome still becomes a user cancellation. An already computed valid plan can be retained, while a queued conflict cannot become the cancellation result. Its existing explanation receives interruption metadata. The planning worker terminates after delivering the required artifacts; it never waits for trace acknowledgements.
|
||||
|
||||
## Verification
|
||||
|
||||
@@ -42,10 +42,10 @@ bunx tsc --noEmit -p tsconfig.app.json
|
||||
bunx tsc --noEmit -p tsconfig.spec.json
|
||||
```
|
||||
|
||||
The public cancellation tests cover immediate cancellation, queued trace updates on both sides of the request, a stable partial outcome despite late completion or artifact error, distinct worker failure and replacement (including non-browser explanation metadata), and cancellation after settlement. A core regression compares the retained plan's full objective vector with every valid candidate actually evaluated before cancellation. The CalendarYear tests cover keyboard activation, translated statuses with and without an incumbent, focus, no implicit recalculation, explicit recalculation, and obsolete trace delivery.
|
||||
The public cancellation tests cover immediate cancellation, a stable partial outcome despite late completion or artifact error, distinct worker failure and replacement (including non-browser explanation metadata), and cancellation after settlement. Core regressions verify retained validity, provenance, earned chronology and the best available partial plan through the existing checkpoint seam. The CalendarYear tests cover keyboard activation, translated statuses with and without an incumbent, focus, no implicit recalculation, explicit recalculation, and obsolete explanation delivery.
|
||||
|
||||
The real-worker suite bundles the production worker source with Bun and runs it on a separate Node worker thread. Its small host adapter maps browser worker messages to the thread's message port; the planner, CPU work, cancellation handling, and acknowledged trace protocol are real. It checks interruption after preparation has started, before the first obligation solution, during active obligation CPU work after a solution exists, and after a completed checkpoint. It also compares an uncancelled outcome with the synchronous adapter's deterministic result. These checks complement the controlled worker boundary tests.
|
||||
The real-worker suite bundles the production worker source with Bun and runs it on a separate Node worker thread. Its small host adapter maps browser worker messages to the thread's message port; the planner, CPU work, cancellation handling, and outcome and explanation delivery protocol are real. It checks interruption after preparation has started, before the first obligation solution, during active obligation CPU work after a solution exists, and after a completed checkpoint. It also compares an uncancelled outcome with the synchronous adapter's deterministic result. These checks complement the controlled worker boundary tests.
|
||||
|
||||
For a browser check, serve the production browser build and use native Chromium Web Workers with a saved annual horizon. Check cancellation during calendar preparation, a broad obligation search, and a quality pass. Confirm one planning request per Calculate, no new request on Cancel, the appropriate translated status, joint rule satisfaction and balance accounting for an incumbent, and complete trace delivery after the outcome. Also check the Cancel control with the keyboard, at 375px width, and in light and dark themes.
|
||||
For a browser check, serve the production browser build and use native Chromium Web Workers with a saved annual horizon. Check cancellation during calendar preparation, a broad obligation search, and a quality pass. Confirm one planning request per Calculate, no new request on Cancel, the appropriate translated status, joint rule satisfaction and balance accounting for an incumbent, and stable explanation delivery after the outcome. Also check the Cancel control with the keyboard, at 375px width, and in light and dark themes.
|
||||
|
||||
The production build was checked in native Chromium for all three interruption points, including keyboard cancellation during a 375px dark-theme obligation search and desktop light-theme checks. An editable input could still receive focus during CPU work, and focus moved to the result heading after keyboard cancellation. Each run sent one planning request and one cancellation request, displayed the appropriate French status, and completed acknowledged artifact delivery. The Cancel control was 44px high and the page had no horizontal overflow. In these sample runs, cancellation through artifact completion took under 200ms; this is measured evidence, not a latency guarantee.
|
||||
Earlier browser checks before #76: the production build was checked in native Chromium for all three interruption points, including keyboard cancellation during a 375px dark-theme obligation search and desktop light-theme checks. An editable input could still receive focus during CPU work, and focus moved to the result heading after keyboard cancellation. Each run sent one planning request and one cancellation request, displayed the appropriate French status, and completed acknowledged artifact delivery. The Cancel control was 44px high and the page had no horizontal overflow. In these sample runs, cancellation through artifact completion took under 200ms; this is measured evidence, not a latency guarantee.
|
||||
Reference in new issue
Block a user