14 KiB
14 KiB
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
- PROJECT_INSTRUCTIONS.md (this file) is the source of truth.
- DEBUGGING_INSTRUCTIONS.md (repo root) is required for bugfix work.
- 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_URLpoints 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/.envis reloaded:docker compose -f docker-compose.dev.yml up -d --force-recreate --no-deps backend
- For the Docker frontend on port
3010,ALLOWED_ORIGINSmust include the exact browser origin, for examplehttp://localhost:3010andhttp://127.0.0.1:3010. - Verify the restarted API with
GET http://127.0.0.1:5000/andGET http://127.0.0.1:5000/config. - Do not print or commit real
.envvalues 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
byteatablereceipts. - 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_idand 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:
- 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.
- 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.
- 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.
- 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.paramsas async andawaitit before reading properties.- Example:
const { id } = await context.params;
- Example:
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_OWNERrole 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_atandrevoked_at, refreshesexpires_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)
- DB migrate command + schema
- Register/Login/Logout (custom sessions)
- Protected dashboard page
- Group create/join + group switcher (approval-based joins + optional join disable)
- Entries CRUD (no receipt bytes in list)
- Receipt upload/download endpoints
- Settings + Reports
8) Definition of done
- Works via
docker-compose.dev.ymlwith 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).
- Frontend destructive actions should use the shared
ConfirmSlideModalpattern instead of browserconfirm()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 --branchgit branch -vvgit log --oneline --decorate -8git 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
mainafter 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
mainor 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/<short-description>bugfix/<short-description>refactor/<short-description>chore/<short-description>spike/<short-description>
- Do not include tracker numbers in branch names.
- Use standalone branches from
mainfor 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:
- Run
git diff --stat. - Run relevant tests or checks when practical.
- Stage only files that belong to the logical unit.
- Run
git diff --cached --stat. - Commit with an imperative, present-tense subject at or below 72 characters.
- Run
- 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
- follow Conventional Commits style (
- 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.mdandscripts/gitea-pr.jsfor PR creation, lookup, and merge operations. - PR tooling must read auth from
GITEA_TOKEN/GITEA_BASE_URLshell environment or ignored.codex-local.envonly; 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 / doneBranch:Branch relationship: standalone from main / stacked on parent branch / continuing existing branchLikely modified areas:Actual modified files:Collision risk: low / medium / highLast 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 <base>..HEADgit diff --stat <base>..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, orResolves. - Merge PRs only after explicit operator approval, required checks, and a final
npm run pr:view -- --number <pr-number>status check.