From 770ecbd20447a3d73e55b673391af412bef98ffc Mon Sep 17 00:00:00 2001 From: Deardo Satria Date: Mon, 2 Nov 2026 13:43:33 +0700 Subject: [PATCH] Clarify mandatory checks in cleo-code-review skill Mark steps 1-6 as ordered with mandatory mechanical checks (reference check before delete, session chain, zod messages, import order), add the CRLF caveat for prettier on Windows, and spell out the report format with a concrete example. --- .claude/skills/cleo-code-review/SKILL.md | 171 ++++++++++++++++++++--- 1 file changed, 153 insertions(+), 18 deletions(-) diff --git a/.claude/skills/cleo-code-review/SKILL.md b/.claude/skills/cleo-code-review/SKILL.md index e9c0790..be5df7d 100644 --- a/.claude/skills/cleo-code-review/SKILL.md +++ b/.claude/skills/cleo-code-review/SKILL.md @@ -8,6 +8,9 @@ description: Review struktur dan penulisan kode Cleo Minimarket (Next.js 16, Ser Review read-only atas perubahan kode di proyek ini. Tujuannya menangkap bug nyata dan pelanggaran aturan proyek sebelum di-push. Jangan memperbaiki file kecuali diminta. +Kerjakan langkah 1 sampai 6 berurutan. Langkah 3 berisi perintah yang wajib dijalankan, bukan +sekadar dipertimbangkan; checklist di langkah 4 baru dibaca setelahnya. + ## 1. Tentukan cakupan - Default: commit yang belum di-push plus perubahan yang belum di-commit. @@ -17,6 +20,7 @@ pelanggaran aturan proyek sebelum di-push. Jangan memperbaiki file kecuali dimin git diff --stat '@{u}' git diff '@{u}' ``` + File baru (`??` di `git status`) tidak muncul di `git diff`; baca isinya langsung. - Kalau user menyebut commit, branch, atau file tertentu, pakai itu. - Abaikan `old-source/`, `backup/`, `drizzle/`, `components/ui/`, `db/amigo/schema.ts` (referensi, backup, atau generated), kecuali perubahannya memang di sana dan mencurigakan. @@ -33,10 +37,90 @@ pnpm build Kutip baris error terpendek yang menentukan. Build perlu dijalankan karena `cacheComponents` membuat error Suspense baru muncul di build, bukan di `tsc`. -## 3. Baca diff dengan checklist ini +Kalau `prettier --check` gagal, pastikan dulu penyebabnya bukan akhir baris CRLF. Di Windows, +`core.autocrlf=true` membuat file di disk ber-CRLF walau isi di git tetap LF: + +``` +pnpm exec prettier --check --end-of-line auto +git ls-files --eol +``` + +Kalau perintah pertama lolos dan kolom `i/` di perintah kedua berisi `lf`, prettier dianggap +lolos. Itu bukan temuan, jangan sarankan `prettier --write` atau mengubah config prettier. +Laporkan sebagai temuan hanya kalau `i/crlf` (CRLF ikut ter-commit) atau `--end-of-line auto` +tetap gagal. + +## 3. Pengecekan mekanis wajib + +Jalankan setiap pengecekan yang relevan dengan diff, lalu catat hasilnya. Pelanggaran yang +juga ada di fitur acuan (`master/pelanggan`) tetap dilaporkan; sebutkan bahwa fitur acuan ikut +kena, jangan dijadikan alasan untuk lolos. + +### 3a. Referensi sebelum hapus + +Untuk setiap fungsi `delete` yang baru atau berubah, cari tabel lain yang menyimpan +kunci baris yang dihapus. Nama kolom penyimpannya sering berbeda dari nama kolom kunci +(mis. `no_barang.satuan` menyimpan `satuan_barang.idSatuanBarang`), jadi cari lewat nama +tabelnya di query lama: + +``` +grep -rn "" old-source --include=*.php +``` + +Baca query yang memakai tabel itu dalam `JOIN` atau `WHERE a.x = b.y`. Kalau ada tabel lain +yang menunjuk ke baris ini dan action hapus tidak mengecek pemakaian dulu, laporkan sebagai +**Bug**. Skenarionya: data di tabel lain jadi yatim, dan query `JOIN` di aplikasi PHP diam-diam +menyembunyikan baris itu. Kalau DB lokal menyala, hitung baris yatim yang sudah ada sebagai +bukti: + +``` +echo 'SELECT COUNT(*) FROM t LEFT JOIN x ON t. = x. WHERE x. IS NULL;' | docker exec -i cleo-mariadb sh -c 'mysql -uroot -p$MARIADB_ROOT_PASSWORD -N cleo' +``` + +### 3b. Session di setiap halaman dashboard + +Setiap file di `app/(dashboard)/` yang memanggil fungsi `data/` (selain `data/auth`) untuk +dirender harus ikut memanggil `getCurrentUser()` di rantai render-nya, yaitu di page itu +sendiri atau di komponen server yang mengambil datanya: + +``` +grep -rn --include=*.tsx 'from "@/data/' "app/(dashboard)" | grep -v 'import type' | grep -v '@/data/auth' +grep -rln --include=*.tsx 'getCurrentUser' "app/(dashboard)" +``` + +Perintah pertama sudah membuang `import type` (hanya tipe, tidak membaca data). File yang +muncul di daftar pertama tapi rantai render-nya tidak ada di daftar kedua adalah +**Bug**. `proxy.ts` hanya mengecek ada/tidaknya cookie, dan `SidebarUser` di layout tidak +dirender ulang saat navigasi client, jadi cookie kedaluwarsa tetap bisa melihat data. + +### 3c. Pesan zod + +Setiap aturan zod yang bisa gagal karena isian user (`.min`, `.max`, `.regex`, `.email`, +`z.enum`, dan sejenisnya) harus membawa pesan bahasa Indonesia sendiri. Tanpa itu, zod +menampilkan pesan bawaan bahasa Inggris (mis. "Too big: expected string to have <=30 +characters"). + +``` +grep -rn --include=actions.ts '\.min(\|\.max(\|\.regex(\|\.email(\|z\.enum(' app +``` + +Baris yang argumennya hanya angka, mis. `.max(30)` tanpa pesan, adalah temuan. + +### 3d. Urutan import + +Untuk setiap file yang berubah, termasuk file lama yang hanya diubah sedikit, cek urutan: +package luar, baris kosong, `@/...`, lalu path relatif (`./`, `../`). Import `@/...` yang +ditulis setelah import relatif adalah temuan **Kerapian**. + +## 4. Baca diff dengan checklist ini Untuk temuan di sekitar kode, baca file utuhnya, jangan hanya potongan diff. +**"Sama dengan PHP lama" bukan alasan untuk lolos.** Aturan bisnis ditiru dari `old-source/`, +tapi kalau perilaku lama merusak data (hapus tanpa cek referensi, penomoran bentrok, stok +dihitung baca-hitung-tulis), tetap laporkan sebagai bug. Sebutkan bahwa PHP lama berperilaku +sama supaya pembaca tahu ini bukan regresi. + ### Auth dan routing - `proxy.ts` hanya redirect berdasarkan ada/tidaknya cookie. Redirect "sudah login -> /" di @@ -45,8 +129,8 @@ Untuk temuan di sekitar kode, baca file utuhnya, jangan hanya potongan diff. (`redirectIfLoggedIn()` di `data/auth.ts`), dipanggil dari page di dalam ``. - Matcher proxy harus tetap mengecualikan `login`, `api`, dan aset statis. - Import dari path internal Next (`next/dist/...`) hampir selalu salah. -- Page dan action yang butuh user memanggil `getCurrentUser()`; layout tidak dipakai untuk cek - session. +- Page dan action yang butuh user memanggil `getCurrentUser()` (lihat 3b); layout tidak + dipakai untuk cek session. - Komponen yang membaca `headers()`/`cookies()`/`searchParams` berada di dalam ``. ### Server Actions @@ -56,6 +140,7 @@ Untuk temuan di sekitar kode, baca file utuhnya, jangan hanya potongan diff. (bukan query langsung), kembalikan objek kecil, `revalidatePath` sebelum `redirect`. - Error validasi dikembalikan per field (`z.flattenError(err).fieldErrors`), bukan hanya `issues[0].message`; kalau tidak, pesan tampil di field yang salah. +- Setiap aturan zod membawa pesan bahasa Indonesia (lihat 3c). - Return yang gagal ikut membawa `values` (isian mentah), karena React 19 me-reset form setelah action selesai dan ketikan user hilang. - `console.error` berisi tag nama action, `user.id`, input relevan (tanpa password/secret), @@ -86,6 +171,7 @@ Untuk temuan di sekitar kode, baca file utuhnya, jangan hanya potongan diff. - MariaDB `cleo` itu MyISAM: tidak ada transaksi dan `FOR UPDATE` tidak mengunci. Penomoran otomatis harus tahan bentrok dengan PHP lama (retry saat `ER_DUP_ENTRY`), update stok atomik satu statement, master + detail divalidasi penuh sebelum statement pertama. +- Hapus baris yang masih direferensikan tabel lain harus ditolak dulu (lihat 3a). - Tanggal "hari ini" pakai zona `Asia/Jakarta`, bukan `toISOString()` (UTC). - Tanggal `0000-00-00` tetap string, tidak diubah ke `Date`. - Batas panjang zod sama dengan panjang kolom di `db/amigo/schema.ts`. @@ -110,8 +196,9 @@ lain yang sejenis. - `components/` (lintas fitur) tidak import dari `app/`. - Duplikasi lintas fitur: kode yang sama muncul di fitur kedua dipindah ke satu tempat bersama, bukan disalin. Kandidat umum: escape `LIKE`, tipe hasil action - (`{ ok, error, fieldErrors, values }`), pola log error action, pagination, kotak pencarian, - skema zod yang berulang. Jangan memindahkan saat baru dipakai satu fitur. + (`{ ok, error, fieldErrors, values }`), pola log error action, pagination (termasuk helper + `href` halaman dan parsing `?page=`), kotak pencarian, skema zod yang berulang. Jangan + memindahkan saat baru dipakai satu fitur. - Aturan bisnis lama (kode huruf harga `convHrgP`/`convProfit`, format tanggal `dd-mm-yyyy`, tanggal "hari ini" `Asia/Jakarta`) punya satu implementasi di `lib/`, tidak ditulis ulang per fitur. @@ -120,25 +207,73 @@ lain yang sejenis. daftar, dan dialog dalam satu file), ditandai untuk dipecah. - Navigasi: setiap `href` di `app/(dashboard)/_components/nav-items.ts` punya `page.tsx`, dan setiap page baru di `(dashboard)` terdaftar di nav (kecuali halaman detail/turunan). -- Kalau perubahan memperkenalkan konvensi baru (folder, helper bersama, pola action), cek - apakah `CLAUDE.md` ikut diperbarui supaya fitur berikutnya mengikuti pola yang sama. +- Kalau perubahan memperkenalkan konvensi baru (folder, helper bersama di `components/` atau + `lib/`, pola action), cek apakah `CLAUDE.md` ikut diperbarui supaya fitur berikutnya memakai + helper itu. Kalau belum, itu temuan **Tidak sesuai aturan proyek**. ### Kerapian - Format sesuai Prettier (titik koma, baris tidak terlalu panjang, newline di akhir file). -- Urutan import: package luar, baris kosong, lalu `@/...`, lalu path relatif. +- Urutan import: package luar, baris kosong, lalu `@/...`, lalu path relatif (lihat 3d). - Nama file kebab-case, komponen PascalCase, import lintas folder lewat alias `@/`. -## 4. Laporkan +## 5. Verifikasi sebelum melapor + +Untuk setiap calon temuan: + +1. Buka ulang file di baris yang akan disebut dan pastikan kodenya memang seperti yang + diklaim. Nomor baris harus cocok dengan isi file saat ini. +2. Untuk bug, pastikan skenarionya benar-benar bisa terjadi lewat jalur yang ada (UI, POST + langsung ke action, atau aplikasi PHP yang memakai tabel yang sama). +3. Buang temuan yang tidak terbukti. Temuan yang masuk akal tapi belum bisa dibuktikan boleh + tetap ditulis dengan penanda "perlu dicek", bukan dinyatakan pasti. + +## 6. Laporkan + +Ikuti aturan output user (bahasa, nada) dari CLAUDE.md global. Struktur laporannya: -Kalimat pertama berisi kesimpulan: siap di-push atau belum, dan hasil `tsc`/`lint`/`build`. -Lalu temuan dikelompokkan dan diberi nomor berurutan lintas kelompok: +- **Kalimat pertama adalah verdict**: "Siap di-push." atau "Belum siap di-push: .", diikuti hasil `tsc`/`lint`/`prettier`/`build` dalam satu kalimat. Jangan membuka + dengan "Sudah saya review...", "Berikut hasil review...", atau sejenisnya. +- Prettier yang gagal hanya karena CRLF (lihat langkah 2) ditulis "lolos", boleh disertai + satu kalimat penjelasan, dan tidak membuat verdict jadi "belum siap". +- Temuan dikelompokkan dan diberi nomor berurutan lintas kelompok: + 1. **Bug**: perilaku salah yang bisa dialami user. Wajib ada skenario konkret. + 2. **Tidak sesuai aturan proyek**: pelanggaran CLAUDE.md. + 3. **Struktur dan maintainability**: menyimpang dari pola fitur acuan, arah dependency + salah, duplikasi lintas fitur, file terlalu besar, nav tidak sinkron. + 4. **Kerapian**: format, urutan import, kode mati, duplikasi kecil di dalam satu file. -1. **Bug**: perilaku salah yang bisa dialami user. Sertakan skenario konkretnya. -2. **Tidak sesuai aturan proyek**: pelanggaran CLAUDE.md. -3. **Struktur dan maintainability**: menyimpang dari pola fitur acuan, arah dependency salah, - duplikasi lintas fitur, file terlalu besar, nav tidak sinkron. -4. **Kerapian**: format, kode mati, duplikasi kecil di dalam satu file. + Kelompok yang kosong tidak ditulis. -Setiap temuan menyebut `file:baris`, apa yang salah, dan arah perbaikannya dalam satu atau -dua kalimat. Tutup dengan daftar singkat hal yang sudah benar, lalu saran urutan perbaikan. +- Setiap temuan menyebut `file:baris`, apa yang salah, dan arah perbaikannya dalam satu atau + dua kalimat. Bug boleh lebih panjang karena butuh skenario dan bukti. +- Tutup dengan daftar singkat hal yang sudah benar, lalu saran urutan perbaikan (nomor + temuan, bug lebih dulu), lalu satu tawaran konkret untuk dikerjakan berikutnya. + +Contoh bentuk laporan (isi hanya ilustrasi): + +``` +Belum siap di-push: satuan yang masih dipakai barang bisa dihapus. tsc, lint, prettier, +dan build lolos (peringatan prettier hanya karena CRLF di disk, isi git tetap LF). + +## Bug + +1. **`data/satuan-barang.ts:46`**: `deleteSatuanBarang` tidak mengecek pemakaian di + `no_barang.satuan`. Skenario: user menghapus satuan "PCS", lalu semua barang bersatuan + PCS hilang dari daftar barang PHP karena query di `old-source/ajaxbarang.php:302` memakai + inner join. DB lokal sudah punya 12 baris yatim. PHP lama berperilaku sama, jadi ini + bukan regresi. Fix: hitung pemakaian dulu dan tolak dengan pesan "Satuan masih dipakai + N barang". + +## Kerapian + +2. **`app/(dashboard)/master/pelanggan/page.tsx:5`**: import `@/components/cari-master` + ditulis setelah import relatif. Pindahkan ke atas blok `./_components/...`. + +## Yang sudah benar + +- Action mengecek session, validasi zod, dan mengembalikan `fieldErrors` plus `values`. + +Urutan perbaikan: 1, lalu 2. Mau saya kerjakan nomor 1 sekarang? +```