HRP V4 · Invariant Pipeline · D13 chốt 16/08/2026

Lộ trình thực thi & vị trí hiện tại

Mỗi phase đóng băng một bất biến (DB → Đăng nhập → Phân quyền dữ liệu → Toàn vẹn giao dịch) trước khi layer trên dùng — phase sau không phá được phase trước. Mockup Baseline v1 đã được PM/BoD ký và đóng băng.

Phase đã xong
5 / 6 MVP nội bộ
Mockup ✅ · Phase 0 Foundation ✅ · Phase 1 Identity ✅ · Phase 2 Tenant Scope ✅ · Phase 3 Integrity ✅ · Phase 4 Vertical Slices ✅ · Phase 5 UAT ✅
Đang đứng
P1 Portals · ACCEPTED
Phase 5 đã xong. P1 Portals: TASK hrp-p1-portals ACCEPTED (Round 4 PASS, test matrix 595 xanh, AUD-004 đã được khắc phục hoàn toàn). Chờ OP-01, OP-02, OP-04.
UI thật đầu tiên
cuối 4A · W5–W6
Staffing Fill — demo sếp; Job Board public đã chạy từ Phase 0
Tuyến metro — 10 ga · MVP nội bộ 15 tuần → Full V4 24–29 tuần (Q1/2027)
M
Mockup
Baseline v1
đóng băng 16/08
0
Foundation
1 tuần
Xong
1
Identity
2 tuần
Xong
2
Tenant Scope
2 tuần
Xong
3
Integrity
2 tuần
Xong
4
Slices
6 tuần
Xong
5
UAT + Cutover
2 tuần
Xong
P1
Portals
4–6 tuần
ACCEPTED
Đang ở đây
P2
Commission
2–3 tuần
ACCEPTED
P3
Payroll
3–5 tuần
Full V4
Chi tiết từng phase
0
Foundation 1 tuầnXong
Prisma singleton duy nhất + migration gộp + 3 sub-package (money, payroll-core, job-board).
Exit: build + migrate trên DB sạch; Job Board public lên Vercel — sếp duyệt link.
1
Identity 2 tuầnXong
JWT + 13 vai trò + RBAC + AuthContext thật (không nhận từ body tự khai). Đã ACCEPTED: bcc-fence + identity-core.
Exit: không JWT → 401; đủ quyền → 200; thiếu permission → 403.
2
Tenant Scope 2 tuầnXong
RLS Postgres (SET LOCAL, không BYPASSRLS) + withDbContext + masking 7 trường nhạy cảm thành ***. Đã ACCEPTED 16/08 (10/10 AC).
Bàn giao sếp: production RLS chạy runbook trước Phase 4 (DEC-08).
3
Integrity 2 tuầnXong
Idempotency bảng (ADR-014) + Audit chuẩn (actor/reason/ip/ua/before-after) + Outbox (D16: in-process + cron) + State machine → 409. Đã ACCEPTED 16/08 (8/8 AC, vitest 325/325).
Bàn giao sếp: migration production + cron outbox theo runbook HANDOFF §7 trước Phase 4 (DEC-05).
4
Vertical Slices 6 tuầnXong
4 luồng E2E theo click-path mockup — UI thật bắt đầu từ đây: 4A Staffing Fill (S01→S02→S02A→S02B) · 4B Attendance Lock (S03→S03B→S03_Resolve) · 4C Dual Reconciliation (S04→S04A→S04B) · 4D Job Board (S05, chỉ polish).
Exit: 4A ✅ · 4B ✅ · 4C ✅ · 4D ✅ — ACCEPTED 18/08 (437/437 test).
5
UAT + Cutover 2 tuầnXong
2 kỳ shadow chạy song song (sai số ≤ 0.01%) + security matrix 13 role × 8 bảng + load 5.000 check-in / 100 transfer / 20 statement song song.
Exit: ACCEPTED 18/08 — 11/11 AC, 548 tests, RLS 7 bảng trên production, runbook đủ 5 mục. GO-LIVE đợt 1: production đã deploy 18/08, còn seed + UAT theo runbook.
P1
External Portals 4–6 tuầnSau go-live
Worker PWA (chấm công/claim) + Vendor Portal (đối soát) + CTV dashboard — người dùng bên ngoài.
Tiền điều kiện: MVP nội bộ chạy ổn định.
P2
Commission 2–3 tuầnSau go-live
Group policy + individual override + ledger hoa hồng + permission CAN_OVERRIDE_INDIVIDUAL_COMMISSION.
Tiền điều kiện: P1 xong.
P3
Payroll Engine 3–5 tuầnSau go-live
TNCN/BHXH tính thật + payslip — thay placeholder Phase 4; golden case 5 loại NPT; lock guard VERIFIED.
Exit: parallel-run 1 kỳ sai số ≤ 0.01% → FULL V4 hoàn tất (Q1/2027).

