Files
GMW/.hermes/plans/2026-08-24_135602-gmw-fe-constellation-rombak.md
T
asepharyana 1accfd9390 perf(ai-moderation): naikkan cache hit dgn guard akurasi
- Fase-1 exact-cache lookup: N query serial -> SATU query ANY($1::text[])
- Global reuse utk bare key legacy, HANYA verdict non-actionable
  (clean/flagless/action=none, conf>=0.85, umur<=72h) — flagged/warn
  tetap context-scoped
- Semantic cache dua-band: clean band 0.92 default, actionable tetap
  0.97; di antara band -> LLM (fail-open ke akurasi)
- hit_count kini di-increment (bulk UPDATE per batch) -> hit-rate terukur
- Cache hasil wikipediaSearch di Redis (6h, hanya hasil non-kosong)
- Memoize fetchUrlSafely utk type=text (LRU 30m + in-flight dedupe)
- makeImageCacheKey strip query CDN Discord (?ex/is/hm, format/width)
  -> attachment sama = satu key vision, skip re-download+re-vision

Spec: .hermes/plans/2026-08-24-ai-analysis-cache-optimization.md
Tests: +33 (cacheGuards, discordImageKeyNormalize, cacheBatchLookup)
2026-08-24 18:51:23 +07:00

206 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GMW FE Rombak — "Constellation Ops" Implementation Plan
> **For Hermes:** Use subagent-driven-development skill to implement this plan task-by-task.
> Setiap task = edit → lint → build/test → smoke → commit (author `asepharyana`, tanpa Co-Authored-By).
**Goal:** Rombak total FE GMW dari template dashboard klasik menjadi **Constellation Node Graph** — graf node interaktif (three.js force layout) di mana channel/event/moderasi adalah node bintang dan navigasi adalah terbang antar node — untuk semua 9 route sekaligus.
**Architecture:** Hapus total shell template (TopBar + NavRail + MobileNav + main scroll container). Ganti dengan satu `ConstellationStage`: canvas full-bleed fixed di belakang seluruh app yang me-render force-directed graph per-route (scene), plus overlay HTML mengambang (tanpa card box standar) untuk konten detail. Router tetap Next App Router; perpindahan route = kamera "fly-to" ke scene baru. Data tetap lewat pola SSR seed (`page.tsx` server fetch → `view.tsx` client + SWR fallbackData) — tidak ada endpoint baru yang diarankan.
**Tech Stack:** Next.js 16 App Router · React 19 · TypeScript strict · Tailwind v4 (token oklch existing) · three r180 (+ `@types/three`) · d3-force (+ `@types/d3-force`) untuk simulasi layout deterministik · motion v12 (transisi antar-scene) · biome (0 error **dan** 0 warning) · bun:test untuk modul murni.
---
## Keputusan Terkunci (2026-08-24, user MythEclipse)
| Keputusan | Pilihan user |
|---|---|
| Cakupan | **Semua 9 route sekali jalan** (dashboard, voice, media, recordings, moderation, messages, analysis, channels, glossary) |
| Arah struktural | **Constellation Node Graph** — navigasi = terbang antar node |
| Tema | **Pertahankan toggle light/dark** (jangan rebuild sistem tema — harden saja) |
## Kondisi Saat Ini (fakta repo, 2026-08-24)
- Repo: `/home/code/GMW/services/frontend` (monorepo GMW, deploy Nix-first via GHA "Build & Deploy (Nix)" ke `imphnen.asepharyana.my.id`; nginx gmw-proxy :4009 → Next standalone :4017).
- Shell aktif: `src/components/shell/ambient-app.tsx``AppFrame` = NavRail + TopBar + `<main>` scroll + MobileNav + MiniPlayer, dipakai SEMUA route via `(dashboard)/layout.tsx`. AmbientCanvas (233 l) masih ada sebagai background di belakang chrome template.
- 9 route di `src/app/(dashboard)/`: dashboard (368 l), voice (311), media (293), moderation (424), messages (611), analysis (196), recordings (201), channels (23), glossary (15).
- `next.config.ts`: `output: "standalone"` + `trailingSlash: true`**nav via `router.push`/Link bisa no-op; pakai `<a href>` polos** (pitfall terverifikasi).
- Token desain ada di `globals.css` `@theme`: `--color-canvas/-2`, `--color-ink/-soft/-faint`, `--color-signal(+glow)`, amber, vermilion, hairline; font Bricolage Grotesque/JetBrains Mono; `.light` class override + next-themes sudah jalan — JANGAN dibangun ulang.
- Sudah ada & dipertahankan: CommandPalette (⌘K), Chatbot, MiniPlayer, primitives (GlassPanel/GlassCard/Badge/…), shared states+skeletons, `lib/format.ts`, `lib/ai-status.ts`, WS typed events (`message_created/updated/deleted/analyzed/snapshot*`, `voice_state`, dll).
- Tooling lokal: `pnpm lint|build|format`, `pnpm exec biome check --write --unsafe .` buat import order; bun 1.3.14 tersedia untuk unit test modul murni.
- Smoke test SELALU di port non-prod (mis. 4024), cek `ss -ltnp | grep <port>` dulu; JANGAN pernah bind 4017.
## Struktur Target
```
src/components/shell/
constellation-frame.tsx # pengganti AppFrame: tanpa topbar/rail/bottom-bar
constellation-stage.tsx # canvas three.js full-bleed + hit-test + kamera
route-scenes.ts # map pathname → scene config (nodes/edges/focus)
floating-chrome.tsx # brand mark + status dot + route switcher minim + palette hint
(hapus setelah migrasi): topbar.tsx, nav-rail.tsx, mobile-nav.tsx, ambient-app.tsx*
src/lib/constellation/
graph.ts # tipe GraphNode/GraphEdge + builder dari data API (murni)
layout.ts # wrapper d3-force deterministik (seeded) + fallback radial (murni)
camera.ts # interpolasi kamera fly-to (murni, testable)
*.test.ts # bun:test utk modul murni di atas
```
*ambient-canvas/context dievaluasi: bisa di-refactor jadi bagian dari stage atau dihapus.
### Scene per route (bahasa visual satu kesatuan)
| Route | Scene |
|---|---|
| `/dashboard/` | Guild = bintang pusat; channel = node orbit; event WS = pulse edge; metric cluster floating (tanpa box) |
| `/channels/` | Semua channel sebagai konstelasi; klik channel = fly-to + panel detail mengambang |
| `/moderation/` | Pesan flagged = node merah (vermilion) mengelilingi hub AI-verdict; LiveModerationFeed jadi ribbon ticker |
| `/messages/` | Stream pesan = sabuk orbital; node baru masuk lewat animasi dari tepi |
| `/voice/` | Stage node di tengah; speaker aktif = node mengorbit dengan glow intensitas |
| `/media/` | Galeri spiral node (thumbnail via div bg-image); klik = MiniPlayer handoff |
| `/recordings/` | Variasi spiral media, timeline density sebagai cincin |
| `/analysis/` | Hub analitik; chart existing dirender sebagai panel translusen mengambang ter-anchor ke node |
| `/glossary/` | Term KB = cincin satelit; search memfilter node real-time |
### Checklist anti-default (wajib lolos sebelum commit akhir)
- [ ] Tidak ada top bar / nav rail / side panel / bottom prompt bar
- [ ] Tidak ada card grid standar sebagai struktur utama
- [ ] Layout beda SPASIAL dari versi lama (bukan ganti warna)
- [ ] Verifikasi visual sungguhan: browser_navigate + browser_vision → "bukan template dashboard"
---
## Tasks
### Phase 0 — Fondasi
#### Task 1: Dependencies + scaffold direktori
**Files:** `package.json`, create `src/lib/constellation/`
1. `cd /home/code/GMW/services/frontend && pnpm add three @types/three d3-force @types/d3-force && pnpm add -d @types/bun`
2. Buat 4 file kosong bertipe sesuai struktur target (ekspor placeholder bertipe agar tsc lolos).
3. Run: `pnpm exec tsc --noEmit` → expected PASS.
4. Commit: `chore(frontend): scaffold constellation lib + deps`.
> Catatan: jika `@types/bun` bentrok dengan tsconfig Next (duplikat globals), fallback: exclude `"**/*.test.ts"` dari `tsconfig.json` dan jalankan test hanya via `bun test` (tanpa typecheck). Putuskan saat eksekusi, dokumentasikan di commit body.
#### Task 2 (TDD): Modul murni graph + layout + camera
**Files:** `src/lib/constellation/{graph,layout,camera}.ts` + `*.test.ts`
1. **Tulis test dulu** (bun:test):
- `graph.test.ts`: builder `dashboardToGraph()` menghasilkan node guild pusat + N channel + edges benar dari fixture `/api/dashboard/stats|channels` shape (lihat AGENTS.md; nama channel di `message.metadata.channelName`).
- `layout.test.ts`: `computeLayout(nodes, edges, {seed:42})` DETERMINISTIK (dua pemanggilan = posisi identik), semua posisi finite, radius dalam bound; `radialLayout()` fallback untuk reduced-motion.
- `camera.test.ts`: `flyTo(from,to,t)` easing monotonic, t=0→from, t=1→to.
2. Run: `bun test src/lib/constellation` → expected FAIL (belum diimplementasi).
3. Implementasi minimal sampai PASS.
4. Run: `bun test src/lib/constellation && pnpm exec tsc --noEmit` → PASS.
5. Commit: `feat(frontend): constellation graph/layout/camera pure modules (tested)`.
### Phase 1 — Shell Baru
#### Task 3: `ConstellationStage` (canvas renderer)
**Files:** `src/components/shell/constellation-stage.tsx`
- `<canvas>` fixed inset-0 `-z-10`; three.js orthographic 2D-ish render node+edge (sprite/glow shader sederhana — jangan over-engineer; titik + garis + glow cukup).
- Interaksi: drag = pan, wheel/pinch = zoom, hover = highlight node + tooltip title, click node ber-`href` = navigasi via `<a>` semantics (window.location assign — hindari router.push no-op).
- DPR-aware resize; pause render loop saat `document.hidden`; `prefers-reduced-motion` → render statis tanpa animasi.
- Sampling warna dari CSS var (`getComputedStyle`) + `MutationObserver` pada `documentElement.classList` agar ikut toggle dark/light.
- Verify: `pnpm lint && pnpm build` PASS → commit `feat(frontend): constellation stage canvas renderer`.
#### Task 4: `route-scenes.ts` + `ConstellationFrame`
**Files:** `src/components/shell/{route-scenes,constellation-frame,floating-chrome}.tsx`
- `route-scenes.ts`: `usePathname()` → config scene (builder mana, focus node, overlay slots).
- `ConstellationFrame`: render `ConstellationStage` + `children` (overlay HTML absolute, BUKAN flex column bertingkat) + `floating-chrome` (brand mark kiri-atas dgn status dot WS, switcher route minimal kanan-bawah, hint ⌘K). MiniPlayer + Chatbot + CommandPalette tetap termounting.
- Switcher & semua nav internal pakai `<a href="/<route>/">` polos (trailingSlash!). Verifikasi klik di browser nanti, bukan cuma baca kode.
- Wire `(dashboard)/layout.tsx`: ganti `AppFrame``ConstellationFrame` (provider Ambient/WS tetap).
- Smoke: `ss -ltnp | grep 4024` kosong → `PORT=4024 pnpm start` → curl semua 9 route = 200 → kill server.
- Commit: `feat(frontend): replace classic shell with constellation frame`.
#### Task 5: Hapus chrome lama + bersih-bersih
**Files:** hapus `shell/topbar.tsx`, `shell/nav-rail.tsx`, `shell/mobile-nav.tsx`, update `shell/index.ts`; eval `ambient-*` (refactor jadi layer stage ATAU hapus).
- `grep -rn "TopBar\|NavRail\|MobileNav\|AppFrame" src/` → 0 referensi tersisa.
- `pnpm exec biome check --write --unsafe .` → import order rapi.
- Commit: `refactor(frontend): remove classic dashboard chrome`.
### Phase 2 — Migrasi 9 Route (batch, tiap batch ship hijau)
Urutan sengaja dari yang paling graph-native ke paling padat konten:
#### Task 6: `/dashboard/` + `/channels/`
- Dashboard view: guild-star + channel orbit + WS pulse; metric cluster floating (reuse MetricTile tapi tanpa card-box — ubah jadi teks besar + hairline).
- Channels: konstelasi channel, klik = fly-to + panel detail (data dari hook `use-channels`/server fetch existing — jangan karang endpoint).
- Lint+build+smoke 2 route → commit `feat(frontend): dashboard & channels constellation scenes`.
#### Task 7: `/glossary/` + `/analysis/`
- Glossary: cincin satelit term + search filter node (client-side filter pada node label).
- Analysis: hub node + panel chart translusen mengambang (reuse komponen charts/* existing, anchor posisi ke node).
- Commit: `feat(frontend): glossary & analysis scenes`.
#### Task 8: `/messages/` + `/moderation/`
- Messages: sabuk orbital stream (reuse sortMessages/logika WS `use-messages.ts` — jangan duplikasi); pesan baru animate-in dari tepi.
- Moderation: flagged nodes vermilion + hub verdict; LiveModerationFeed jadi ticker ribbon bawah (bukan panel).
- Commit: `feat(frontend): messages & moderation scenes`.
#### Task 9: `/voice/` + `/media/` + `/recordings/`
- Voice: speaker aktif orbit dgn glow (seed dari snapshot `activeSpeakers` server-authoritative — pertahankan perilaku useSpeakers).
- Media/Recordings: spiral galeri; thumbnail pakai div background-image (hindari `noImgElement`; kalau terpaksa `<img>``// biome-ignore lint/performance/noImgElement: <alasan>`); MiniPlayer handoff tidak boleh regress.
- Commit: `feat(frontend): voice, media & recordings scenes`.
### Phase 3 — Polish, A11y, Tema
#### Task 10: Motion & micro-interaction
- Fly-to antar scene pakai `motion` v12 (interpolasi kamera dari `lib/constellation/camera.ts`).
- Overlay: stagger masuk (pattern `animate-stagger` + `staggerDelay(i)` existing); tambah semua `animate-*` baru ke kill-list `prefers-reduced-motion` di `globals.css`.
- Commit: `feat(frontend): scene transitions + motion polish`.
#### Task 11: A11y & fallback semantik
- Setiap scene punya fallback semantik: `<nav aria-label="Routes">` visually-hidden berisi link semua route + `<ul>` node (label + href) yang ter-sync dgn graph state.
- Keyboard: Tab ke daftar route; panah memindahkan fokus antar node; Enter aktivasi; `focus-visible` ring pakai `--color-ring`.
- Kontras ink-on-canvas dicek (WCAG AA utk teks overlay).
- Hooks-after-early-return pitfall: compute flags dulu, guard di dalam useEffect.
- Commit: `feat(frontend): a11y semantic fallback for constellation`.
#### Task 12: Hardening tema light/dark
- Pastikan canvas sampling var `.light` benar saat toggle (MutationObserver dari Task 3); harden `.light`: `color-scheme: light`, scrollbar/selection, glow opacity turun.
- JANGAN menyentuh sistem next-themes.
- Commit: `fix(frontend): light theme parity for constellation stage`.
### Phase 4 — Gerbang Ship (non-negotiable)
#### Task 13: Verifikasi penuh + deploy
1. `pnpm lint`**Found 0 errors. Found 0 warnings** (warning pun harus bersih).
2. `bun test src/lib/constellation` → all PASS; `pnpm exec tsc --noEmit` PASS; `pnpm build` sukses.
3. Smoke: port 4024 bersih → start → curl 9 route (dgn trailing slash) semua **200** → kill server.
4. `git status` audit: hanya file scope yang ter-commit (`git checkout --` file liar hasil auto-format).
5. Push `origin/main``gh run watch` "Build & Deploy (Nix)" → hijau.
6. Live: curl `https://imphnen.asepharyana.my.id/<route>/` × 9 → 200.
7. **Verifikasi visual sungguhan**: browser_navigate ke live URL → browser_vision: "Apakah ini masih template dashboard?" — jawaban HARUS bukan; screenshot dikirim ke user (`MEDIA:<path>`).
8. Checklist anti-default dicentang satu per satu di PR/commit message akhir.
---
## Files Likely to Change
```
package.json, pnpm-lock.yaml
next.config.ts # hanya jika perlu (tidak diharapkan)
src/app/(dashboard)/layout.tsx # wire frame baru
src/app/(dashboard)/{9 route}/page.tsx|view.tsx # scene migration
src/components/shell/** # frame baru + hapus lama
src/lib/constellation/** # baru (murni + test)
src/app/globals.css # tokens tambahan + reduced-motion kill list
biome.json # hanya jika rule intentional baru perlu "off"
```
## Risks / Tradeoffs
| Risiko | Mitigasi |
|---|---|
| Perf GPU di device lemah (full-page WebGL) | Node count kecil (<200/route), pause saat tab hidden, fallback radial statis utk reduced-motion; jika tetap berat → downgrade ke Canvas2D renderer dgn API sama |
| router.push no-op (standalone+trailingSlash) | Semua nav pakai `<a href>`; verifikasi klik nyata di browser |
| Konten padat (messages 611 l) tak muat di metafora graf | Prinsip: graf = struktur/navigasi; DETAIL tetap lewat overlay panel mengambang — konten tidak dikorbankan, hanya wadahnya yang berubah |
| Bun test + Next tsconfig konflik types | Exclude `*.test.ts` dari tsconfig (keputusan Task 1) |
| Regress fitur existing (theme toggle, palette, chatbot, mini-player, WS sync) | Dipertahankan apa adanya; smoke + klik verifikasi per phase; sortMessages & snapshot voice TIDAK disentuh logikanya |
## Open Questions
- Tidak ada blocker. Satu preferensi opsional: apakah brand mark perlu jam/uptime seperti TopBar lama — default: tidak (minimal). Keputusan bisa diambil saat review Task 4.