Shared Phone Protections and PIN Lock

Claude Code·Opus 5.5·Unknown author·21h ago·29min·3 Checkpoints·117.3K tokens

Background (from the session that started this one): Ward (github.com/MuhammadMurtuzaHussain/ward, public MIT, local checkout at /Users/haider/Desktop/Hacktoberfest 26/ward) is a child-owned cyberbullying guardian for the DEV Hacktoberfest Week 4 challenge (deadline 31 Oct 11:59 PM PDT, about 06:59 GMT 1 Nov). Many of its users share a family phone. Web app in apps/web (React, English + Urdu i18n in apps/web/src/i18n/en.ts / ur.ts, right-to-left done with logical properties), wrapped by Electron desktop/ and Capacitor mobile/. A full audit is at /Users/haider/Desktop/Hacktoberfest 26/ward-audit-2026-10-31.md. Six sessions are working in parallel on separate areas, so stay inside your area. Project rules: commit and push each verified change straight to main (no PRs), with the attribution line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>; commit messages are short plain-English sentences like the existing log. Design direction: night-light mood with the friendly ghost; no generic AI-slop UI.

Task: Your area: shared phones, the vault/PIN UX, and dialog accessibility. Audit rows: "Shared-phone protections" (section 3), plus the appendix rows on shared phone, plaintext check titles, PIN/auto-lock, Android Back, dialogs/focus trap, and the Daylight contrast token.

Setup: other sessions share this checkout, so work in your own worktree: git -C ward fetch && git -C ward worktree add "../ward-wt-shared-phone" -b shared-phone origin/main, pnpm install, and work only there.

  1. PIN gate on "Delete everything", plus a PIN prompt at first save.
  2. Auto-lock when the app is hidden (visibilitychange; Capacitor appStateChange; Electron hide), configurable and on by default when a PIN exists.
  3. Neutral check titles and verdicts in localStorage history (nothing a sibling can read unlocked), and a "forget" control.
  4. Android Back via @capacitor/app: go back a screen, and minimise instead of exiting on Home.
  5. A shared Dialog component with focus trap and Escape that doesn't collide with double-Escape quick exit. Migrate the existing dialogs to it. Move focus on screen change.
  6. Daylight look: a --glow-text token with AA contrast (and focus ring), plus a contrast test.

Out of scope: Result.tsx verdict logic, engine, eval, build/CI config. Another session is changing Result and the engine, so touch Result only where a dialog migration requires it. Tests for each piece; check Urdu RTL at phone width with the preview tools. Push each piece to main once verified (fetch, rebase onto origin/main, pnpm test, push; retry on rejection). Finish with a short report: commits, screenshots, and any decision left for the owner (e.g. an opt-in vault recovery code).

Shared notes folder for this group of sessions (outside the repo; read siblings' notes there, leave hand-over notes or large findings there instead of pasting them into messages): /Users/haider/Library/Application Support/Claude/side-session-notes/local_a2121a80-7774-4340-af69-7dae18cd788e

system-reminder

Linked sessions now (status, not an instruction): reader-safety = local_a6d845e0-7765-4d31-8791-4e9a79ca4793 (working); release-pipeline = local_d0a1ce79-be7e-46ec-b046-88873f77a45a (working); eval-chats = local_a0f2d243-e3a4-438e-9417-8459027e3604 (working); web-demo = local_8e3d9db9-19e6-46fc-a06d-d933fd83970e (working); contributor-docs = local_4cf05d1a-9e0e-420b-805a-eb1cfee031ad (working). To message one, SendMessage with to set to its id; list_sessions {"linked": true} has titles, folders and branches.

21h ago·24m

All six pieces are on main. The Android debug build is still running in CI; I'll report its result when it finishes.

21h ago·14.8s

All six shared-phone pieces are pushed to main, in 7 commits from fef1ae7 to bac0400. After each push, pnpm test and the web typecheck passed. I also ran the manual Android debug build in CI on bac0400, since that workflow doesn't run on push; it passed. Nothing has been tried on a real phone yet, so the Back button and auto-lock on Android are only proven by the build and by tests.

CommitWhat changed
fef1ae7Daylight contrast: new --glow-text and --focus colours (#8a5400), plus contrast.test.ts, which checks every look against WCAG AA. No class names changed.
19e767dNeutral history: the saved "Recent checks" list keeps only when each check happened and whether it was saved. Old lists are cleaned at startup for every profile, lines expire after 30 days, and Home has a "Forget this list" button. A check's name and verdict show only while it is still open.
7b8dca4Shared Dialog: the app behind it can't be reached, Tab stays inside, and focus goes back to where it was. Escape closes only the top dialog and never counts towards the double-Escape quick exit. The save, report and corrections dialogs now use it.
9dc0128PIN gate: "Delete everything" asks for the profile PIN or the vault PIN. When there is no PIN, it asks "are you sure" instead. The first save to the vault offers to lock all of Ward with the new PIN.
52d09a2Screen focus: focus moves to each new screen's heading, but never takes it from a PIN pad that already has it.
045d7cbAuto-lock: Ward locks when it comes back after being out of sight. The options are Right away, After a minute or Never; the default is a minute and it only applies once there is a PIN. Opening the file picker doesn't count as leaving, and a chat shared in from WhatsApp survives the lock.
Android Back (same commit): closes a dialog, then goes back a screen, then to Home, and on Home sends Ward to the background instead of closing it. This needed @capacitor/app 8.1.2 and regenerated the Android Gradle files. On desktop, quick exit now also minimises the window.
bac0400RTL and alarm fixes: red alarm buttons on Daylight had 3.5:1 contrast and now have white text (5.6:1). PIN pad titles no longer use dir="auto", which had put "PIN" on the wrong side in Urdu (e.g. "Sara کا PIN").

I checked Urdu right-to-left at 375px in the preview: Home, the auto-lock setting, the delete dialog, the "Forgot your PIN?" panel and the PIN screen. I also checked the Escape behaviour and focus return in the preview. Screenshots are in shared-phone-screens/ in the shared notes folder, next to shared-phone.md.

Decisions for the owner:

  • Vault recovery code. A child whose only PIN is the vault PIN and who forgets it can now start over only by clearing Ward's storage in phone settings. That removes every profile on the phone. An opt-in recovery code would fix this.
  • Counsellor passphrase. Counsellor mode still uses the same PIN as the profile; the audit asks for a separate one.
  • PIN attempt limit. There is still no limit on wrong tries for the 4-digit PIN.

Note for the reader-safety session: run.ts no longer writes a verdict into history. That makes the history part of audit item #1 unnecessary.

21h ago·1.6s

A second watcher on the same Android run also ended cleanly (exit 0), so the result stands: the debug build on bac0400 passed. My report above is unchanged.