# 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). ### Docker dev runtime - After backend/API code changes while using `docker-compose.dev.yml`, rebuild and restart only the backend service: - `docker compose -f docker-compose.dev.yml up -d --build backend` - After backend env/CORS changes, recreate the backend service so `backend/.env` is reloaded: - `docker compose -f docker-compose.dev.yml up -d --force-recreate --no-deps backend` - For the Docker frontend on port `3010`, `ALLOWED_ORIGINS` must include the exact browser origin, for example `http://localhost:3010` and `http://127.0.0.1:3010`. - Verify the restarted API with `GET http://127.0.0.1:5000/` and `GET http://127.0.0.1:5000/config`. - Do not print or commit real `.env` values while checking or updating local Docker env. ### 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. - Avoid redundant nearby labels. If a tab, section title, count chip, row label, or control state already communicates the meaning, do not repeat it with an eyebrow label, explanatory zero-state sentence, or duplicate card label. - Prefer compact label/value rows for dense settings controls instead of stacked labels with large vertical gaps. - 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). - Frontend destructive actions should use the shared `ConfirmSlideModal` pattern instead of browser `confirm()` unless there is a documented exception. - 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) Git Intake, Branching, Commit, and PR Discipline (required) ### Read-only intake before editing Before editing, run this read-only intake: - `git status --short --branch` - `git branch -vv` - `git log --oneline --decorate -8` - `git ls-files --others --exclude-standard` - Check current PR status when GitHub CLI is available. - Check open PRs for overlapping work before editing shared or collision-prone areas. ### Branch suitability gate before editing - Continue on the current branch only when the requested work belongs to that branch or PR. - Start independent work from `main` after pulling latest. - Start stacked work from the current PR branch when the work intentionally builds on that PR. - If the current branch purpose does not match the request, stop before editing and switch or create the correct branch. - If either `main` or the current branch could be valid, ask whether the work is independent side work or required follow-on work. - Do not layer unrelated work on top of a dirty worktree. - If unrelated local changes exist, pause and ask how to separate them. ### Branch creation and naming - Create a descriptive branch before writing code. - Preferred branch prefixes: - `feature/` - `bugfix/` - `refactor/` - `chore/` - `spike/` - Do not include tracker numbers in branch names. - Use standalone branches from `main` for independent work. - Use stacked branches from the parent PR branch for follow-on work. - Target standalone PRs at `main`. - Target stacked PRs at the parent PR branch. - Never push directly to `main`. ### Commit discipline - Treat committing as a first-class part of the workflow: create frequent, verified checkpoint commits for completed work instead of accumulating large uncommitted changes. - Commit after each coherent logical unit of work. - Commit in small, logical slices (no broad mixed-purpose commits). - Before committing: 1. Run `git diff --stat`. 2. Run relevant tests or checks when practical. 3. Stage only files that belong to the logical unit. 4. Run `git diff --cached --stat`. 5. Commit with an imperative, present-tense subject at or below 72 characters. - 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 - Do not stage unrelated user or collaborator changes. - Do not start a second unrelated task while the first has uncommitted work. - If existing local changes are external/user changes, leave them untouched unless explicitly told otherwise. - If asked to commit and external changes already exist, commit those separately on the proper branch before starting new work. - 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. - Before switching tasks or stopping after a completed change, check git status and either commit the finished slice or clearly document why it remains uncommitted. - If a rule or contract changes, commit docs first (or in the same atomic slice as enforcing code). ### Push and PR coordination - Push the branch before opening a PR. - For this Gitea repo, use `docs/GITEA_PR_WORKFLOW.md` and `scripts/gitea-pr.js` for PR creation, lookup, and merge operations. - PR tooling must read auth from `GITEA_TOKEN`/`GITEA_BASE_URL` shell environment or ignored `.codex-local.env` only; never commit tokens or print token values. - Open a draft PR early for non-trivial, collision-prone, or multi-agent work once the first coherent commit exists. - Use the PR body as the coordination record: - `Owner:` - `Status: proposed / in-progress / blocked / review / done` - `Branch:` - `Branch relationship: standalone from main / stacked on parent branch / continuing existing branch` - `Likely modified areas:` - `Actual modified files:` - `Collision risk: low / medium / high` - `Last meaningful update:` - Collision risk levels: - Low: isolated docs, tests, or one leaf component. - Medium: shared stores/types, panels/components, handlers, helpers. - High: interface contracts, broad app flows, core registries, cross-cutting behavior. - If a branch already contains assigned feature work and has no current PR, stop before adding more feature commits. Push the branch and open a draft PR, or record the GitHub/auth blocker. - Before writing or updating the final PR body, inspect: - `git log ..HEAD` - `git diff --stat ..HEAD` - The PR should describe the cumulative branch diff against the target branch, not only the latest commit. - Include: - Summary of functional changes. - Tests run, or a clear reason tests were not run. - For broad branches, organize the summary by subsystem, workflow, or behavior area. - Do not use auto-closing keywords such as `Closes`, `Fixes`, or `Resolves`. - Merge PRs only after explicit operator approval, required checks, and a final `npm run pr:view -- --number ` status check.