feat(crewing): land full Crewing module on master (Phases 1–5 + hardening) #93
No reviewers
Labels
No labels
bug
claude-failed
claude-pr
claude-queue
claude-working
epic
feature
interactive
portal
triaged
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: shad0w/pelagia-portal#93
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/crewing-review-hardening"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Lands the complete Crewing module on
masterin one clean merge.Phases 2–5 were merged up the stacked-PR chain (each PR into its base branch) rather than into
master, so only Phase 1 foundations (#64) reached master. This branch is the stack tip — it contains every phase plus the reviewed hardening from #90, and trial-merges into master with no conflicts. Merging this supersedes the open stacked crewing PRs (#65–#90), which can then be closed.Lands: Phase 1 foundations; Phase 2 requisitions + relief + clash auto-backfill; Phase 3 candidates / 7-stage pipeline / onboarding; Phase 4 crew records + PPE, leave/attendance, sign-off; Phase 5 verification + appraisal + EPFO; plus hardening (PII masking, onboarding D1/D3, vetting gates C5+C3, audit-type fixes + atomic backfills, ex-hand recognition, requisition A3, EPFO tests).
Verification: 249 unit + 242 integration green, type-check clean. Stays behind NEXT_PUBLIC_CREWING_ENABLED.
First slice of Phase 3 (Epics B/C/D shipped as stacked sub-PRs). Adds the CrewMember talent-pool spine and the Candidates screens. Behind NEXT_PUBLIC_CREWING_ENABLED; production unchanged. Stacks on the requisitions branch (Phase 2). What's in - Schema (crewing_candidates migration): CrewMember (spine) + CrewStatus, CandidateType, CandidateSource enums; CrewAction gains a nullable crewMemberId; CrewActionType += CANDIDATE_ADDED/UPDATED. employeeId is assigned at onboarding (3c), so it's nullable here. - Actions (crewing/candidates/actions.ts): addCandidate / updateCandidate — guard flag + manage_candidates, write a CrewAction, optional CV upload via buildStorageKey("cv", …) + uploadBuffer (no parsing — A2 deferred). EX_HAND source ⇒ type/status EX_HAND; edits never downgrade an EMPLOYEE. - Screens: /crewing/candidates (master list with search/source/rank-applied/ min-experience filters as removable chips + match count + Clear all; Add-candidate modal) and /crewing/candidates/[id] (profile; pipeline stepper is 3b). Candidates added to the flag-gated Crewing nav (Manager + MPO). Tests & docs - Integration: candidates.test.ts (7) — add/update, ex-hand derivation, employee no-downgrade, permission gating. type-check clean; full unit (225) + integration (153) suites green. - CLAUDE.md "Crewing" section updated with the Phase 3a surface. Deferred: public careers intake API (A2, §13 open question); CV parsing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>Replace the implicit "strength = 1" clash rule with a configurable per-vessel, per-rank requirement (director decision). Adds VesselRankRequirement {vesselId, rankId, minStrength} (migration crewing_vessel_rank_requirement) and reworks lib/leave-clash.ts → leaveCausesClash: a leave approval clashes when the remaining active same-rank cover over the window would fall below minStrength (default 1 when unconfigured), auto-raising a LEAVE requisition. The requirement is managed by the office (manage_crew, admin UI in the follow-up). - Integration: leave-attendance.test.ts gains a configured-strength case (minStrength 2, one remaining → clash). Full unit (240) + integration (183) green. - CLAUDE.md updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>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>- C5 (Epic C5 AC1): advanceStage("verify_competency") now requires ≥1 ReferenceCheck before leaving COMPETENCY_AND_REFERENCES. - C3 (Epic C3 AC1): verifyDocuments blocks advancement when a mandatory document for the seat's rank that the candidate holds is expired. Missing-document presence stays enforced post-onboarding in the verification queue (seafarer docs aren't collected pre-onboarding) — documented inline + in wiki Tech-Debt. - C4 (experience): deferred with an inline note (Requisition has no min-experience field yet — Epic A2 AC1). applications.test.ts: reference-gate block/pass and expired-required-doc block/renew-pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>- AC1: addCandidate recognizes a returning hand re-entered as a fresh candidate — matched to their existing EX_HAND pool record by email (preferred) or exact name — and reuses that row instead of creating a duplicate, preserving tour history/documents/bank. Audited CANDIDATE_UPDATED { exHandRecognized: true }. - AC2: the Candidates list sorts ex-hands above new candidates by default (stable, preserving createdAt order within each group). - AC3: the candidate detail "Returning crew" callout now renders the matched member's actual tour history (ExperienceRecord) and documents on file. candidates.test.ts covers email/name recognition, the no-match path, and the ex-hand-first page ordering. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>