بنقرة واحدة
awcms-mini-tenant-entitlement
Kerjakan bagian mana pun dari modul tenant_entitlement AWCMS-Mini (Issue
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Kerjakan bagian mana pun dari modul tenant_entitlement AWCMS-Mini (Issue
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
| name | awcms-mini-tenant-entitlement |
| description | Kerjakan bagian mana pun dari modul tenant_entitlement AWCMS-Mini (Issue |
tenant_entitlement (src/modules/tenant-entitlement, Issue #871, epic #868
SaaS control plane Wave 1, ADR-0022) adalah modul control-plane KEDUA dan
JANTUNG epic — Official Optional Business Foundation, opt-in per tenant,
default-disabled, dan modul control-plane TENANT-SCOPED pertama. Ia
menurunkan entitlement efektif fitur/modul/kuota tenant dari offer
service_catalog published + override operator, lalu mengekspos SATU kontrak
penegakan fail-closed (effective_entitlement) yang dikonsumsi #872/#873/#875/
#876 dan modul bisnis. Baca src/modules/tenant-entitlement/README.md +
docs/adr/0022-*.md sebelum mengubah.
domain/resolution.ts
isFeatureAllowed/isModuleEntitled/getQuota + port adapter), BUKAN per-
route ([[ssr-admin-pages-skip-module-enabled]]). Port adapter juga cek
resolveModuleEnabled (disabled → deny-all) + catch error → deny.awcms_mini_tenant_modules)
tapi terkoordinasi. #871 hanya RESOLVE; tidak meng-toggle tenant_modules
(itu tugas provisioning #872). Entitlement loss = ubah state + gate,
TIDAK PERNAH DELETE data tenant.tenant_id + ENABLE+FORCE RLS + policy predikat SELALU DAN HANYA
tenant_id = current_setting('app.current_tenant_id')::uuid. JANGAN tambah
OR platform-claim. Operator kelola entitlement tenant HANYA lewat konteks
per-tenant (withTenant), tiap mutasi audited. Route TIDAK punya path param
{tenantId} — beroperasi pada tenant konteks saat ini.sql/081) + REVOKE grant, bukan hanya app-layer.EffectiveEntitlementSnapshot (§pattern
#5, lihat Fix 4). Hash atas HANYA field yang port ekspos: feature/module
key+allowed; quota key+allowed+isUnlimited+limit+unit. JANGAN sertakan
source.kind (di-strip port = oracle) NI omit unit (tenant-visible). TANPA
timestamp (dua resolusi identik → hash sama → invalidasi deterministik). GATE
test bandingkan field hash vs field port snapshot.resolveTenantEntitlement (application) memuat bulk (2 record read + 1
published-offer read per offer distinct — via port service_catalog_read) lalu
memanggil resolveEffectiveEntitlement (PURE). Query count konstan terhadap
jumlah key (no per-request N+1 catalog query — [[n-plus-1-batch-835-premises]];
ada perf/query-count test). Precedence per key: override aktif MENGGANTI
keputusan offer (DB jamin ≤1 override aktif per key → grant/deny tak ambigu);
tanpa override → grant dari assignment aktif; tanpa keduanya → absent → helper
lookup DENY. Suspended/expired assignment = tak berkontribusi (restriction).
Module dependency SAFE-DOWNGRADE: modul entitled yang dependency-nya (yang juga
keputusan entitlement) tak entitled → di-downgrade (fixpoint monotonic).
Tiap write path (application/entitlement-directory.ts): row-lock FOR UPDATE
ON CONFLICT pada
partial unique index (assignment current, override active) → duplikat = 409
bersih (bukan raw 23505); idempotency-replay (replayConcurrentIdempotentWinner)
menang atas business-conflict same-key. assign/transition/override/revoke wajib
Idempotency-Key (hash ber-resource-id) + audit (action diskriminatif) + emit
event same-commit + tulis snapshot same-commit.application/request-parsing.ts: absent scalar/enum/bool → default; present →
verbatim (validator domain tolak 400); nullable → tri-state (absent→null,
present-wrong-type→400 bukan coerce ke null). Override targetKey WAJIB dikenal
(fail-closed unknown, domain/entitlement-key-registry.ts). Sejak #874 file itu
me-RE-EXPORT SINGLE SOURCE src/modules/_shared/saas-contract-registry.ts
(union rich ModuleDescriptor.serviceCatalog.{features,meters} + listModules()
keys) — TIDAK lagi replikasi agregasi service_catalog; boundary tetap terjaga
(seam _shared/, bukan import lintas modul), drift-guard test tetap hijau. Helper
entitlement (isEntitlementGatedModule/resolveGatedModuleKeys/overrideResolutionCap,
mapping override kind→namespace) tetap di sini. Lihat skill awcms-mini-saas-contracts.
Emit awcms-mini.tenant-entitlement.assignment.changed /
.override.changed (v1.0) via appendDomainEvent (terdaftar di
event-type-registry.ts + AsyncAPI + module.ts events.publishes, parity
di-gate). PROVIDES port effective_entitlement
(_shared/ports/effective-entitlement-port.ts, read-only). CONSUMES
service_catalog_read — wire adapter di composition-root route/page, JANGAN
import service-catalog app/domain (tests/unit/module-boundary.test.ts menegakkan).
Reviewer+security-auditor+audit adversarial 5-lensa = MERGE-READY/0-Crit/0-High, tapi 7 fix ditutup dalam SATU push (front-load hindari 7 ronde #870):
if (depDecision && !depDecision.allowed) perlakukan dep absent = satisfied →
OVER-GRANT (modul granted walau dep gated tak di-subscribe). FIX: thread SET
gatedModuleKeys (resolveGatedModuleKeys: type domain/integration/derived
ATAU defaultTenantState disabled) ke ResolutionInput. depSatisfied = !gated.has(dep) || modules[dep]?.allowed===true. Base (tenant_admin/
identity_access/logging = type undefined) absent = satisfied; gated absent =
DENY. Bedakan "base selalu-on" dari "gated tak dibeli".?at=<masa lalu> TOLAK. Resolusi baca record set SEKARANG (current/
non-revoked absolut) → at lampau rekonstruksi entitlement yang TAK PERNAH
berlaku (override di-revoke pagi ini hilang → key flip allowed). History
sejati = evaluation_snapshots. FIX: reject at < now - 60s 400 + OpenAPI
desc jujur. (Future OK — well-defined atas current records.)EffectiveEntitlementSnapshot. Hash
DULU encode source.kind (di-STRIP port toSnapshot = ORACLE #870) + OMIT
quota unit (tenant-visible → cache-invalidation miss). FIX: feature/module
key+allowed; quota key+allowed+isUnlimited+limit+unit. DROP sourceKind,
ADD unit. GATE test: field hash projection == field port snapshot (mirror
information_schema gate #870). Test: redundant grant-override tak ubah hash
(boolean sama); unit berubah → hash berubah.LIMIT truncation = fail-OPEN (drop DENY → key jatuh ke offer
grant; asimetris vs assignment cap yang drop GRANT = fail-safe). FIX: override
aktif hard-bounded registry cardinality (partial unique per kind+key) →
overrideResolutionCap = |moduleKeys|+|featureKeys|+|meterKeys|; LIMIT cap+1;
rows.length > cap → throw EntitlementIndeterminateError (port catch →
deny-all). Jangan diam percaya partial set.resolveEntitlementKeyRegistry(listModules()) == resolveServiceCatalogKey Registry(listModules()) (3 set). Tangkap divergensi pra-#874.Full check FRESH DB (awcms-mini-verify871b): 5151 pass/0 fail/build Complete
setelah 7 fix. Pelajaran meta #872+: audit front-load (5-lensa + reviewer +
security-auditor) SEBELUM Codex + tutup semua dalam satu push.
application/control-plane-signals.ts — collectEntitlementSignals(tx, now):
assignment status='active' yang effective_to sudah lewat (belum tersapu).
Ini masalah KOMERSIAL dan OTORISASI sekaligus — tenant masih memegang akses
yang seharusnya dicabut — karena itu objective-nya ketat (satu interval sweep),
bukan sekadar eventual.
effective_to IS NULL = open-ended, TIDAK PERNAH kedaluwarsa. Cek NULL
harus eksplisit: NULL < now() bernilai NULL (bukan false), jadi predikat
yang dinegasikan akan menjatuhkan baris open-ended ke bukan-cabang-mana-pun.(tenant_id, plan_key) WHERE superseded_at IS NULL AND canceled_at IS NULL — satu entitlement hidup per PLAN, bukan satu per
tenant. Fixture test butuh plan_key berbeda per baris (dan plan_key
punya CHECK format snake_case — tanda hubung ditolak).103).bun run check PENUH dengan PostgreSQL nyata (DB terisolasi FRESH, lihat memory
scratch-db-verify-when-shared-db-poisoned + local-postgres-connection-details).
*.integration.test.ts tak boleh skip. Test wajib: precedence/effective-date/
override/suspension/unknown-key/explanation (unit); RLS + constraint +
concurrency + revocation + event same-commit + cross-tenant + authorization-
negative + contract + perf/query-count (integration); mutation test (unknown-key
→ allow HARUS bikin test gagal). Regen: openapi:bundle, api:docs:generate,
i18n:extract, repo:inventory, modules:composition:inventory. Doc 01/13/21 +
module-doc-reconciliation + module-skill-coverage di-gate saat menambah modul.
Kelola/konsumsi sistem Module Management AWCMS-Mini (registry, validasi komposisi registry base, tenant lifecycle enable/disable, settings, permission sync/status, navigation, job registry, health/readiness). Gunakan saat menambah field descriptor baru (permissions/navigation/settings/jobs/health) di modul lain, saat menambah modul domain baru langsung di `src/modules/` dan perlu memverifikasi registry base tetap valid (`bun run modules:compose:check`), saat menyelidiki kenapa suatu modul terlihat degraded/orphaned, atau saat mengubah perilaku enable/disable/settings/health module_management sendiri. Sesuai src/modules/module-management/README.md, epic
Pilih dan jalankan profil deployment AWCMS-Mini (development/staging/production/offline-LAN). Gunakan saat menyiapkan deployment baru, memutuskan LAN-first vs registry-based, atau deploy ke Coolify. Sesuai doc 18 dan deployment-profiles.md/deploy-coolify.md.
Kelola sistem log/audit/metrics AWCMS-Mini yang sudah aktif — correlation ID lintas hop, retensi/purge audit log, extension point untuk consumer eksternal (alerting/export/SIEM), dan metrics port (counter/histogram/gauge berkardinalitas rendah untuk request/pool/job/provider). Gunakan saat menambah endpoint baru (correlation ID otomatis), menjadwalkan purge audit log, memasang consumer log/audit di aplikasi turunan, atau menambah/mengonsumsi metrik operasional. Berbeda dari awcms-mini-audit-log (APA yang wajib diaudit) — skill ini tentang BAGAIMANA sistem log/audit/metrics itu sendiri dikelola, sesuai Issue
Kerjakan bagian mana pun dari modul tenant_provisioning AWCMS-Mini (Issue
Triase dan perbaiki temuan CodeQL code scanning AWCMS-Mini (github.com/ahliweb/awcms-mini/security/code-scanning). Gunakan saat diminta "analisis code scanning"/"perbaiki CodeQL", saat sebuah PR gagal check CodeQL, atau saat menemukan alert baru. Mendokumentasikan satu TRUE-positive terkonfirmasi (password sungguhan → digest cepat yang dipersist = oracle cracking offline, plus dua "fix" yang membungkam alert tanpa memperbaiki apa pun) dan lima false-positive nyata (name-heuristic password, incompatible-types typeof/null, URL substring-sanitization di test mock, dua kasus dismiss resmi tanpa reformulasi kode, dan Bun.SQL tagged-template null-cast) plus pola "unused-local-variable di test kadang menandai coverage gap" — supaya tidak diinvestigasi ulang dari nol.
Daftarkan tabel bervolume tinggi ke registry lifecycle AWCMS-Mini (retensi/partisi/arsip/legal hold/purge), atau kerjakan bagian mana pun modul data_lifecycle sendiri. Gunakan saat menambah tabel baru yang tumbuh besar (log, telemetry, outbox, queue, provider attempt) dan butuh kebijakan retensi/purge aman, saat membuat/melepas legal hold, atau saat mengubah engine dry-run/archive/purge/archive-port. Sesuai Issue