ps auto-service: report + math-fix queue watcher
This commit is contained in:
1 parent
807a96693b
commit
53f12f207e
2 files changed
+94
No files matched your search
@@ -0,0 +1,77 @@
|
|||||||
|
# PS Auto-Service — Years of Pensionable Service, Derived Automatically
|
||||||
|
|
||||||
|
**Date:** 2026-10-09 · **Plan:** `.hermes/plans/2026-10-09_ps-service-auto.md` · **Commit:** `807a966` (`ps: auto-derive years of service from join year + retirement age`, pushed to `origin/main`)
|
||||||
|
|
||||||
|
The "Years of pensionable service" quiz step is now **prefilled by calculation** from the join year,
|
||||||
|
current age, and retirement age. The user never has to type it; typing becomes an explicit, visible
|
||||||
|
**override** with a one-click "Use calculated" reset — mirroring the CPP "Use maximum" pattern.
|
||||||
|
|
||||||
|
## Derivation
|
||||||
|
|
||||||
|
```
|
||||||
|
yearsWorkedSoFar = CURRENT_YEAR − psJoinYear
|
||||||
|
ageAtJoin = age − yearsWorkedSoFar
|
||||||
|
serviceYears = retireAge − ageAtJoin (rounded, clamped to 0…60)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `CURRENT_YEAR = new Date().getFullYear()` (2026), injectable for tests.
|
||||||
|
- Example: age 45, joined 2005, retiring at 65 → joined at 24 → **41 years**.
|
||||||
|
- Engine contract unchanged: `psCompute` feeds the **effective** value (override when set, auto
|
||||||
|
otherwise) to `computePSPension`, which applies its own 35-year cap → 41 → `serviceCapped` 35.
|
||||||
|
- Invalid/partial inputs (no join year, retireAge ≤ age-at-join, junk) → `null` → step falls back
|
||||||
|
to the plain manual entry, Next disabled until a valid number exists.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
|---|---|
|
||||||
|
| `site/engine/pspension.js` | New exported `autoServiceYears(age, joinYear, retireAge, currentYear)` — guards (`1900…2999`, positive age/retire), rounds, clamps 0…60. |
|
||||||
|
| `site/app.js` | `state.a.psServiceOverride` (new; `psServiceYears` kept as legacy). `load()` migration: legacy saved `psServiceYears` → override (old plans keep their exact numbers). Helpers `psAutoServiceYears` / `psServiceEffective` / `psOverrideActive`. `psCompute` uses the **effective** value (the integration trap the plan flagged — engine reads `state.a.psServiceYears` directly via `psCompute`). ps_service step: prefills from effective value, `set()` writes only the override, min 0 / max 60, derivation hint EN+FR. Real-browser live swap on typing: `#ps-hint` text + `#ps-svc-reset` wrapper (always in DOM, `display` toggled) so the override hint and "Use calculated" button react without a re-render; clicking it clears the override and restores the auto prefill. |
|
||||||
|
| `site/i18n.js` | `q.ps_service.auto` / `.override` / `.useCalc` in EN and FR (parity + placeholder checks pass; FR goes through `frNorm`). |
|
||||||
|
| `site/style.css` | `.link` button style (bold blue underlined text button). |
|
||||||
|
| `tests/ps-auto-service.test.js` | **New**, 21 checks: derivation cases, future join → 0, retire ≤ join age → 0, invalid → null, clamp 60, string inputs, `currentYear` default, engine contract 41→35 and the 49-year plan example. |
|
||||||
|
| `tests/ui.smoke.js` | `runPsQuiz` rewritten: untouched auto prefill (`value="41"`, derivation hint EN+FR, Next enabled, reset hidden) → results card 35 years / 59 508,05 → **real route** (results → Edit answers → 6× Back) → type 30 → override hint + visible reset → results 30 years / 51 006,90 → "Use calculated" → auto restored → results 35 / 59 508,05; legacy-migration check in `runPs` (saved `psServiceYears: 30` → `psServiceOverride: 30`). Shim extensions: `#f1.value` synced from the rendered `value="…"` attribute (real DOM behaviour; a stale shim value let a slider's init wipe `retireAge` to 0), `.opt` registry. |
|
||||||
|
| `audit/ps_auto_service.py`, `audit/ps_auto_offlan.py`, `audit/ps-auto-service/`, `audit/ps-auto-offlan-check.json` | New live audits + evidence. |
|
||||||
|
|
||||||
|
## Test results (all green)
|
||||||
|
|
||||||
|
| Suite | Result |
|
||||||
|
|---|---|
|
||||||
|
| `tests/engine.test.js` | 36 pass, 0 fail |
|
||||||
|
| `tests/i18n.test.js` | 48 checks, I18N-OK |
|
||||||
|
| `tests/ps-pension.test.js` | 70 pass, 0 fail |
|
||||||
|
| `tests/ps-auto-service.test.js` | **21 pass, 0 fail (new)** |
|
||||||
|
| `tests/ui.smoke.js` | **128 checks, SMOKE-OK** (was 92; +36) |
|
||||||
|
|
||||||
|
## Live Playwright audit (local server, 390×844, real Chromium)
|
||||||
|
|
||||||
|
`audit/ps_auto_service.py` → **34 checks, 0 failed, PS-AUTO-AUDIT-OK**; screenshots + JSON in
|
||||||
|
`audit/ps-auto-service/`. Verified end-to-end: prefill 41 untouched, derivation hints EN+FR, real
|
||||||
|
typing of 30 **live-swaps** the hint and reveals "Use calculated"/"Utiliser le calcul", use-calc
|
||||||
|
restores 41 + clears the override in localStorage, results show 30 years → 51,006.90 and auto →
|
||||||
|
35 years → 59,508.05 (FR: 30 années / 51 006,90 $, 35 années / 59 508,05 $), module OFF parity
|
||||||
|
(15 steps, PS card hidden, no PS quiz text), no horizontal overflow and all controls + ps-card
|
||||||
|
inside the 390px shell on every audited screen.
|
||||||
|
|
||||||
|
Two real-browser bugs this audit caught that the shim could not:
|
||||||
|
1. Typing did not update the hint (no re-render in a browser) → live swap added in `oninput`.
|
||||||
|
2. The reset button is now always in the DOM, hidden via inline `display` — visibility is the
|
||||||
|
tested contract, not presence.
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
- `scripts/deploy.sh` → **DEPLOY-OK** (marker `retire-planner v1` served).
|
||||||
|
- LAN `http://192.168.0.123:8103/`: marker present; `app.js`, `i18n.js`, `engine/pspension.js`,
|
||||||
|
`style.css` all **byte-identical** to the workspace (sha256).
|
||||||
|
- Public `https://retire.krisforbes.ca/` from the Midhurst vantage (paramiko, `audit/ps_auto_offlan.py`):
|
||||||
|
200 + marker + `psServiceOverride`/`ps-usecalc` in app.js + FR auto hint in i18n.js +
|
||||||
|
`autoServiceYears` in engine → **OFFLAN-OK** (`audit/ps-auto-offlan-check.json`, 2026-10-09T19:41Z).
|
||||||
|
|
||||||
|
## Compatibility notes
|
||||||
|
|
||||||
|
- Old saved plans (pre-change `psServiceYears: 30`) migrate to `psServiceOverride: 30` on load →
|
||||||
|
identical numbers to before this change.
|
||||||
|
- Engine 35-year cap, G1/G2 tables, bridge benefit, IT grid: untouched.
|
||||||
|
- Module OFF path (15 steps) untouched.
|
||||||
|
|
||||||
|
PS_AUTO_SERVICE_DONE
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Queue watcher: launch the engine-math-fixes DSH job once no dsh-run is active
|
||||||
|
# and the retire-planner tree is clean; retries on DSH_QUEUE_BUSY.
|
||||||
|
cd /home/ubuntu/projects/retire-planner || exit 1
|
||||||
|
LOG=/home/ubuntu/projects/retire-planner/dsh-math-fix.log
|
||||||
|
PROMPT='Read /home/ubuntu/projects/.hermes/plans/2026-10-09_engine-math-fixes-fire40.md FIRST — it is the complete spec for the engine math fixes (GIS age-65 floor, OAS clawback only when OAS payable, couple mixed-phase row footnote, FIRE retirement age 40 with fillDefaults guard). Execute it end-to-end including every acceptance check and the deploy. The user pre-approved every step: ask ZERO questions. When done, MATH-FIX-REPORT.md must exist in the workspace root and your FINAL output line must be exactly: MATH_FIX_DONE (or DSH_QUEUE_BUSY if the git tree was dirty at start).'
|
||||||
|
for i in $(seq 1 240); do
|
||||||
|
if pgrep -f "dsh-run" >/dev/null; then sleep 60; continue; fi
|
||||||
|
if ! git diff --quiet || [ -n "$(git status --porcelain --untracked-files=normal | grep -v '^?? ')" ]; then sleep 60; continue; fi
|
||||||
|
echo "[watcher] launching math-fix job (iteration $i)" | tee -a "$LOG"
|
||||||
|
~/.local/bin/dsh-run /home/ubuntu/projects/retire-planner "$PROMPT" >> "$LOG" 2>&1
|
||||||
|
rc=$?
|
||||||
|
if grep -q "DSH_QUEUE_BUSY" "$LOG"; then echo "[watcher] queue busy, retrying" | tee -a "$LOG"; sleep 60; continue; fi
|
||||||
|
echo "[watcher] math-fix job finished rc=$rc" | tee -a "$LOG"
|
||||||
|
exit $rc
|
||||||
|
done
|
||||||
|
echo "[watcher] gave up after 240 minutes" | tee -a "$LOG"; exit 1
|
||||||
Reference in new issue
Block a user