Scaffolds EPFO/UAN verification the same way GST works — a standalone Playwright
proxy microservice + an /api proxy + an assisted affordance that records the
result. Aadhaar stays manual (UIDAI-restricted). Stacks on the follow-ups branch.
Behind NEXT_PUBLIC_CREWING_ENABLED.
What's in
- EpfoService/ (new microservice, GstService pattern): Express + Playwright.
POST /otp {uan} → session + OTP request; POST /verify {sessionId,uan,otp} →
member record; GET /health. EPFO is OTP-gated (no anonymous captcha lookup like
GST), so the handshake is two steps. Live portal navigation is gated behind
EPFO_LIVE (default STUB: OTP 000000 → matched) until real selectors/OTP are
validated. README documents the differences + that Aadhaar is out of scope.
- App: /api/epfo/otp + /api/epfo proxies (gated by verify_bank_epf) to
EPFO_SERVICE_URL. EpfDetail += epfoMemberName + epfoCheckedAt (migration
crewing_epfo_check). recordEpfoCheck action persists the EPFO result + audit.
- UI: an "EPFO check" affordance on the verification EPF rows — request OTP →
enter OTP → matched member → record. Aadhaar noted as manual-only.
Tests & docs
- Integration: verification.test.ts gains recordEpfoCheck (records name+timestamp,
Accounts-only gating). type-check clean; full unit (245) + integration (213)
green (RESEND_API_KEY unset).
- .env.example (EPFO_SERVICE_URL/EPFO_LIVE), CLAUDE.md, EpfoService/README.
Note: the EpfoService live portal selectors/OTP are stubbed and must be validated
against a real EPFO session before enabling EPFO_LIVE.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Clears the self-contained deferrals tracked across phases. Stacks on 5b appraisal.
Behind NEXT_PUBLIC_CREWING_ENABLED.
- SITE_STAFF login on onboard/placement (Epic D follow-up): lib/crew-login.ts
maybeCreateSiteStaffLogin creates a passwordless SITE_STAFF User (sharing the
CRW- employee no., siteId = the assignment's site) when a grantsLogin rank is
onboarded (onboardCandidate) or placed (placeCrew) and the crew member has an
email. No-op otherwise.
- Own-site scoping (Epic E follow-up, §8.7): User.siteId added (migration
crewing_followups); the Crew directory filters a SITE_STAFF user with a home site
to crew whose active assignment is at that site (graceful when unset). The link is
set at login creation.
- PPE / next-of-kin verify gates (Epic F/I follow-up): PpeIssue/NextOfKin gained
verificationStatus + verifiedById; verifyPpe / verifyNextOfKin (verify_site_records,
MPO) + queue sections in /crewing/verification.
Tests & docs
- Integration: crewing-followups.test.ts (6) — login created/skipped by rank+email
(+ siteId set), PPE/NoK verify + reject-reason + already-decided guard + gating.
type-check clean; full unit (245) + integration (211) green (RESEND_API_KEY unset).
- CLAUDE.md updated.
Part of Epic D (#78), Epic E (#79), Epic F (#80), Epic I (#83).
Still deferred (not self-contained): public careers API (A2); Pay-status pay rows (Phase 6).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>