# Integrated planning verification after trace removal Functional verification for [#77](https://gitea.nessar.fr/nessar/vac-optimizer/issues/77), run on 2026-10-15 against `2f4af14869c47836d2c1008157136f75c9d3e98b` (`develop`, including the merged #76 implementation). The original checkout was clean at `d9d9befa1256cd2200aa4778d28b2bc23ba141e6`; both commits have the same tree. This verification adds two real-worker lifecycle tests and this report. It does not change production behavior, saved data, placement expectations, benchmark inputs, CI settings or performance limits. ## Contract audit | Path | Verified retained contract | | --- | --- | | Synchronous and cooperative core | Outcomes, valid checkpoints, final-state snapshots, stable explanations and explicit cancellation; no recorder, candidate history or historical replay API. | | Server calculation context | Immutable submitted configuration, independently delivered outcome and explanation, and displayed-placement date analysis. | | Browser without Worker | Validation and explicit `worker-unavailable` handling; no synchronous planning or trace compatibility result. | | Native worker and coordinator | Heartbeat, progress, checkpoint, result, explanation and final artifacts. The coordinator terminates the worker after required delivery with no acknowledgement message or queue. | | Calendar and remaining balances | Final and valid partial calendars, placement provenance, objective comparisons, earned sources, validity dates and concrete non-placement reasons. | | Alternative dates | Captured horizon, entitlement, type and rule provenance; unrelated placements stay fixed. Hypothetical comparisons never update the displayed plan. | | Benchmark callers | Supported calculation outcome, explanation and timings; no trace promises, waits, counts or replacement trace metrics. | Production-source searches found no decision-trace, historical analysis, acknowledgement or replay implementation. Angular hydration's `withEventReplay` is unrelated to planning history. `PlanningArtifacts` carries actual explanation, final-state and planning-work timing data, rather than a compatibility shell. ## Existing deterministic fixtures The full functional suite passes without changing any existing expected placement, balance, metric, objective, chronology or completed-stage assertion. | Requirement | Existing regression coverage | | --- | --- | | Typed joint obligations and shared provenance | `leave-planner.service.spec.ts`, `leave-planner-explanation.spec.ts` and real-worker tests. | | Earned-entitlement chains and availability | Integration, explanation, supporting-placement and real-worker tests, including declarations and cancellation. | | Custom formulas and reordered priorities | Optimization, calendar-objective, work-sequence and supporting-placement tests, including real-worker comparisons. | | Two- and three-year horizons | Service, calendar and worker tests retain the submitted dates and deterministic completed plans. | | No active optimization rules | Optimization and migration tests retain declarations and remaining balances without adding placements. | | Stable explanations and remaining reasons | Entitlements, every contributing rule, ordered objective values, first decisive objective, periods and dated non-placement reasons. | | Cancellation and failures | Before and after a jointly valid incumbent, stale calculations and date analyses, worker liveness, technical budgets, and local explanation errors. | | Saved settings | Existing migration and persistence tests preserve user rule choices, balances, declarations and explicitly selected horizons. | An additional direct comparison uses the pre-removal core from `ea42561`, with only its relative imports redirected to the unchanged calendar/date/objective modules. Both versions receive the same cloned requests and calculation date `2024-01-01`. Full outcomes and stable explanations are compared with deep equality; no placement expectations or retained explanation fields are filtered. All ten completed comparisons match for both the full outcome and stable explanation: | Fixture | Outcome | Placements | Last completed stage | | --- | --- | --- | --- | | Representative year | `Plan` | 30 | `dates` | | Typed joint obligations | `Plan` | 10 | `dates` | | Chained earned entitlements | `Plan` | 3 | `dates` | | Deliberate conflict | `PlanningConflict` | 0 | — | | Two years | `Plan` | 60 | `dates` | | Three years | `Plan` | 90 | `dates` | | Holiday value before non-working-day gain | `Plan` | 3 | `dates` | | Non-working-day gain before holiday value | `Plan` | 3 | `dates` | | Custom maximum holiday-week aggregation | `Plan` | 3 | `dates` | | No active optimization, with a declared day | `Plan` | 0 | `dates` | The reordered priorities retain their different placement dates: 6, 7 and 10 May for holiday value first; 29 April, 10 May and 17 May for gain first. The zero-rule case retains its declaration. These are correctness comparisons, not timed benchmark repetitions. An exploratory pre-removal loaded-year replay was stopped and is not used as passing evidence; its full protocol remains with #78. ## Correctness and static checks Environment: Node `24.18.0`, Bun `1.3.14`, Linux under WSL2. ```sh NODE_OPTIONS=--max-old-space-size=2048 bunx vitest run --coverage --maxWorkers=1 --exclude='src/app/benchmark/**' bunx tsc -p tsconfig.app.json --noEmit bunx tsc -p tsconfig.spec.json --noEmit bun run lint bun run format:check bunx prettier --check 'scripts/ci-*.mjs' '.gitea/workflows/*.yml' node scripts/ci-policy.test.mjs node scripts/ci-image.test.mjs bun run build git diff --check ``` All checks pass. Functional coverage: **632 tests across 33 files**, 93.56% statements, 85.90% branches, 95.69% functions and 95.41% lines. The full coverage run took 95.67 seconds with the existing 2 GB Node heap setting. The focused real-worker file passes all 15 tests, including the two added lifecycle checks. Coverage configuration and thresholds are unchanged. The production build retains its existing warning budgets: initial bundle 2.57 MB against the 2.50 MB warning and calendar-year styles 12.98 kB against the 8 kB warning. Both remain below their unchanged error limits. ## Browser QA The production build was served locally with `NG_ALLOWED_HOSTS=localhost,127.0.0.1 PORT=4177` and exercised in headless Chromium `149.0.7827.55`, using native module Workers, reduced motion and 375×915, 768×915 and 1440-pixel-wide viewports. The calculation date was fixed to `2024-01-01`. This is desktop functional evidence, not a phone performance run. | Flow | Result | | --- | --- | | Completed calendar and explanation | Three CP placements on 6, 7 and 10 May 2024, six gained non-working dates; keyboard disclosure, placement navigation and calendar selection work. | | Remaining balances | Future RTT availability and unavoidable expiry show their actual dates, reasons and planning horizon without attempt/history controls. Earned-source rendering is also covered by component tests. | | Date comparison | An alternative on 13 May is hypothetical; placements and selected calendar dates remain unchanged. Escape closes the dialog and restores focus to Compare dates. | | Immutable settings | Persisting a current CP balance edit from 3 to 99 leaves the displayed three placements unchanged. Select another placement and verify that a new comparison request is sent after the edit; that request still contains the captured quantity 3 and original horizon. | | Cancellation before a valid plan | At 375 px, hold the native worker script download, activate Cancel with Enter, then release startup. Result is `PlanningIncomplete` / `user-cancelled`, without an incumbent or a false conflict. This controlled startup is not latency evidence. | | Cancellation after a valid plan | At 375 px, use the unchanged loaded-year request and cancel with Enter after a real checkpoint. All three typed obligation windows remain satisfied, placements plus remaining balances total 72, and the partial explanation is available. Inputs remain editable during planning; Cancel is at least 44 px high. | | Delayed and stale delivery | At 375 px, hold explanation and artifact messages after a real outcome. The three-placement calendar is usable immediately. Recalculate, then release the old messages; the current result and explanation remain intact. | | Explanation failure | At 375 px, inject explanation transport and checkpoint-explanation recovery failures. An inline error appears while the completed calendar and all three placements remain available. | | Responsive and keyboard interaction | No page-level horizontal overflow at 375, 768 or 1440 px. Comparison dialogs fit, retain keyboard focus and restore it on dismissal. No unexpected browser page errors. | | Removed UI | No advanced trace explorer, historical overlays, replay controls, candidate-status UI or remaining-balance attempt controls. | Controlled-clock transport tests additionally verify that heartbeats renew the 30-second inactivity watchdog across repeated windows and that monitoring ends at outcome delivery. Native-worker tests verify actual heartbeat delivery, incumbent validity and retained explanations. No automatic engine cutoff is added. ## Handoff to #78 The #70 → #71 → #72 → #73 → #74 → #75 dependency order is preserved. The tracker already reports those six tickets and #76 closed. This work verifies their functional integration and does not close or modify any other ticket. The benchmark source and its original correctness assertions remain unchanged from the integrated baseline: | File | SHA-256 | | --- | --- | | `src/app/benchmark/leave-planner-benchmark.ts` | `16e057fa042c898d203f71e018f183c9fbecb5cd0edbf0ce0f3f25277cb00d3a` | | `src/app/benchmark/leave-planner-benchmark.spec.ts` | `c7b4c37b6c169b42c4d18dfc3e650b25a2ba47415c97268dafefafbfb0ee3023` | All seven scenarios, typed and earned-chain fixtures, two-/three-year horizons, 60-/90-placement expectations, determinism comparisons and final `dates` stage assertions remain in place. Warm-ups and measured repetitions remain unchanged. The full 2 GB / 300-second benchmark protocol and any required optimization stay open in [#78](https://gitea.nessar.fr/nessar/vac-optimizer/issues/78); they are not a completion gate for #75 or #77. This run makes no performance acceptance or trace-removal speedup claim. The separate advisory benchmark status must be resolved by #78 under the existing limits. Local run logs, the original spec and checkout record, comparison scripts and browser screenshots are in `/tmp/vac-issue-77`. The source fixtures and commands above identify the reproducible correctness checks; temporary QA controls are not part of the application.