Mốc UI — khi nào thấy gì

16/08/2026
Mockup Baseline v1 đóng băng
48 file · ~22 màn nghiệp vụ — PM/BoD đã ký (D14). UI tĩnh xem trước luồng.
Từ Phase 0
Job Board public chạy thật
Read-only, không cần login — CTV xem job công khai ngay từ bây giờ.
W5–W6 · cuối 4A
UI thật đầu tiên: Staffing Fill
S01→S02→S02A→S02B có dữ liệu thật — khoảnh khắc demo BoD "Guided Transfer 02:10–03:10". Demo sếp, sếp accept.
4B → 4C
2 khoảnh khắc còn lại
Exception→Lock (06:20–08:30) · Dual Reconciliation (09:30–13:00) — mỗi slice demo sếp.
Cuối Phase 4
Demo tổng 4 slice
1 buổi 30–45 phút; UI dùng chung shared component (tokens G27, sheet, data-table, role-guard).
Phase 5
UAT người dùng thật
HR + PM + Accountant dùng dữ liệu shadow 2 kỳ — trải nghiệm nội bộ đầy đủ 4 luồng.
P1 → P3 · Q1/2027
Giao diện đầy đủ 100%
Worker PWA + Vendor Portal + CTV dashboard (P1) · Commission (P2) · Payslip + payroll thật (P3) — toàn bộ tính năng đã thiết kế.
Mở rộng · Việc cần làm, thay đổi UX, artifacts theo từng phase

Mỗi phase liệt kê 3 nhóm: việc kỹ thuật (deliverable + file), thay đổi UX (user nhìn thấy gì — nếu có), và artifact files để sếp truy nguyên. Click ▸ để mở rộng. Cập nhật sau mỗi task ACCEPTED.

0 Foundation Done W1 · 1 tuần

