一键导入
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 职业分类
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
| 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.