# STTracker Self-hosted meta media tracker: the user's own permanent record of books, games, film/TV, manga, podcasts and music, pulled from Hardcover, Trakt, MangaBaka, ListenBrainz, Last.fm and more. Bun 1.3, TypeScript 5, React, Kysely over SQLite (`bun:sqlite`) or Postgres (`Bun.sql`). Bun workspaces. `AGENTS.md` is the single source of truth for agents here. `CLAUDE.md` and `GEMINI.md` only point here. Deeper context on demand: `docs/ARCHITECTURE.md`, `docs/DECISIONS.md`, `docs/TODO.md`. Product spec: `docs/specs/001-sttracker-v1-design.md`. Read it before any feature work. ## Commands Install: `bun install --frozen-lockfile` Test: `bun test` | Single test: TODO(maintainer): confirm `bun test ` once tests exist Typecheck: TODO(maintainer): confirm `bun run typecheck` (fails until source files exist) Lint: TODO(maintainer): confirm once a linter is chosen Dev / Build: TODO(maintainer): confirm once `apps/server` exists ## Workflow Required skills: `superpowers`, `impeccable`. Check at session start; install if missing. Specs → `docs/specs/NNN-slug.md` (permanent). Plans → `docs/plans/NNN-slug.md` while in progress; `git rm` them before opening a merge request. The spec is the record. (Overrides the superpowers default paths.) Anything beyond a trivial fix: brainstorm → spec (sign-off) → plan (sign-off) → TDD → review. TDD means failing test first, watch it fail, minimal code, watch it pass, commit. Tick plan checkboxes as you go so an interrupted session is resumable. Trivial changes (typo, version bump, one-line config): say "trivial, skipping spec" and proceed. Use impeccable for user-facing UI: `craft` for new UI, `audit`/`polish` on existing UI, `harden` before shipping. Design context: `PRODUCT.md`, `DESIGN.md`, `docs/design/mockups/`. ## Rules - Bun-native only: - `Bun.serve()` with HTML imports for the web app. No Express, no Vite. - `bun:sqlite` and `Bun.sql`. No `better-sqlite3`, `pg` or `postgres.js`. - `bun test`. No Jest or Vitest. - `Bun.password`, `Bun.file`. - Bun loads `.env` itself. No dotenv. - Domain code queries through Kysely from `packages/db`. Dialect-specific SQL lives only in `packages/db`. Every DB-touching test must pass on both SQLite and Postgres. - Syncs never overwrite or delete user data directly. Source records go through the change engine. It applies a value only if `field_sources` says the user never edited the field; anything else becomes a Changes-inbox event. Deletes are soft (`deleted_at`). - Plugins (`plugins/*`) import only `@sttracker/plugin-sdk`. They never import `packages/db` or core internals, and they return `SourceRecord`s instead of writing. - `packages/*` and `plugins/*` never import from `apps/*`. - IDs are ULIDs generated in app code. Ratings are integers 0–100. Dates carry a precision (`day | month | year | unknown`). - Tests never call live APIs. Plugin tests use recorded fixtures through the SDK test harness. - Write-back (`push`) is opt-in per connection and off by default. No plugin pushes in Phase 1. ## Architecture apps/server -> Bun.serve: REST API `/api/v1`, `/ingest/`, job worker, serves the web app apps/web -> React app via HTML imports packages/core -> domain services: entries, matching, change engine, history, export packages/db -> Kysely setup, SQLite/Postgres dialects, migrations packages/plugin-sdk -> plugin contract, SourceRecord types, http/rate-limit helpers, test harness plugins/ -> one source integration each Full picture: `docs/ARCHITECTURE.md`. Read it before your first change in a session. ## Boundaries Never touch: `docs/design/mockups/` (reference only), `node_modules/`, `data/`. Never commit `.env`, secrets, API keys, or recorded fixtures containing real tokens. Never force-push, rewrite pushed history, or commit directly to `main`. Ask first: new runtime dependencies · public API, export schema (`sttracker-export/v1`) or migration changes after release · auth, token and credential-encryption code · anything destructive (`rm -rf`, `git reset --hard`, DB resets) · work the approved plan does not cover. If the task turns out much larger than the plan assumed: stop, report, re-plan. ## Docs duties Same commit as the code, not "later": - `docs/ARCHITECTURE.md`: when a module, boundary, or data flow changes. - `docs/DECISIONS.md`: append-only; any choice a future maintainer would reverse-engineer. - `docs/TODO.md`: anything noticed and deliberately not fixed. No `TODO` comments in source. - `docs/PERMISSIONS.md`: together with `.claude/settings.json`, `.codex/` and `.gemini/settings.json`, in one commit. ## Git Branches: `feat|fix|chore/`. Commits: Conventional Commits. Keep them small, each leaving tests green. ## Done means verified Do not report success on work you have not run. Before saying done, all of these must hold: - tests run *in this session* and seen passing, on both databases for DB code; - typecheck clean; - matches the approved spec; - docs duties complete; - `git status` clean and the diff is only what you intended. "It should work" is not verification.