Mục tiêu: chứng minh Tenant lifecycle hoạt động thật trong mô hình multi-tenant. Founder điều hành không cần SQL thủ công.
Không Payment · không Notification prod · không Frontend · không refactor Core · không đổi deployment · không đổi mô hình Partner (Beta-4).
tenants.status (migration 20260706000000_add_tenant_status): additive TEXT NOT NULL DEFAULT 'active'. Backward-compatible — mọi tenant/seed/Alpha hiện có → active (không hồi quy). 1 migration duy nhất, đúng phạm vi Tenant lifecycle. RLS áp qua prisma/rls/enable-rls.sql (tách khỏi migration) nên cột mới không đụng RLS.
booking-service.ts): assertTenantActive chạy TRONG tenant context (runInTx + checkAvailability) — nơi RLS cho phép đọc chính row tenant. Chặn booking/availability khi tenant != active ⇒ TENANT_INACTIVE (403). Đặt ở BookingService, KHÔNG ở auth vì tenants là RLS-FORCE (auth chạy trước context). Tương thích ngược: tenant không có row lifecycle (legacy/test) giữ hành vi cũ; chỉ chặn tenant đã provision & không active.
provisioning-service.ts, additive — không sửa Partner Beta-4): provisionTenant(provisioned) · activateTenant · suspendTenant(reversible) · reactivateTenant · archiveTenant(terminal) với guard invariant (TenantTransitionError/TenantNotFoundError); audit 5 event tenant.* (scope tenantId); tenantHealth (read-only).
TENANT_INACTIVE → 403.cli.ts): provision-tenant · activate-tenant · suspend-tenant · reactivate-tenant · archive-tenant(--confirm) · tenant-health · show-audit --tenant. Transition sai → exit 2 (không throw thô, không 5xx).
docs/platform/TENANT-LIFECYCLE.md — trạng thái, transition, invariant, audit, quan hệ Partner↔Tenant↔Inventory↔Booking + ma trận 2 cổng.docs/operations/TENANT-OPERATIONS.md — mọi thao tác + tenant health (không secret).docs/demo/TENANT-PROVISIONING-DEMO.md — 11 bước Founder tự chạy, mỗi bước tiêu chí PASS.docs/platform/TENANT-AUDIT-VERIFICATION.md — cách kiểm chứng audit.platform-ci.yml → test:infra)tenant-provisioning-e2e.test.ts — luồng DoD qua HTTP thật: provision→activate→inventory→key→availability 200→booking 201→suspend→403 TENANT_INACTIVE→reactivate→201→archive→403 (terminal); assert 5 audit event; assert không 5xx; assert suspend Tenant không đụng Partner (độc lập 2 cổng).
ops-tenant.test.ts — vòng đời qua CLI, invariant→exit 2, partner-không-tồn-tại→exit 2, tenant-health, show-audit --tenant.npm run lint ✅ · npm run typecheck:all ✅ · npm run test:domain ✅ · npm run test:core ✅ · npm run platform:metadata:check ✅npm run test:infra (Postgres ephemeral Docker, migrate deploy gồm migration mới): 192/192 PASS, 0 fail.partner-management-e2e, ops-partner), Beta-1/2 booking (booking-e2e, booking-lifecycle-e2e, availability-concurrency), auth, RLS harness.
pg_dump trước khi migrate deploy (theo DB-SAFETY.md). Đề nghị áp ở lần deploy staging kế (an toàn: additive column, non-locking).
lint/typecheck:all/test:domain/test:core/metadata:check/test:infra) + Postgres thật.(gate ở BookingService, không ở resolver) — đã phủ. (Cổng Partner Beta-4 vẫn chỉ ở api-key path — xem Rủi ro.)
tenants.status (append awaited ngay sau update) — như Beta-4.createTenant (tạo active, back-compat seed/Beta-4) và provisionTenant (tạo provisioned, có audit) — không hợp nhất để tránh phá Beta-4/seed.platform-ci.yml chạy thật trên GitHub trong sprint (verify cục bộ bằng đúng các lệnh CI). Chạy khi push/PR main.update và append có thể lệch 1 event. *Khắc phục Beta-6:* gói mỗi thao tác Partner/Tenant + audit trong prisma.$transaction.tenants mỗi write/availability (Thấp): point-lookup theo PK; chấp nhận được. Nếu cần, cache theo request.createTenant active vs provisionTenant provisioned) (Thấp): tài liệu nêu rõ; Beta-6 cân nhắc hợp nhất.prisma.$transaction) — khử rủi ro #1 cho cả hai sprint.pg_dump → migrate deploy → chạy demo Tenant thật (đo end-to-end).status khởi tạo rõ ràng + test hồi quy seed/Alpha.| Tiêu chí DoD | Kết quả |
|---|---|
| Founder tạo Partner | ✅ (Beta-4, demo tái dùng) |
| Founder tạo Tenant | ✅ provision-tenant + activate-tenant |
| Founder provision inventory | ✅ seed-inventory (tenant-health: units > 0) |
| Booking qua Tenant active | ✅ E2E: 201 |
| Tenant suspend chặn request | ✅ E2E: 403 TENANT_INACTIVE |
| Tenant re-activate cho chạy lại | ✅ E2E: 201 (cùng key + inventory) |
| Audit đầy đủ | ✅ 5 event tenant assert (E2E + CLI) |
| E2E PASS · CI PASS | ✅ 192/192 (lệnh CI cục bộ xanh); job chạy khi push main |
| Không thao tác DB trực tiếp | ✅ toàn bộ qua ops CLI |
| Ràng buộc QA | Kết quả |
|---|---|
| Không ảnh hưởng Partner lifecycle (Beta-4) | ✅ additive; partner-management-e2e + ops-partner xanh |
| Không ảnh hưởng Booking (Beta-1/2) | ✅ booking-e2e/booking-lifecycle-e2e/concurrency xanh (fix tương thích: tenant không row ⇒ cho phép) |
| Không đổi deployment | ✅ |
| Không ảnh hưởng Alpha/Legacy | ✅ column default active; tenant chưa provision không bị gate |
| Không secret trong repo | ✅ secret scan sạch |
| Không migration ngoài phạm vi | ✅ 1 migration add_tenant_status, đúng Tenant lifecycle |
| CI xanh | ✅ lệnh CI cục bộ xanh (chờ quan sát job trên PR) |
Trạng thái Sprint: deliverable + DoD đạt trên kiểm chứng cục bộ. Còn lại: (a) áp migration + LIVE verification staging (cần cửa sổ bảo trì), (b) một lần chạy xanh platform-ci.yml trên PR. Chờ Founder review.