# Contributing ## Project Structure - `src/app`: Next.js routes, layouts, route handlers, and server actions. - `src/components`: Reusable UI. Client components declare `"use client"`. - `src/lib/twenty`: Twenty URL handling, GraphQL construction, API calls, metadata enrichment, and short-lived read caching. - `src/lib/db`: Drizzle schema and database client. - `drizzle`: Ordered SQL migrations committed with schema changes. ## Data Ownership Twenty remains the record system of record. PostgreSQL stores portal users, authorization mappings, configuration, metadata snapshots, webhook receipts, and audit history. All Twenty API calls must remain server-side. Browser components should call server actions or authenticated portal routes. ## Portal Record Access Every record read or mutation must: 1. Resolve the authenticated portal view. 2. Apply its configured record scope and fixed filters. 3. Verify an existing record before update, delete, note, or attachment actions. 4. Record mutations in the audit log. Use helpers in `src/lib/access.ts`, `src/lib/portal-record.ts`, and `src/lib/twenty/filters.ts` rather than rebuilding authorization filters. ## Upload Lifecycle Local branding and profile uploads live under `data/uploads` and are served by `/api/uploads/[filename]`. Use `src/lib/uploads.ts` for validated saves and deletes. Never join a client-provided path directly onto the upload directory. Twenty record files use this flow: 1. Upload the binary through the metadata GraphQL `uploadFilesFieldFile` mutation. 2. Create an `Attachment` record through the generated data API. 3. Link it using the target field for the portal object. 4. Delete the scoped `Attachment` record to remove it. File previews and download links are generated by `src/lib/file-values.ts` and streamed through the authenticated `/api/twenty/files` proxy. ## Settings Uploaded local files and external URL settings are intentionally separate. Local `/api/uploads/...` paths are used for previews but are not copied into URL input fields. Save actions preserve existing local uploads unless a replacement or explicit delete action is submitted. ## Verification Run before opening a pull request: ```bash npm run lint npm run typecheck npm run test npm run build ``` When the Drizzle schema changes, add a new ordered SQL migration and run: ```bash npm run db:migrate ```