diff --git a/AGENTS.md b/AGENTS.md index 51ac5731..5dd161de 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,179 +1,100 @@ -# GMW — Agent Development Guide +# GMW — Agent Guide -> **Rule #1: No code without a spec.** -> Setiap perubahan signifikan dimulai dari spec di `docs/specs/`. -> Baca spec → verifikasi facts → implement → verify → commit. -> Gunakan `todo.md` (TODO list terdokumentasi) untuk melacak progress. +GMW (Guild Moderation Watcher) is a Discord bot + web dashboard for AI-powered moderation. A monorepo with three services: a selfbot gateway that captures messages/voice and runs LLM moderation, an Express/oRPC backend that serves the dashboard API, and a Next.js 16 SSR frontend. They communicate via Redis pub/sub (gateway→backend) and WebSocket (backend→browser). -## Project overview - -GMW (Go Mod Watch) adalah Discord bot + dashboard untuk AI-powered moderation. -Monorepo berisi 3 service utama: - -| Service | Path | Port | Tech | -|---|---|---|---| -| **discord-gateway** | `services/discord-gateway/` | 4016 (metrics) | discord.js-selfbot-v13, Piscina, Drizzle ORM, pino | -| **backend** | `services/backend/` | 4001 | Express, oRPC, Drizzle ORM, Redis pub/sub, Vitest | -| **frontend** | `services/frontend/` | 4017 (standalone) | Next.js 16 App Router, React 19, Tailwind v4, SWR | - -## Data flow - -``` -Discord → discord-gateway → Redis pub/sub → backend (:4001) ←→ frontend Next.js SSR - ↑ REST /api/* - └ WS /ws -``` - -- Gateway = event-driven (no HTTP, except Prometheus :4016/metrics) -- Backend = HTTP + WebSocket server, serves the frontend -- Frontend = SSR (RSC) + client hydration, proxied by nginx :4009 - -## Konvensi dokumentasi: `docs/` - -Semua dokumen kerja ada di `docs/`, bukan di `.hermes/`: - -``` -docs/ -├── README.md # Panduan workflow (spec-driven + todo) -├── spec-template.md # Template spec standar -├── todo-template.md # Template todo list -└── specs/ # Semua spec & implementation plan - ├── YYYY-MM-DD_-spec.md # Spec (apa & mengapa) - └── YYYY-MM-DD_.md # Plan/fix (spec + plan dalam satu file) -``` - -Service-specific docs: `services//docs/specs/`. - -## Workflow: Spec-Driven Development - -### Checklist wajib untuk setiap perubahan signifikan - -1. **Tulis spec** → `docs/specs/YYYY-MM-DD_-spec.md` -2. **Tulis `todo.md`** → daftar task konkret yang bisa diceklis -3. **Verifikasi facts** → baca kode aktual, konfirmasi referensi file:line -4. **Keputusan desain** → pilih approach, dokumentasikan alternatif yang ditolak -5. **Implementasi** → ikuti spec + todo step-by-step -6. **Verifikasi** → jalankan semua verification steps dari spec -7. **Commit** → reference spec di commit message - -### Todo list (`todo.md`) - -Setiap tugas berjalan WAJIB punya `todo.md`. Format: - -```markdown -# Todo — - -## Task -- [ ] Tulis spec -- [ ] Verifikasi facts (baca kode: file:line) -- [ ] Implementasi tahap 1: ... -- [ ] Implementasi tahap 2: ... -- [ ] Verifikasi: pnpm typecheck / lint / build / test -- [ ] Commit + push -``` - -Aturan: -- Task harus **konkret & verifiable** — bukan "fix bug", tapi "ubah X di file Y". -- Ceklis `[x]` saat selesai, jangan menunggu batch di akhir. -- Gunakan `docs/todo-template.md` sebagai template. -- Simpan `todo.md` di `docs/` untuk tugas lintas-service, atau di - `services//docs/` untuk tugas satu service. - -### Spec template - -Salin `docs/spec-template.md` untuk setiap spec baru. Sections: - -1. **Problem** — apa yang rusak/missing, dengan evidence -2. **Root cause** — analisis teknis (bukan symptom) -3. **Behavior target** — perilaku setelah fix, daftar verifiable -4. **Verified facts** — fakta dari pembacaan kode, citation ke file:line -5. **Keputusan desain** — pilihan + rationale + alternatif ditolak -6. **Perubahan file** — semua file yang disentuh, per service -7. **Schema/type changes** — perubahan tipe/DB -8. **Verification** — command executable + expected outcome - -### Kapan perlu spec + todo.md - -Perlu: fitur baru, bug fix non-trivial, refactor behavior-changing, perubahan -DB schema, perubahan API contract, perubahan arsitektur. - -Tidak perlu: typo fix, dep bump, format/lint auto-fix, test-only, README update. - -Baca selengkapnya di `docs/README.md`. - -## Coding conventions - -### General - -- **TypeScript strict mode** — semua service -- **Biome** — formatting + linting (`pnpm format`, `pnpm lint`) -- **Bun** — package manager (`bun install`, `bun run`) -- **Bisa bilingual** — code comments & specs boleh Indonesia/English - -### Per-service conventions - -#### discord-gateway -- **Event-driven** — no HTTP server (except metrics). Listeners → Redis pub/sub. -- **Module pattern**: `src/modules//` — each module encapsulates own logic. -- **Piscina pools**: text pool (4 threads) + media pool (2 threads), each with own pg Pool. -- **Logger**: `createChildLogger('module-name')` — never raw `console`. -- **Config**: Zod-validated env in `shared/config/index.ts` — single source of truth. -- **DB**: Drizzle ORM. Migrations in `drizzle/migrations/`. -- **Invariant**: LLM is the only judge. Never reintroduce regex content classification. -- **Invariant**: Discord tokens sanitized before reaching LLM. -- Lihat `services/discord-gateway/AGENTS.md` untuk detail. - -#### backend -- **Modular MVC**: `modules//` — schema → repository → service → controller → routes. -- **No cross-module repo imports** — each module owns its data. -- **Data flows up only**: Repository → Service → Controller. -- **Error hierarchy**: `AppError` subclasses with code + statusCode. -- **Config**: Zod-validated env in `shared/config/index.ts`. -- **API**: oRPC for type-safe procedures + standard Express routes. -- Lihat `services/backend/AGENTS.md` untuk detail. - -#### frontend -- **SSR-first**: `page.tsx` (server component) → fetch via `src/lib/api/server.ts` → pass to `view.tsx` (client). -- **No auth**: all endpoints public. -- **Never hardcode host**: same-origin or `GMW_BACKEND_URL` only. -- **WebSocket**: `src/lib/ws/` — auto-reconnecting, typed events. -- **Local dev**: `NEXT_PUBLIC_API_URL`, `NEXT_PUBLIC_WS_URL`, `GMW_BACKEND_URL`. -- Lihat `services/frontend/AGENTS.md` untuk detail Next.js rules. - -## Build & Verify +## Quick reference ```bash -# Per service (run from service root) -pnpm typecheck # TypeScript strict -pnpm lint # Biome check -pnpm build # Compile -pnpm test # Vitest (gateway & backend only) -pnpm format # Biome auto-format +# Per-service — cd into the service first +pnpm install # install deps (pnpm 11, not npm or bun) +pnpm typecheck # tsc --noEmit +pnpm lint # biome check +pnpm format # biome format --write +pnpm build # gateway/backend: tsc + fix-imports.mjs; frontend: next build +pnpm test # vitest run (gateway & backend only — frontend has no tests) ``` -## Commit conventions +No monorepo-level scripts exist. Run each command from inside the service directory. + +## Layout ``` -(): - - - -Ref: docs/specs/.md +services/ +├── discord-gateway/ Event-driven selfbot. No HTTP (except :4016 metrics). +│ ├── src/ +│ │ ├── app/ Bootstrap, shutdown, retention +│ │ ├── modules/ Feature modules — each self-contained +│ │ └── shared/ Config, DB (Drizzle), logger, errors, utils +│ ├── tests/ Vitest tests (colocated, not inside src/) +│ ├── drizzle/migrations/ DB migrations +│ └── scripts/fix-imports.mjs Post-build: rewrites @/ aliases → relative .js +│ +├── backend/ Express HTTP + WebSocket + oRPC server (:4001). +│ ├── src/ +│ │ ├── modules/ Feature modules (schema→repo→service→controller→routes) +│ │ ├── http/ Express app setup +│ │ ├── ws/ WebSocket server + Redis bridge +│ │ ├── orpc/ oRPC router definition +│ │ └── shared/ Config, DB, errors, Redis, logger +│ └── tests/ Vitest tests +│ +└── frontend/ Next.js 16 App Router, React 19, Tailwind v4 (:4017). + ├── src/ + │ ├── app/ Pages — route groups under (dashboard)/ + │ ├── components/ UI components (primitives, shell, charts, etc.) + │ ├── hooks/ React hooks + │ └── lib/ API clients, types, utils, WebSocket, audio + └── pnpm-workspace.yaml Build-script approvals (sharp only) ``` -Types: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`, `build`, `ci` +## Conventions -## Deployment +### Package manager & runtime -CI/CD: GitHub Actions → build → deploy to production server via Nix flakes. -- Gateway: `nixos-rebuild` or `systemctl restart gmw-discord-gateway` -- Backend: `nixos-rebuild` or `systemctl restart gmw-backend` -- Frontend: Next.js standalone, proxied by nginx :4009 +- **pnpm** (v11), not npm or bun. Lockfiles are committed. Node ≥ 22. +- ESM throughout (`"type": "module"` in all package.json files). -## Remember +### Import style -- Spec dulu, code belakangan. -- Todo list (`todo.md`) wajib untuk tugas yang berjalan — ceklis tiap selesai. -- Verified facts harus dari pembacaan kode aktual, bukan asumsi. -- Setiap change harus verifiable — tulis command di spec. -- One spec = one focused change. Don't mix unrelated features. \ No newline at end of file +Source files use `@/*` path aliases (mapped in tsconfig to `./src/*`). Relative imports **must include the `.js` extension** (e.g., `from "./embed.js"`). The `moduleResolution: "bundler"` tsconfig setting allows bare specifiers during dev, but `tsc` emits them as-is. A post-build script (`scripts/fix-imports.mjs`) rewrites both `@/` aliases and extensionless imports in `dist/` so Node ESM can resolve them at runtime. + +### Error handling + +Both gateway and backend define an `AppError` base class in `@/shared/errors/index` with subclasses: `ValidationError` (400), `NotFoundError` (404), `UnauthorizedError` (401), `DatabaseError` (500), `ConfigError` (500). Services throw these; callers or middleware map them to HTTP status codes. + +### Logging + +Use `createChildLogger('module-name')` from `@/shared/logger/index`. Never use raw `console`. It wraps pino; in development it pretty-prints via `pino-pretty`. + +### Config + +Environment variables are validated with Zod at startup in `shared/config/index.ts` of both gateway and backend. Do not read `process.env` directly outside config modules. + +### Module boundaries + +- Gateway: each feature lives in `src/modules//` with its own `index.ts` barrel. Modules register event listeners and are composed in `src/app/bootstrap.ts`. +- Backend: `modules//` follows schema → repository → service → controller → routes. Data flows up only. No cross-module repository imports. +- Frontend: `page.tsx` is a server component that fetches data via `src/lib/api/server.ts` (oRPC over HTTP, server-side only) and passes it to a `view.tsx` client component. Browser code uses `src/lib/orpc/client.ts` (oRPC over WebSocket via partysocket). Never import the server API client from a client component. + +### API layer + +The backend exposes an oRPC router mounted at `/trpc` (both HTTP and WebSocket). The frontend does **not** use a REST `/api/*` layer — all data goes through oRPC procedures. The server-side client (`src/lib/api/server.ts`) uses a fetch-based RPCLink; the browser client uses a WebSocket-based RPCLink backed by partysocket for auto-reconnection. Results are asserted to the frontend's local types at each call site (the backend's router type is not imported into the frontend). + +### Testing + +- **Gateway**: Vitest, tests in `tests/` at the service root. Config sets env vars (`DISCORD_TOKEN`, `DATABASE_URL`, etc.) so tests run without real services. +- **Backend**: Vitest, tests in `tests/` and `src/`. Includes an `e2e.test.ts` excluded from CI (needs a live backend). +- **Frontend**: No test runner configured. +- Tests are pure-function / unit-level. Mock external dependencies; do not start real DB/Redis in tests. + +### Formatting + +Biome, 2-space indent, spaces. Config at repo root `biome.json`. `lint` uses `--diagnostic-level=error`; `format` auto-writes. + +## Pitfalls + +1. **Never commit without running `fix-imports.mjs` after `tsc`** — gateway and backend builds will produce ESM that crashes at startup (`ERR_MODULE_NOT_FOUND`). +2. **Don't add `@discordjs/opus` build-from-source flags** — it ships prebuilt binaries for Node 22. Forcing source builds in CI/Nix will fail or add minutes of compile time. +3. **Frontend SSR fetches are always `cache: "no-store"`** — the server API client bypasses Next.js fetch cache. Do not add caching without understanding the live dashboard requirement. +4. **oRPC types are loosely coupled** — the frontend casts oRPC results to its own types with `as unknown`. Adding a field to the backend schema does not automatically update the frontend type. Update both sides. +5. **Gateway is a selfbot** (`discord.js-selfbot-v13`) — it uses a user token, not a bot token. It must not be deployed as a standard Discord bot.