Keep one ward access object per desk so the idle lock fires on signed-in desks (WF-22)
Bug
On signed-in ward desks (shared mode, any non-executive role), the 15-minute idle lock (WardIdleLock) never fires. An unattended desk stays open.
Cause
Every shared poll (about every 3 s) parses a fresh access object that is equal in value. WardFlowConnection in ward-flow-provider.tsx builds parseStoredAccess(JSON.stringify(shared.access ?? null)) on each render and passes it to an inner <WardShellAuthorityProvider>. That shadows the outer provider. React Compiler memoises on object identity, so a new object comes out each time. WardIdleLock's effect depends on access, so it clears and restarts the 15-minute timer on every poll.
5ea825c6b (28 Oct) fixed the same problem for the outer provider in ward-workspace-host.tsx only. The inner provider (from 1e5cdfa29, 27 Oct) was not covered. Executive desks are not affected because they have no inner provider.
Fix
WardShellAuthorityProvider (shell/ward-shell-public-context.tsx) now keys its context value on JSON.stringify(access) with useMemo. This applies the same JSON-key idea as 5ea825c6b at the one place both providers pass through. The object stays the same while the role and place are equal, and a real change still hands over a new object.
The fix lives in the context file, not in ward-flow-provider.tsx, on purpose. Open PRs #1605 and #1608 both edit ward-flow-provider.tsx (other lines). This way the PR overlaps no one's files.
Also adds a What's new line, because staff will see desks lock again.
Test evidence (local, at b154aaec8 on main 72d99b1cf)
All data is synthetic.
- New regression test:
tests/ward-idle-lock-shared-poll.dom.test.tsx- It re-renders the provider with a fresh, equal access every 3 s for idle time + 3 s, then expects the lock dialog.
- It also checks that the object identity holds for equal access and changes for different access.
- Command:
node scripts/run-vitest.mjs run tests/ward-idle-lock-shared-poll.dom.test.tsx - With the fix: 2 passed.
- With the context file restored to main: 2 failed (no dialog;
Object.ismismatch).
- Related suites: every test file that references
WardIdleLock,WardShellAuthorityProvider,useWardShellAuthority,ward-shell-public-context,ward-access-storeoridleLock.- Command:
node scripts/run-vitest.mjs run <those 159 files> - Result: 159 files, 1391 tests passed.
- Command:
- Lint:
eslint --max-warnings 0on the two touched TS files. Exit 0. - Typecheck:
tsc -p tsconfig.typecheck.json --noEmit. Exit 0. The pre-commit hook's lint and typecheck also passed. - Script budgets:
WARD_GATE_BUILD=1 next build --webpack, thennode scripts/check-script-budget.mjs. Every route is under its budget, and no budget changed. For example,/ward-flowis 572.7 / 574.7 KB and/ward-flow/edis 533.1 / 534.3 KB.
Hosted CI is not yet observed.