一键导入
awcms-workflow-approval
Kerjakan bagian mana pun dari modul workflow_approval AWCMS (Issue 11.1 linear engine, evolved Issue
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Kerjakan bagian mana pun dari modul workflow_approval AWCMS (Issue 11.1 linear engine, evolved Issue
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | awcms-workflow-approval |
| description | Kerjakan bagian mana pun dari modul workflow_approval AWCMS (Issue 11.1 linear engine, evolved Issue |
workflow_approval (src/modules/workflow-approval, Issue 11.1 lalu
evolved Issue #747 epic platform-evolution #738 Wave 2) adalah managed,
versioned, graph-based enterprise workflow minimum — tetap menjaga
guardrail asli base: tidak ada term/aksi bisnis domain-spesifik (base tidak
mengirim POS cancel/Coretax export/warehouse transfer), tidak ada BPMN
engine eksternal, dan tidak ada eksekusi kode runtime di condition/action
(doc 21 §3 decision tree, node Q5). Baca
src/modules/workflow-approval/README.md untuk detail lengkap; skill ini
merangkum invariant keamanan yang WAJIB dipertahankan (4 finding PR #778
sudah pernah diregresi sekali, jangan diulang).
Melengkapi awcms-abac-guard (self-approval-deny check yang dipakai
ulang di sini), awcms-idempotency, awcms-audit-log. Skill ini
menyediakan konteks graph-engine dan invariant keamanan spesifik modul ini.
| Issue 11.1 (linear) | Issue #747 (managed, graph-based) |
|---|---|
Satu status: active/inactive per definisi | version + lifecycle_status: draft/active/retired, versi immutable |
steps (jsonb list terurut) | graph (nodes/transitions — approval/condition/parallel/join/notify/end) |
| Tidak ada endpoint create-definition publik | POST/PUT/DELETE /workflows/definitions, .../publish, .../retire, dst |
current_step_order (satu int) | awcms_workflow_tasks (satu baris per node aktif — multi-node aktif) |
| Satu assignee implisit | _task_assignments — quorum/any/all, delegation-resolved deciders |
| Tidak ada delegation | _delegations — effective-dated, scoped, reason, audited, revocable |
| Tidak ada escalation/timeout | Per-node escalation config + workflow:escalations:dispatch |
| Tidak ada administrative recovery | Reassign/cancel/force-approve/force-reject, permission-gated+audited |
domain/workflow-graph.ts) — set node type TERTUTUP, bukan scripting engineapproval — 1+ assigneeTenantUserIds; quorumRule (all/any/
quorum + quorumThreshold) menentukan kapan node selesai. Satu
reject SELALU menyelesaikan node sebagai rejected, apa pun rule-nya
(default konservatif yang disengaja, domain/workflow-quorum.ts).
escalation config opsional (timeoutMinutes,
escalateToTenantUserId, maxEscalations).condition — SALAH SATU: perbandingan bounded (factKey/
operator/value, operator eq|neq|gt|gte|lt|lte|in) atas fact yang
dideklarasikan di factsSchema definisi, ATAU referensi ke
WorkflowConditionResolver yang terdaftar statis (resolverName).
Tidak pernah keduanya, tidak pernah tidak ada satu pun.parallel/join — fan-out 2+ cabang konkuren, fan-in setelah
SEMUA cabang tiba di join (awcms_workflow_join_arrivals). Nested
parallel/join TIDAK didukung (lihat §Deferred).notify — memicu notifikasi lewat capability port
WorkflowNotificationPort (ADR-0011; adapter di email, wraps
enqueueAnnouncement) dan langsung lanjut; tidak pernah blocking.end — terminal; set outcome instance.validateWorkflowGraph memvalidasi struktural setiap referensi node,
batas quorum threshold, kecocokan branch-set parallel/join, dan menolak
cycle (DFS) — jalan di SETIAP write definisi DAN lagi saat publish
(defense in depth).
awcms_workflow_instances.workflow_definition_id (FK, immutable
setelah published) + workflow_definition_version terdenormalisasi
mem-pin setiap instance ke baris definisi PERSIS yang aktif saat
startWorkflowInstance berjalan. Karena baris published/active/retired
tidak pernah diedit di tempat (application/workflow-definition-directory.ts
menegakkan editing hanya untuk draft), setiap baca/advance instance itu
nanti selalu re-fetch graph yang identik terlepas dari versi baru yang
dipublikasikan setelahnya.
domain/workflow-delegation.ts)Delegation HANYA membiarkan delegate bertindak memakai standing MILIK
delegator — TIDAK PERNAH permission grant, tidak pernah lebih luas dari
workflowKey/resourceType/window efektif yang dideklarasikan baris
delegation itu sendiri. Self-approval denial (identity-access/domain/ access-control.ts, tidak berubah) tetap membandingkan tenant user yang
BERTINDAK terhadap requested_by_tenant_user_id instance — delegate TIDAK
BISA dipakai untuk approve request yang delegatornya sendiri ajukan.
Create (POST /workflows/delegations) dan revoke
(POST /workflows/delegations/{id}/revoke) WAJIB Idempotency-Key dan
tercatat lewat recordAuditEvent (TAMBAHAN dari domain event
workflow.delegation.created/.revoked yang sudah dipublikasikan lewat
outbox domain_event_runtime — audit log dan domain event adalah DUA
record berbeda, dikonsumsi independen, bukan hal yang sama). Revoke
di-gate pada permission workflow.delegation.revoke (Owner/Manager per
doc 17) — ownership check revokeWorkflowDelegation (hanya delegator
asli boleh revoke) tetap sebagai defense-in-depth DI ATAS permission gate
itu, bukan pengganti (lihat §Security finding di bawah — ini pernah bug).
application/workflow-escalation.ts)Dibangun di atas shared worker runner (src/lib/jobs/job-runner.ts) —
bounded batch, advisory lock, --dry-run. Idempotency guard: UPDATE
escalation dikondisikan WHERE status = 'pending' AND escalation_step = <value dibaca pass ini> — race yang kalah (run konkuren, atau pass yang
di-retry) mempengaruhi nol baris dan diam-diam di-skip, tidak pernah
escalate dobel. Role DB-nya: di mini job ini berjalan sebagai role
least-privilege awcms_worker (SELECT-only sejak fix PR #778, lihat
§Security finding). Repo ini tidak punya role awcms_worker —
WORKER_DATABASE_URL fallback ke DATABASE_URL (owner migrasi), jadi job
ini hari ini berjalan dengan hak penuh dan pemisahan privilege itu BELUM
ada di sini. Role yang ada baru awcms_app (sql/019, Issue #141).
Jangan tulis GRANT ... TO awcms_worker di migration repo ini — akan gagal
jalan.
application/workflow-recovery.ts)Reassign (POST /workflows/tasks/{id}/reassign), cancel
(POST /workflows/instances/{id}/cancel), force-approve/force-reject
(POST /workflows/tasks/{id}/force-decision) — masing-masing
permission-gated (workflow.recovery.reassign/.cancel/.force_decide),
reason-required, Idempotency-Key, fully audited. Tidak pernah menimpa/
menghapus baris decision/task/assignment sebelumnya — selalu append baris
baru atau transisi status ter-guard.
force-decision self-approval bypass (High) — route mengotorisasi
lewat workflow.recovery.force_decide TANPA mengisi
resourceAttributes.requestedByTenantUserId, dan self-approval-deny
check access-control.ts di-hardwire hanya untuk action "approve" —
sehingga caller yang mengajukan instance-nya SENDIRI dan punya
force_decide bisa force-approve permintaannya sendiri, bypass quorum
sepenuhnya. Fix: lookup task/instance SEBELUM guard (pola sama
decisions.ts), dan self-approval-deny check diperluas mencakup
"force_decide" juga (blok force-approve DAN force-reject instance
milik sendiri). Endpoint recovery baru wajib lookup requester
SEBELUM guard, mengikuti pola ini.publish, retire, handler
DELETE definitions, dan delegation create/revoke tidak memanggil
recordAuditEvent meski mutation high-risk; kelima sekarang
memanggilnya. DELETE .../definitions/{id} dan kedua endpoint
delegation juga sempat kehilangan enforcement Idempotency-Key —
sekarang ditambahkan.workflow.delegation.revoke tidak ditegakkan (Low) —
route revoke di-gate pada workflow.delegation.read dan HANYA
mengandalkan ownership check; permission revoke yang sudah diseed
(doc 17: Owner/Manager RCV) jadi dead code. Fix: gate pada
workflow.delegation.revoke.SELECT, UPDATE di awcms_workflow_instances ke
awcms_worker, padahal escalation job hanya pernah SELECT dari
tabel itu. Dipangkas jadi SELECT-only. Temuan ini vacuous di repo
ini: tidak ada role awcms_worker maupun migrasi 060 di sini — GRANT
yang over-scope itu tidak pernah ada untuk dipangkas. Relevan hanya
sebagai pelajaran saat pemisahan worker role akhirnya di-port.Pelajaran generik dari keempatnya: endpoint action baru pada resource
yang punya konsep "pemilik/requester" WAJIB (a) lookup requester SEBELUM
guard supaya self-approval-deny bisa membandingkan, (b) selalu panggil
recordAuditEvent untuk mutation high-risk meski "cuma" administrative
action, (c) gate pada permission SPESIFIK-nya sendiri (jangan reuse
permission .read yang lebih lemah), (d) worker role grant SELALU
diverifikasi hanya sebesar yang benar-benar dipakai query nyata.
parallel/join — branch yang punya parallel node sendiri
TIDAK didukung; awcms_workflow_join_arrivals asumsikan satu level
nesting.any-join — hanya all-join yang diimplementasikan.action yang memanggil WorkflowActionHandler terdaftar
— registry/port sudah ada dan teruji, tapi belum ada node type yang
memanggilnya.findEligibleAssignment/evaluateAccess tanpa rewrite, tapi belum ada
yang SoD-specific dibangun di sini.POST/PUT /workflows/definitions/**
hanya via API, tidak ada UI authoring graph.Setiap mutation high-risk (decisions, reassign, force-decision,
publish, retire, DELETE .../definitions/{id},
.../instances/{id}/cancel, .../delegations create,
.../delegations/{id}/revoke) wajib Idempotency-Key, memakai store
generik awcms_idempotency_keys yang sama.
evaluateAccess — kalau tidak, self-approval-deny check
tidak punya nilai untuk dibandingkan (lihat finding #1 di atas).recordAuditEvent untuk mutation baru — domain event lewat
appendDomainEvent BUKAN pengganti audit log, keduanya wajib ada..read sebagai gate mutation — selalu bikin
permission spesifik-aksi (.revoke, .reassign, dst.).parallel/join tanpa redesign
awcms_workflow_join_arrivals's skema fan-in — asumsi satu-level
nesting tertanam di situ.condition node — hanya
perbandingan bounded atau resolver terdaftar statis, tidak pernah
eval/scripting.Cari tests/**/workflow*.test.ts dan tests/integration/workflow*.integration.test.ts
untuk test self-approval-deny, quorum, delegation, escalation idempotency,
dan recovery action. Jalankan bun test dengan DATABASE_URL — bun run check tanpa DATABASE_URL melewatkan test integration secara diam-diam.
Triase dan perbaiki temuan CodeQL code scanning AWCMS (github.com/ahliweb/awcms/security/code-scanning). Gunakan saat diminta "analisis code scanning"/"perbaiki CodeQL", saat sebuah PR gagal check CodeQL, atau saat menemukan alert baru. Mendokumentasikan enam false-positive nyata yang sudah ditemukan (name-heuristic password, incompatible-types typeof/null, URL substring-sanitization di test mock, dua kasus dismiss resmi tanpa reformulasi kode, Bun.SQL tagged-template null-cast, dan build-time extension seam trivial-conditional) plus pola "unused-local-variable di test kadang menandai coverage gap", DAN satu counter-example trivial-conditional yang ternyata bug NYATA (dead-code fallback karena helper non-nullish, alert
Port sebuah modul/fitur yang sudah matang & teruji dari repo awcms-mini ke repo awcms secara atomic, adaptasi (bukan copy), sampai semua cek hijau + commit atomic. Gunakan saat diminta "port modul <X> dari mini", "terapkan fitur mini ke awcms", atau saat sebuah modul fondasi awcms-mini belum ada di awcms. Menegakkan kontrak alur mini-first (docs/awcms/alur-pengembangan-mini-first.md): rename prefix awcms_mini_→awcms_, penomoran migrasi lanjutan, drop dependensi/toolchain yang belum ada di awcms, dan verifikasi DoD.
Jalankan proses rilis AWCMS dengan Changesets. Gunakan saat diminta merilis versi, bump version, generate CHANGELOG, membuat tag vX.Y.Z, atau memeriksa changeset pending. Sesuai kebijakan SemVer doc 09.
BACAAN SAJA / HISTORIS (ADR-0034) — kontrak kesiapan ekstensi ERP base (`_shared/business-transaction-contract.ts`/`_shared/erp-reference-data-contract.ts`/`_shared/ports/period-lock-port.ts`) DAN jalur aplikasi-turunan yang skill ini asumsikan SUDAH DIHAPUS. ERP kini dibangun sebagai modul `domain` LANGSUNG di `src/modules/` (pola sama modul base lain), bukan repo turunan; file kontrak & fixture `example-erp-extension` yang dirujuk tidak lagi ada. Rujukan historis dari Issue
Audit dan perkuat backend & integrasi eksternal AWCMS (resiliensi, outbox, webhook/provider, versioning API, observability). Gunakan saat menambah/mengeraskan integrasi ke provider luar (R2, WhatsApp, email, AI, pajak), memperbaiki keandalan delivery, atau menaikkan ketahanan backend. Menegakkan ADR-0006 (provider opsional, di luar transaksi) dan pola outbox doc 16.
Scaffold modul baru pada modular monolith AWCMS. Gunakan saat membuat modul domain baru di src/modules/ (mis. warehouse-management, accounting-tax) atau saat memerlukan struktur module.ts + domain/application/infrastructure/api + README. Ikuti struktur standar doc 10 & 11.