# CLAUDE.md Guidance for Claude Code (claude.ai/code) working in this repo. ## What this is Manga read-progress tracker, user read on **asurascans.com** (current domain; asuracomic.net 301s here) and **demonicscans.org** via **Violentmonkey**. Userscript inject on-page UI (floating button + slide-in panel), sync progress to self-hosted Go backend so bookmarks unify across both sites and devices. ## Hard constraints (drive design — don't violate) Userscript targets **Violentmonkey**, so `GM_*` APIs available, but stay GM-free where plain web APIs suffice — keeps portability across engines: - **Avoid `GM_*` unless needed.** Prefer page `localStorage` over `GM_setValue`/`GM_getValue`, on-page UI over `GM_registerMenuCommand`, plain `fetch()` over `GM_xmlhttpRequest` for cross-origin. - Cross-origin `fetch()` work **only** against CORS-enabled backend. Manga sites `https://`, so backend **must be HTTPS** (else mixed-content block). - Asura and Demonic are **separate origins with separate `localStorage`** — shared remote store only way to unify bookmarks. Cloud sync required, not optional. - Userscript run in **isolated world**, so embedded API token safe from site's JS. - Cloudflare's block on manga sites **IP-reputation-based, not universal — and not reliably reproducible.** Verified 2026-07-26: plain `curl` from both CGNAT dev machine *and* deployed VPS got clean 200s with real HTML on both asurascans.com and demonicscans.org (homepage, series, chapter pages) — no interactive Turnstile challenge from either IP at test time. Contradicts earlier untested assumption CGNAT dev IP blocked; wasn't, at least this date. Treat "does curl work right now" as live, time-varying fact to re-check, not fixed property of machine — Cloudflare's bot scoring can flip previously-clean IP without notice. Backend fetcher still needs graceful-degrade path for when challenged, and adapters should be **verified against live pages** (Playwright MCP, on-device devtools, or direct probe) before finalize, not assumed from single earlier test. ## Architecture ``` Violentmonkey userscript (isolated world, per-site adapters, localStorage cache) -- fetch() HTTPS --> reverse proxy (TLS + CORS) --> Go net/http --> SQLite (volume) ``` Backend-specific architecture (packages, endpoints, poller, config env vars) lives in `backend/CLAUDE.md`. Userscript-specific structure (adapters, retry queue, UI, live URL shapes) lives in `userscript/CLAUDE.md`. ## Commands Backend (`cd backend`): - Test all: `go test ./...` - Single test: `go test -run TestName ./...` - Build static binary: `CGO_ENABLED=0 go build` Local stack: `docker compose up` (named volume mounted at `/data`, `restart: unless-stopped`). Smoke test: `curl` endpoints with `Authorization: Bearer `; confirm `OPTIONS` preflight return CORS headers and `/healthz` return 200. ## Forge: Gitea, not GitHub `origin` is self-hosted Gitea instance (`gitea.violetcrown.my.id`), so **`gh` don't work here — use `tea` (Gitea CLI) for anything past plain git.** Common ones: - Open PR: `tea pr create --head --base main --title "..." --description "..."` - List / view / check out: `tea pr list`, `tea pr `, `tea pr checkout ` - Issues: `tea issue create`, `tea issue list` - Auth lives in `tea login`, not `GH_TOKEN` env var. `tea` print output as rendered boxes rather than plain text; PR URL lands on last line. ## Design system Web UI + userscript panel follow **Cinder**, rules in `docs/design-system.md` — source of truth Claude Design project `mangaBookmark Web UI` (`969ac210-fe02-4c01-ae1b-9a271dcc779a`). Read it before touching `backend/internal/web/static/style.css`, `backend/internal/web/templates/*`, or userscript `TEMPLATE`/`CSS`. Core law: **ember means new chapter only** — no other state (busy, error, destruction) may use `--ember`; destruction gets `--danger`. No cards/corners/shadows, one `--measure: 760px` column, tokens only (never hardcode hex outside `:root`), both colour branches touched together. Any move that pulls series out of list (archive/finish/remove) must be confirm-gated via its own `.confirm-row`; only restore fires instantly. ## Security invariants - Auth on `/bookmarks*`: require `Authorization: Bearer `, **constant-time compare**, 401 otherwise. - CORS: reflect `Origin` only when in `ALLOWED_ORIGINS`; allow `GET,PUT,DELETE,OPTIONS` + headers `Authorization,Content-Type`; answer preflight `OPTIONS` with `204`. ## Comments Comment only if code alone can't carry info. Cost per read — must earn spot. Write for: - Why not what. Tradeoffs, non-obvious decisions. - Load-bearing detail looking incidental — say so if "simplify" breaks it. - Non-local consequence, invisible from function alone. - Wire format / encoding / interface contract — save callers re-deriving. - Gotcha/workaround, with ref if exists. - Domain/business rule not derivable from code. Skip: - Restating code (no `// increment i` above `i++`). - Trivial getter/setter/pass-through. - Banners, dividers, `// helpers`. - Change narration (`// fix bug`, `// as requested`, `// new impl`) — git's job. - Commented-out code — delete. - TODO without concrete action. Style: one dense comment over function beats one per line inside. Tight, no worked example unless bug subtle. Wrong comment worse than none — update/delete on change. Default fewer — sparse+high-signal beats comprehensive. Test: "competent reader get this from code in few sec?" Yes → skip. Needs detour through another file/spec/git-blame → write it. ## Relevant skills `multi-stage-dockerfile` and `docker-compose-orchestration` for container work (referenced in plan). `golang-code-style`, `golang-error-handling`, `golang-performance`, `golang-testing` for backend Go work. ## graphify Project has knowledge graph at graphify-out/ with god nodes, community structure, cross-file relationships. Rules: - For codebase questions, first run `graphify query ""` when graphify-out/graph.json exists. Use `graphify path "" ""` for relationships and `graphify explain ""` for focused concepts. Return scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output. - If graphify-out/wiki/index.md exists, use for broad navigation instead of raw source browsing. - Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain don't surface enough context. - After modifying code, run `graphify update .` to keep graph current (AST-only, no API cost).