Việc kỹ thuật

  • Prisma singleton duy nhất tại src/lib/db.ts — mọi route/service chỉ gọi getPrisma(); cấm new PrismaClient() rải rác.
  • Gộp migration g0_baseline chứa toàn bộ model V4; xóa 2 file patch cũ.
  • Tách 3 sub-package rủi ro thấp: @hrp/money, @hrp/payroll-core, @hrp/job-board (mới, read-only ISR).
  • Refactor 6 route /api/tickets/* qua getPrisma().
  • Build & test pass; prisma migrate dev chạy trên DB sạch + DB upgrade.

Thay đổi UX

  • Chưa có UI thật — phase này chỉ chuẩn bị nền tảng.
  • Trang /job-board public lên Vercel — sếp duyệt link đầu tiên (không cần login).
Artifacts: src/lib/db.ts · prisma/schema.prisma · prisma/migrations/g0_baseline/ · packages/{money,payroll-core,job-board}/ · app/job-board/
1 Identity Done W2 · 2 tuần

Việc kỹ thuật

  • JWT verify bằng jose + auth-context.ts decode userId/role.
  • Permission catalog 7+ code theo HOLISTIC_REVIEW §9.1; permission-resolver 13 role × 5 bảng = 65 case PASS.
  • with-auth-scope.ts Prisma Extension deny-by-default.
  • require-permission.ts helper throw 403 với reason.
  • Seed permission idempotent — re-run không mất grant manual.
  • Middleware Next.js bắt cookie hrp_token; route /api/me trả {userId, role}.
  • Task con hrp-phase1-bcc-fence: rào /bcc bằng JWT tối giản (D15).

Thay đổi UX

  • Chưa có UI thật mới — phase này chỉ API backend.
  • Trang /bcc (BCC Sync) yêu cầu đăng nhập thật thay vì stub — sếp/test thấy 401 khi không JWT, 200 khi có JWT.
  • Behavior đã chốt: không tạo lại bộ login/JWT/cookie trong task sau — Phase 1 là nguồn duy nhất (DEC-11).
Artifacts: src/shared/auth/{jwt,auth-context,require-permission,with-auth-scope,permission-catalog,permission-resolver}.ts · app/api/auth/* · app/api/me/route.ts · middleware.ts · app/bcc/
2 Tenant Scope Done Xong 16/08 · W3

Việc kỹ thuật

  • Tách credential web/ETL (DEC-09 A): web runtime dùng role app_user_writer, migrate qua directUrl = DATABASE_URL_ADMIN; appBCC 1 dòng env → APPBCC_DATABASE_URL (role hrp_etl, grants tối thiểu).
  • 3 migration RLS dev: s1_rls_worker, s1_rls_project, s1_rls_vendorFORCE ROW LEVEL SECURITY, không BYPASSRLS.
  • with-db-context.ts + rls-context.ts set 4 GUC (app.user_id, app.role, app.vendor_id, app.worker_id) bằng set_config(..., true) trong transaction — cấm SET ROLE.
  • 4 scope builder: scopes/{worker,project,vendor,ctv}.scope.ts — model thiếu vẫn deny-by-default.
  • worker-projection.ts mask 7 trường: cccdNumber, cccdImageUrl, selfieImageUrl, cccdChipData, bankAccount, bankName, bankBranch thành *** khi thiếu CAN_VIEW_WORKER_SENSITIVE.
  • 2 route demo: GET /api/workers (list scoped), GET /api/workers/me (chỉ WORKER; role khác 403).
  • 13 role × 4 bảng matrix = 52 case PASS + L2 two-transaction integration + checklist §5.7.
  • Runbook production RLS (defer tới trước Phase 4 theo DEC-08) — preflight, apply order, rollback <5 phút, dry-run trên dev.

Thay đổi UX

  • Chưa có UI thật mới — phase này chỉ API backend.
  • Người dùng nội bộ (HR/PM/CTV/VENDOR/...) thấy danh sách worker/admin giờ tự giới hạn row theo scope — không còn thấy row ngoài phạm vi quyền.
  • 7 trường nhạy cảm (CCCD, bank, selfie) ở role không có CAN_VIEW_WORKER_SENSITIVE sẽ hiển thị thành *** thay vì plaintext.
  • ADMIN vẫn thấy tất cả + plaintext — phục vụ kiểm toán.
Artifacts: prisma/migrations/*_s1_rls_{worker,project,vendor}/ · src/shared/auth/with-db-context.ts · src/shared/auth/rls-context.ts · src/shared/auth/scopes/*.scope.ts · src/shared/auth/worker-projection.ts · app/api/workers/{route,me/route}.ts · appBCC/app.py (1 dòng env)
3 Integrity Done Xong 16/08 · W4

Việc kỹ thuật

  • Migration dev: bảng idempotency_keys (UNIQUE (actorId, route, key) + request hash + response JSON + expiresAt 24h), bảng outbox (eventType, payload, status PENDING/PROCESSED/FAILED, retryCount, availableAt), 3 cột nullable AuditLog (reason, ip_address, user_agent).
  • 4 helper integrity: idempotency.ts (check-or-create + replay theo ADR-014), audit.ts (writer chuẩn 5 thành phần), outbox.ts (enqueue + drain in-process sau commit + cron daily retry PENDING — D16 (b)), state-machine.ts (generic + IllegalTransitionError → 409).
  • Refactor ticket.service.ts: bỏ idempotency query metadata, nối 4 helper, bộ TRANSITIONS + role guard + ticketTypes + optimistic lock + SLA giữ nguyên — không đổi nghiệp vụ.
  • Test: 2 lần cùng Idempotency-Key → 1 transition + 1 audit + lần 2 nhận kết quả lần 1; PENDING → PAY sai luồng → 409 reason; outbox rollback không còn event; drain idempotent.
  • Runbook production (defer): migration + bật cron outbox, preflight, rollback <5 phút, verify.

Thay đổi UX

  • Chưa có UI thật mới — chỉ API backend.
  • User thấy: retry nút approve/reject/pay (mạng chập chờn) → chỉ 1 transition xảy ra, lần 2 nhận kết quả lần 1 — không còn double-state.
  • Transition sai (vd PENDING → PAY không qua APPROVED) → trả 409 với reason — debug được, không còn lỗi mơ hồ.
  • Notification ghi outbox cùng transaction — process chết giữa chừng không làm mất thông báo; cron daily lưới an toàn đẩy lại.
Artifacts: prisma/migrations/*_s1_{outbox,idempotency}.sql · src/shared/integrity/{idempotency,audit,outbox,state-machine}.ts · src/domains/attendance/ticket.service.ts · app/api/tickets/*
4 Vertical Slices (4A · 4B · 4C · 4D) Xong 6 tuần · ACCEPTED 18/08 — 437/437 test · Phase 5 UAT/Cutover ✅ ACCEPTED

4A · Staffing Fill 1.5 tuần · UI thật đầu tiên · code ✅ 17/08

  • Tech: schema migration (nếu cần) + service/repository + route + UI mount + 4-role integration test (ADMIN/HR_STAFF/VENDOR/WORKER) + E2E theo click-path S01 → S02 → S02A → S02B.
  • UX: CTV/WORKER/VENDOR nộp hồ sơ → HR review → PM phê duyệt → gửi Client. Khoảnh khắc demo BoD Guided Transfer 02:10–03:10. 4 vai trò thấy 4 view khác nhau (RBAC + RLS + projection).

4B · Attendance Lock 1.5 tuần · code ✅ 18/08 (core + resolve + RLS + drawer) — ACCEPTED

  • Tech: service + repository + route + UI + 4-role test + E2E theo S03 → S03B → S03_Resolve.
  • UX: HR nhập giờ → exception hiện → sửa → lock. Khoảnh khắc demo Exception→Lock 06:20–08:30. Sau lock → không thể sửa, phải mở khóa có audit.

4C · Dual Reconciliation 1.5 tuần · code ✅ 18/08 — ACCEPTED

  • Tech: service + repository + route + UI + 4-role test + E2E theo S04 → S04A → S04B.
  • UX: Vendor xác nhận statement → Client xác nhận → đối chiếu 2 bên → Accountant khóa. Khoảnh khắc demo Dual Reconciliation 09:30–13:00. Sai lệch → highlight dòng chênh + audit log.

4D · Job Board public 1.0 tuần · code ✅ 18/08 — ACCEPTED

  • Tech: polish + analytics tracking; bản chất đã chạy từ Phase 0.
  • UX: vẫn public (không cần login) — CTV và bên ngoài xem job công khai; thêm analytics thống kê view/apply.
Shared UI: tokens G27 · sheet · data-table · role-guard · audit-trail · state-badge
Artifacts: src/domains/{staffing,attendance,reconciliation,job-board}/ · app/(portal)/* · tests/e2e/{4A,4B,4C,4D}/*
5 UAT + Cutover Chờ W11–W12 · 2 tuần

Việc kỹ thuật

  • 2 kỳ shadow reconciliation (HRP vs Excel cũ) chạy song song — sai số ≤ 0.01%.
  • Security matrix 13 role × 8 bảng = 104 case PASS.
  • Load burst 5.000 check-in / 100 transfer / 20 statement song song (V4 §17 R-39).
  • Runbook deploy + rollback + incident có chữ ký sếp.
  • Cutover dry-run 1 lần trên staging Neon riêng.
  • Go-live đợt 1: 1 dự án + 1 vendor + 1 client (giới hạn scope quan sát).

Thay đổi UX

  • HR + PM + Accountant dùng data thật (tháng 06 + 07/2026) trên hệ thống — nội bộ chuyển từ Excel sang HRP.
  • Email domain nội bộ dùng cho go-live; dự án/vendor/client đầu tiên onboard.
  • User thấy: hệ thống chạy ổn định, có runbook khi sự cố, có người chịu trách nhiệm vận hành.
Artifacts: docs/runbook/{deploy,rollback,incident}.md · tests/load/{5000-checkin,100-transfer,20-statement}/ · tests/security/matrix-13-role-8-table.spec.ts · docs/uat/signoff.md
P1 External Portals ACCEPTED 4–6 tuần

Việc kỹ thuật

  • Worker PWA: GPS evidence + offline storage IndexedDB + push notification.
  • Vendor Portal: confirm/dispute statement + audit + SLA tracking.
  • CTV Dashboard: source claim overview + commission cumulative.
  • Điều kiện: Phase 4 đã có source of truth (Worker CRUD, Vendor Statement, CTV SourceClaim).
  • Không: native app, eKYC (deferred theo V4 §5).

Thay đổi UX

  • 3 cổng bên ngoài ra đời — đây là lúc người dùng ngoài công ty (worker/vendor/CTV) bắt đầu dùng hệ thống thật.
  • Worker chấm công bằng điện thoại (có GPS); Vendor xác nhận công nợ online; CTV xem hoa hồng cộng dồn.
  • Audit + SLA đảm bảo vendor phản hồi trong X ngày, ngược lại hệ thống tự nhắc.
Artifacts: app/worker-pwa/ · app/vendor-portal/ · app/ctv-dashboard/ · packages/pwa-shared/
P2 Commission ACCEPTED 2–3 tuần

Việc kỹ thuật

  • CommissionGroup policy (placeholder đã có từ Phase 0) + individual override + ledger reversal chuẩn.
  • Permission CAN_OVERRIDE_INDIVIDUAL_COMMISSION — audit log mỗi lần override.
  • Tính hoa hồng CTV + Worker + Referrer trên cùng ledger.
  • Điều kiện: P1 đã có SourceClaim + ProjectAssignment ổn định.

Thay đổi UX

  • HR/Accountant nội bộ: dashboard hoa hồng theo tháng/quý, filter theo CTV/Worker/Referrer.
  • Override flow: chọn chính sách → nhập lý do → audit log ghi lại.
  • Worker/CTV thấy số hoa hồng cộng dồn realtime; ledger truy vết được từng khoản.
Artifacts: src/domains/commission/ · prisma/models/commission-ledger.prisma · app/(internal)/commission/
P3 Payroll Engine Sau go-live 3–5 tuần

Việc kỹ thuật

  • Thay MOCKED ở Phase 4 bằng engine tính TNCN/BHXH thật theo luật VN.
  • Payslip PDF/HTML cho worker.
  • Golden case 5 loại NPT: người Việt, nước ngoài, có người phụ thuộc, có tạm ứng, có điều chỉnh.
  • Parallel run 1 kỳ với engine cũ (Excel/Python) — sai số ≤ 0.01%.
  • Lock guard: không cho PayRun.LOCK nếu statutory status ≠ VERIFIED.
  • Điều kiện: Phase 4 đã có PayRun + WorkerPayResult + StatutoryCalculation (placeholder).

Thay đổi UX

  • Worker thấy payslip thật (lương gross, BHXH, TNCN, net) — tải PDF/HTML.
  • HR chạy 1 kỳ payroll cuối tháng thật trên hệ thống, không cần Excel song song.
  • Accountant kiểm tra statutory VERIFIED trước khi khóa kỳ; nếu chưa → hệ thống chặn + gợi ý bước thiếu.
  • Full V4 hoàn tất: tất cả tính năng đã thiết kế đều chạy thật.
Artifacts: src/domains/payroll/{engine,statutory,calculator,cases}/ · packages/payroll-core (đã có từ P0) · app/(internal)/payroll/runs/ · app/worker-pwa/payslips/
P0
P1
P2
P3
P4 — 4 slice
P5
Đã xong (P0–P5) Đang: Chờ UAT/Ops P1 & P2 (Cả hai đã ACCEPTED) Chưa tới (P3 Payroll) + P1–P3 (9–14 tuần sau go-live) → Full V4