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.
***. Đã ACCEPTED 16/08 (10/10 AC).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.
src/lib/db.ts — mọi route/service chỉ gọi getPrisma(); cấm new PrismaClient() rải rác.g0_baseline chứa toàn bộ model V4; xóa 2 file patch cũ.@hrp/money, @hrp/payroll-core, @hrp/job-board (mới, read-only ISR)./api/tickets/* qua getPrisma().prisma migrate dev chạy trên DB sạch + DB upgrade./job-board public lên Vercel — sếp duyệt link đầu tiên (không cần login).src/lib/db.ts · prisma/schema.prisma · prisma/migrations/g0_baseline/ · packages/{money,payroll-core,job-board}/ · app/job-board/
jose + auth-context.ts decode userId/role.with-auth-scope.ts Prisma Extension deny-by-default.require-permission.ts helper throw 403 với reason.hrp_token; route /api/me trả {userId, role}.hrp-phase1-bcc-fence: rào /bcc bằng JWT tối giản (D15)./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.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/
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).s1_rls_worker, s1_rls_project, s1_rls_vendor — FORCE 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.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.GET /api/workers (list scoped), GET /api/workers/me (chỉ WORKER; role khác 403).CAN_VIEW_WORKER_SENSITIVE sẽ hiển thị thành *** thay vì plaintext.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)
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).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).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ụ.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.PENDING → PAY không qua APPROVED) → trả 409 với reason — debug được, không còn lỗi mơ hồ.prisma/migrations/*_s1_{outbox,idempotency}.sql · src/shared/integrity/{idempotency,audit,outbox,state-machine}.ts · src/domains/attendance/ticket.service.ts · app/api/tickets/*
S01 → S02 → S02A → S02B.S03 → S03B → S03_Resolve.S04 → S04A → S04B.tokens G27 · sheet · data-table · role-guard · audit-trail · state-badge
src/domains/{staffing,attendance,reconciliation,job-board}/ · app/(portal)/* · tests/e2e/{4A,4B,4C,4D}/*
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
app/worker-pwa/ · app/vendor-portal/ · app/ctv-dashboard/ · packages/pwa-shared/
CAN_OVERRIDE_INDIVIDUAL_COMMISSION — audit log mỗi lần override.src/domains/commission/ · prisma/models/commission-ledger.prisma · app/(internal)/commission/
PayRun.LOCK nếu statutory status ≠ VERIFIED.src/domains/payroll/{engine,statutory,calculator,cases}/ · packages/payroll-core (đã có từ P0) · app/(internal)/payroll/runs/ · app/worker-pwa/payslips/