원클릭으로
awcms-mini-data-exchange
Kerjakan bagian mana pun dari modul data_exchange AWCMS-Mini (Issue
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Kerjakan bagian mana pun dari modul data_exchange 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-data-exchange |
| description | Kerjakan bagian mana pun dari modul data_exchange AWCMS-Mini (Issue |
data_exchange (src/modules/data-exchange, Issue #752, epic
platform-evolution #738 Wave 3, admission decision
docs/adr/0018-data-exchange-module-admission.md) adalah Official
Optional Module (type: "domain", ADR-0013 §3 "Official Optional
Business Foundation") — framework import/export CSV/JSON staged yang
provider-neutral, tenant-scoped, opt-in per tenant.
Baca src/modules/data-exchange/README.md untuk peta lengkap tiap
tabel/endpoint. Skill ini merangkum yang tidak jelas dari membaca satu
file: kontrak descriptor, invariant fail-closed pada jalur preview
(tiga cacat berkomposisi yang diperbaiki Issue #820), dan batas parsing.
Melengkapi (bukan menggantikan) awcms-mini-new-endpoint,
awcms-mini-new-migration, awcms-mini-idempotency,
awcms-mini-abac-guard, awcms-mini-sensitive-data. Pakai skill ini
untuk konteks domain data_exchange spesifik: cara modul lain
menyambungkan adapter import/export-nya, aturan sensitiveFields, dan
batas parse yang wajib dipertahankan.
DataExchangeAdapterPort/DataExchangeExportSourcePort
(src/modules/_shared/ports/data-exchange-adapter-port.ts) plus
descriptor pure-data (ExchangeDescriptor, field dataExchange di
_shared/module-contract.ts).commitRow milik adapter modul pemilik.Modul ini hanya mengapalkan SATU pasangan descriptor/adapter referensi
self-contained — reference_items
(application/reference-items-exchange-adapter.ts, tabel
awcms_mini_data_exchange_reference_items) — sebagai bukti mekanisme
end-to-end, mengikuti preseden "foundation issue ships zero real business
integrations" (domain_event_runtime, #742). Jangan perlakukan
reference_items sebagai domain bisnis nyata atau contoh skema yang layak
ditiru isinya — yang ditiru adalah BENTUK-nya.
POST /imports (multipart) -> staged (checksum, size bound, Idempotency-Key)
worker: bun run data-exchange:worker
runImportValidatePass -> validating -> previewed
GET /imports/{id}/preview (zero mutation, masked)
POST /imports/{id}/commit -> committing (TRIGGER saja, Idempotency-Key)
worker
runImportCommitPass -> committed | partially_committed
Commit tidak pernah dikerjakan di dalam request HTTP: route hanya
memindahkan status ke committing, worker yang mengeksekusi dalam pass
berbatas dan resumable (commit_cursor). Jangan "sederhanakan" ini
menjadi commit sinkron — itu mengembalikan transaksi tak berbatas dan
request panjang yang desain ini hindari.
data-exchange-worker.ts membuka withTenant terpisah untuk setiap
batch validate pass, setiap batch commit pass, dan setiap export job —
exception pada SATU item tidak pernah me-rollback item lain di pass yang
sama (diuji dengan throw sengaja di tengah pass).
Export cermin dari ini: POST /exports (queue) -> worker runExportJob
(baca paginated lewat DataExchangeExportSourcePort) -> completed ->
GET /exports/{id}/download.
Tiga cacat pada jalur GET /imports/{id}/preview yang berkomposisi
jadi kebocoran identifier mentah (NIK/NPWP/email). Semua sudah
diperbaiki; jangan diregresi:
sensitiveFields
sekarang WAJIB — gate registry (domain/exchange-registry.ts)
menolak descriptor tanpanya. Nyatakan "tidak ada yang sensitif" secara
afirmatif dengan { fieldNames: [] }, jangan dengan menghilangkan
field-nya. Sebelum #820, sensitiveFields opsional dan absennya
berarti seluruh staged row dikembalikan MENTAH tanpa cek permission
sama sekali — lupa mendeklarasikan = membuka, kebalikan doc 17.rawValuePermission benar-benar di-enforce. Field di
sensitiveFields.fieldNames hanya unmask untuk caller yang memegang
permission yang descriptor itu sendiri sebut di
sensitiveFields.rawValuePermission — permission sempit miliknya
sendiri (mis. profile_identity.identifiers.reveal_raw), tidak
pernah data_exchange.* yang generik. Sebelum #820 field ini
divalidasi di registry tapi punya nol enforcement site; route
memakai konstanta hardcoded data_exchange.preview_errors.read yang
jauh lebih luas. Ini pengulangan pola "validator ada tapi tak
tersambung" (lih. #769/#740, waktu itu Critical) — kalau menambah
field kontrak baru yang divalidasi di registry, telusuri mundur dari
setiap call site nyata, jangan berhenti di test registry-nya.resolveImportDescriptor
mengembalikan null bila modul pemiliknya di-disable/dihapus lewat
module_management SETELAH batch di-stage.
application/descriptor-authorization.ts's
authorizeExchangeDescriptorPermission tidak lagi menerima null —
preview/commit/retry/download menjawab 409 INVALID_STATE. Sebuah batch
tidak boleh jadi LEBIH terbuka setelah modulnya dimatikan (efek absurd
yang dulu terjadi: descriptor null → guard lolos → fieldNames kosong →
semua mentah).naturalKey ikut di-masking bila sensitiveFields.naturalKeyField
menunjuk field yang sendiri sensitif. Kunci dedup import profil lazimnya
justru email/NIK — mask salinan di fields tapi echo nilai sama sebagai
naturalKey = tidak mask apa pun.Verifikasi: tests/unit/data-exchange-staged-row-masking.test.ts,
tests/unit/data-exchange-registry.test.ts,
tests/unit/data-exchange-descriptor-permission-ssr.test.ts,
tests/integration/data-exchange.integration.test.ts.
// <modul-pemilik>/module.ts
dataExchange: [
{
key: "profile_identity.parties",
ownerModuleKey: "profile_identity",
direction: "both",
formats: ["csv", "json"],
schemaVersion: "1.0",
limits: {
maxFileBytes: 5 * 1024 * 1024,
maxRowCount: 5000,
maxFieldsPerRow: 10
},
adapterRegistryKey: "profile_parties",
requiredPermission: "profile_identity.parties.create",
sensitiveFields: {
fieldNames: ["email", "nik"],
naturalKeyField: "email",
rawValuePermission: "profile_identity.identifiers.reveal_raw"
},
description: "..."
}
];
Lalu daftarkan adapter-nya di
infrastructure/exchange-adapter-registry.ts (registry statik source-code
yang direview, bentuk sama dengan
domain-event-runtime/infrastructure/consumer-registry.ts) dengan kunci
yang sama persis dengan adapterRegistryKey. Parity descriptor↔kontrak
dijaga oleh gate response-vs-schema umum
tests/unit/response-contract-validation.test.ts (Issue #844) yang
memvalidasi response nyata GET /api/v1/data-exchange/descriptors terhadap
schema OpenAPI yang dipublikasikan.
requiredPermission (permission tambahan modul pemilik) dicek di SETIAP
route yang me-resolve descriptor: stage, preview, commit, retry,
export-create, dan export-download. Yang terakhir sempat terlewat dan
ditemukan security-auditor di PR #782 — route yang menyajikan KONTEN FILE
mentah lebih sensitif daripada metadata job yang sudah dicover
exports.read. Setiap call site sekarang dibuktikan test, bukan diklaim
di prosa.
readFormBody("large")) diberlakukan SEBELUM parsing
apa pun.domain/csv-codec.ts) adalah state machine tulisan tangan yang
ABORT DI TENGAH PARSE begitu maxRowCount/maxFieldsPerRow terlampaui.
Jangan ganti dengan regex/split — bandingkan pelajaran tokenizer di
awcms-mini-new-migration's scanner: alternation regex tidak bisa
mengekspresikan state.JSON.parse, dicek segera
sesudahnya (tidak bisa abort mid-parse seperti CSV — lihat header
domain/json-codec.ts).domain/media-type-allowlist.ts), 415 bila tidak diizinkan.
Catatan penting di header file itu: File.type yang di-resolve
request.formData() Bun terbukti diturunkan dari ekstensi nama file,
bukan pass-through header Content-Type per part.limit
dibatasi PREVIEW_PAGE_SIZE_MAX, offset dibatasi PREVIEW_OFFSET_MAX
(= MAX_EXCHANGE_ROW_COUNT, sehingga tidak bisa menyembunyikan baris
yang masih terjangkau). Satu CSV besar mengisi staged_rows sampai
volume deep-offset dalam sekali jalan.String yang diawali =/+/-/@/TAB/CR dinetralkan (prefix ') di
DUA titik independen: intake (domain/formula-injection-guard.ts,
sebelum baris pernah dipersist) DAN serialisasi export
(export-execute-job.ts's neutralizeRowForExport, defense in depth,
tidak bergantung riwayat import). Termasuk field array/objek bersarang —
yang dicek adalah bentuk String()-nya (mis. String(["=1+1"]) ===
"=1+1"), bukan hanya scalar. Jangan hapus salah satu titik dengan alasan
redundan: export bisa memuat baris yang masuk sebelum guard intake ada.
Idempotency-Key wajib di setiap endpoint mutasi: stage-upload,
commit, cancel, retry, pause, resume (imports); create, cancel (exports).
Commit per-baris idempotent: runImportCommitPass hanya pernah memilih
staged row commit_status = 'pending', jadi restart worker di tengah
commit tidak bisa menerapkan ulang baris yang sudah 'committed'; adapter
referensi juga idempotent per naturalKey sebagai defense-in-depth.
data_exchange.export_downloads.read adalah permission terpisah dan
lebih sensitif dari data_exchange.exports.read, dan setiap download
menulis recordAuditEvent-nya sendiri (berbeda dari entri audit "job
selesai") — SIAPA yang mengunduh artefak mentah harus terlacak.
awcms-mini.data-exchange.import.{staged,previewed,committed,failed},
.export.completed, .reconciliation.mismatch.
sensitiveFields — gate registry
menolaknya; nyatakan { fieldNames: [] } bila memang tidak ada.data_exchange.* generik —
pakai descriptor.sensitiveFields.rawValuePermission.null lolos guard — 409 INVALID_STATE.Kegagalan commit per-baris yang retryable menghentikan pass worker saat
ini tanpa menandai baris 'failed' — baris tetap 'pending' dan
dicoba lagi pada tick berikutnya. Tidak ada eskalasi otomatis
retryable→failed setelah N attempt; baris yang terjebak loop transient
butuh intervensi operator (pause + investigasi). Lihat header
application/import-commit-job.ts.
tests/unit/data-exchange-*.test.ts (14 file: codec, guard, state,
masking, registry, parity, neutralization) dan
tests/integration/data-exchange.integration.test.ts. Jalankan bun test
dengan DATABASE_URL — bun run check tanpa DATABASE_URL melewatkan
seluruh test integration secara diam-diam.