# 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/<domain>/...` + `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.
- For every frontend action that manipulates database state, show a toast/bubble notification with basic outcome details (action + target + success/failure).
- Progress-type notifications must reuse the existing upload toaster pattern (`UploadQueueContext` + `UploadToaster`) for consistency.
- 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.

---

## 12) Commit Discipline (required)
- Commit in small, logical slices (no broad mixed-purpose commits).
- Each commit must:
  - follow Conventional Commits style (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`)
  - include only related files for that slice
  - exclude secrets, credentials, and generated noise
- Run verification before commit when applicable (lint/tests/build or targeted checks for touched areas).
- Prefer frequent checkpoint commits during agentic work rather than one large end-state commit.
- If a rule or contract changes, commit docs first (or in the same atomic slice as enforcing code).
