# Project Instructions — Fiddy (External DB) ## 1) Core expectation This project connects to an **external Postgres instance (on-prem server)**. Dev and Prod must share the **same schema** through **migrations**. ## 2) Authority & doc order 1) **PROJECT_INSTRUCTIONS.md** (this file) is the source of truth. 2) **DEBUGGING_INSTRUCTIONS.md** (repo root) is required for bugfix work. 3) Other instruction files (e.g. `.github/copilot-instructions.md`) must not conflict with this doc. If anything conflicts, follow **this** doc. --- ## 3) Non-negotiables (hard rules) ### External DB + migrations - `DATABASE_URL` points to **on-prem Postgres** (**NOT** a container). - Dev/Prod share schema via migrations in: `packages/db/migrations`. - Active migration runbook: `docs/DB_MIGRATION_WORKFLOW.md` (active set + status commands). ### No background jobs - **No cron/worker jobs**. Any fix must work without background tasks. ### Security / logging - **Never log secrets** (passwords, tokens, session cookies). - **Never log receipt bytes**. - **Never log full invite codes** — logs/audit store **last4 only**. ### Server-side authorization only - **Server-side RBAC only.** Client checks are UX only and must not be trusted. --- ## 4) Non-regression contracts (do not break) ### Auth - Custom email/password auth. - Sessions are **DB-backed** and stored in table `sessions`. - Session cookies are **HttpOnly**. ### Receipts - Receipt images are stored in Postgres `bytea` table `receipts`. - **Entries list endpoints must never return receipt image bytes.** - Receipt bytes are fetched only via a **separate endpoint** when inspecting a single item. ### Request IDs + audit - API must generate a **`request_id`** and return it in responses. - Audit logs must include `request_id`. - Audit logs must never store full invite codes (store **last4 only**). --- ## 5) Architecture contract (Backend ↔ Client ↔ Hooks ↔ UI) ### No-assumptions rule (required) Before making structural changes, first scan the repo and identify: - where `app/`, `components/`, `features/`, `hooks/`, `lib/` live - existing API routes and helpers - patterns already in use Do not invent files/endpoints/conventions. If something is missing, add it **minimally** and **consistently**. ### Single mechanism rule (required) For any cross-component state propagation concern, keep **one** canonical mechanism only: - Context **OR** custom events **OR** cache invalidation Do not keep old and new mechanisms in parallel. Remove superseded utilities/imports/files in the same PR. ### Layering (hard boundaries) For every domain (auth, groups, entries, receipts, etc.) follow this flow: 1) **API Route Handlers** — `app/api/.../route.ts` - Thin: parse/validate input, call a server service, return JSON. - No direct DB queries in route files unless there is no existing server service. 2) **Server Services (DB + authorization)** — `lib/server/*` - Own all DB access and authorization helpers. - Server-only modules must include: `import "server-only";` - Prefer small domain modules: `lib/server/auth.ts`, `lib/server/groups.ts`, `lib/server/entries.ts`, `lib/server/receipts.ts`, `lib/server/session.ts`. 3) **Client API Wrappers** — `lib/client/*` - Typed fetch helpers only (no React state). - Centralize fetch + error normalization. - Always send credentials (cookies) and never trust client-side RBAC. 4) **Hooks (UI-facing API layer)** — `hooks/use-*.ts` - Hooks are the primary interface for components/pages to call APIs. - Components should not call `fetch()` directly unless there is a strong reason. ### API conventions - Prefer consistent JSON error shape: - `{ error: { code: string, message: string }, request_id?: string }` - Validate inputs at the route boundary (shape/type), authorize in server services. - Mirror existing REST style used in the project. ### Next.js route params checklist (required) For `app/api/**/[param]/route.ts`: - Treat `context.params` as **async** and `await` it before reading properties. - Example: `const { id } = await context.params;` ### Frontend structure preference - Prefer domain-first structure: `features//...` + `shared/...`. - Use `components/*` only for compatibility shims during migrations (remove them after imports are migrated). ### Maintainability thresholds (refactor triggers) - Component files > **400 lines** should be split into container/presentational parts. - Hook files > **150 lines** should extract helper functions/services. - Functions with more than **3 nested branches** should be extracted. --- ## 6) Decisions / constraints (Group Settings) - Add `GROUP_OWNER` role to group roles; migrate existing groups so the first admin becomes owner. - Join policy default is `NOT_ACCEPTING`. Policies: `NOT_ACCEPTING`, `AUTO_ACCEPT`, `APPROVAL_REQUIRED`. - Both owner and admins can approve join requests and manage invite links. - Invite links: - TTL limited to 1–7 days. - Settings are immutable after creation (policy, single-use, etc.). - Single-use does not override approval-required. - Expired links are retained and can be revived. - Single-use links are deleted after successful use. - Revive resets `used_at` and `revoked_at`, refreshes `expires_at`, and creates a new audit event. - No cron/worker jobs for now (auto ownership transfer and invite rotation are paused). - Group role icons must be consistent: 👑 owner, 🛡️ admin, 👤 member. --- ## 7) Do first (vertical slice) 1) DB migrate command + schema 2) Register/Login/Logout (custom sessions) 3) Protected dashboard page 4) Group create/join + group switcher (approval-based joins + optional join disable) 5) Entries CRUD (no receipt bytes in list) 6) Receipt upload/download endpoints 7) Settings + Reports --- ## 8) Definition of done - Works via `docker-compose.dev.yml` with external DB - Migrations applied via `npm run db:migrate` - Tests + lint pass - RBAC enforced server-side - No large files - No TypeScript warnings or lint errors in touched files - No new cron/worker dependencies unless explicitly approved - No orphaned utilities/hooks/contexts after refactors - No duplicate mechanisms for the same state flow - Text encoding remains clean in user-facing strings/docs --- ## 9) Desktop + mobile UX checklist (required) - Touch: long-press affordance for item-level actions when no visible button. - Mouse: hover affordance on interactive rows/cards. - Tap targets remain >= 40px on mobile. - Modal overlays must close on outside click/tap. - Use bubble notifications for main actions (create/update/delete/join). - Add Playwright UI tests for new UI features and critical flows. --- ## 10) Tests (required) - Add/update tests for API behavior changes (auth, groups, entries, receipts). - Include negative cases where applicable: - unauthorized - not-a-member - invalid input --- ## 11) Agent Response Legend (required) Use emoji/icons in agent progress and final responses so status is obvious at a glance. Legend: - `🔄` in progress - `✅` completed - `🧪` test/lint/verification result - `📄` documentation update - `🗄️` database or migration change - `🚀` deploy/release step - `⚠️` risk, blocker, or manual operator action needed - `❌` failed command or unsuccessful attempt - `ℹ️` informational context - `🧭` recommendation or next-step option Usage rules: - Include at least one status icon in each substantive agent response. - Use one icon per bullet/line; avoid icon spam. - Keep icon meaning consistent with this legend.