SPB Git
2.3 KB

# CLAUDE.md — tools.spboucher.ai

Curated encyclopedia of tools (public) + owner inventory (private). Stack: Next.js 16 App Router, React 19, Tailwind 4, PostgreSQL 17, Drizzle (snake_case casing), postgres-js, Vitest, Playwright. Port 8540.

# Non-negotiables

  • Public ≠ private at the DB layer. Public code uses sqlPublic (role tools_reader, SELECT on public_tools view only). Never import sql/db from src/db into anything under src/lib/public or public routes. Owner tables (owner_notes, projects) are never joined into public queries.
  • Every admin mutation goes through requireUser(req) (session + MFA + same-origin + x-csrf-token) and writes audit_log (+ tool_revisions for records). Never add client-only protection.
  • Evidence rules (docs/editorial-guide.md, docs/source-policy.md): no prices, no certifications without a policy source, no benchmark claims, no copied vendor text, checked_at only for pages actually loaded. Run pnpm content:validate after editing content/.
  • Outbound HTTP only via safeFetch (SSRF guard). Fetched text is untrusted; AI output must be zod-validated and never auto-saved.
  • Secrets only in .env on the node (600) and ~/Desktop/Cluster/secrets/tools-spboucher-ai/. Never in git, manifests, logs or chat.
  • Migrations: add a new drizzle/NNNN_*.sql; never edit applied files (checksums). Mirror changes in src/db/schema.ts. Recreating the public_tools view requires re-granting to tools_reader.
  • Drizzle hijacks postgres-js serializers → the raw sql clients use their own pools (see src/db/index.ts). Use sql.json(x) for jsonb params.

# Workflow

pnpm typecheck && pnpm test && pnpm content:validate before committing. E2E/a11y/security scripts need a running server (pnpm dev or production). Remote-first after deploy: edit on M2M32c:~/apps/tools-spboucher-ai, pnpm build && pm2 restart tools-web tools-worker, commit/push there (docs/deployment.md).

# Content authoring

One JSON per tool in content/tools/<slug>.json matching src/lib/content/schema.ts; deep records 100–300 words, ≥3 sources, ≥2 alternatives, comparison notes. Categories are leaf slugs from content/categories.json; alternatives are existing slugs. Private records → content/private/ with visibility: private. Owner relevance → content/owner/relevance.json.

Files · f05ed656dfb02e05e9363ab635689d5774c375f6
Menu

Theme