Conventions and contributing
This page collects the conventions contributors follow. The authoritative sources in the repository are AGENTS.md, .github/copilot-instructions.md and the decision records in docs/decisions/; where this page and those files disagree, the files win.
Before you change anything
Section titled “Before you change anything”- Read the accepted decisions in
docs/decisions/README.mdthat apply to the paths you’re changing, and follow them. Don’t reopen a decision unless the team asks to. - Check the backlog in
docs/TODO.md. - Get the app running locally: Local development.
Where code goes
Section titled “Where code goes”| You’re adding… | Put it in |
|---|---|
| Supabase reads, writes, RPC calls, storage | apps/web/src/api/ (the only data-access layer) |
| A reusable UI component | apps/web/src/components/ |
| UI owned by one role’s workspace | apps/web/src/screens/<role>/ |
| Workspace data loading and actions | apps/web/src/hooks/<role>/ |
| A pure domain helper | apps/web/src/utils/ |
| A low-level helper | apps/web/src/lib/ |
| A shared status or workflow constant | apps/web/src/constants/ |
| A runtime feature gate | apps/web/src/config/runtime.ts |
| A schema or policy change | a new migration in supabase/migrations/ |
Role-specific code lives in role-named subfolders (admin/, tester/, developer/, product-manager/, stakeholder/).
Naming
Section titled “Naming”| Kind | Style | Example |
|---|---|---|
| Components and screens | PascalCase | ConfirmDialog.tsx, AdminDashboard.tsx |
| Hooks | camelCase with use |
useAppSession.ts |
| API modules, utilities, constants | kebab-case | admin-task-directory.ts, task-request-status.ts |
API conventions
Section titled “API conventions”- Name functions
load*for reads,save*for create/update anddelete*for deletes. - Return a result envelope with
sourceandmessageinstead of throwing. - Define mutation inputs as
Save*Inputinterfaces with explicit fields. - Use the single Supabase client from
apps/web/src/api/supabase.ts. - Enforce access in RLS, not only in the UI.
TypeScript and React
Section titled “TypeScript and React”- Interfaces for domain models; type aliases for unions and workflow states.
- Separate database row interfaces from domain types.
- Explicit prop interfaces; no
any. - Keep components thin; move logic into hooks.
- The import alias
@/resolves toapps/web/, notapps/web/src/, so write@/src/lib/utils. strictisn’t enabled intsconfig.json; be explicit anyway.
Rules recorded as decisions
Section titled “Rules recorded as decisions”- Use the in-house router; don’t add
react-router-dom. - Charts are native SVG; don’t add a charting library.
apps/web/src/apiis the only data-access boundary.- Public self-sign-up stays disabled.
VITE_DEV_LOGIN_SHORTCUTSlives only in.env.development.local.- RLS policies use
SECURITY DEFINERhelpers, never recursive queries onprofiles. - Don’t wrap the app in React
StrictModein development. - npm is the package manager;
package-lock.jsonchanges only throughnpm installornpm uninstall. - Delivery lifecycle and QA execution stay separate fields.
Database changes
Section titled “Database changes”- Every schema or policy change is a new migration. Don’t edit applied migrations, and don’t patch tables by hand.
- New privileged mutations should write audit rows.
- Test locally with
supabase db resetand the validation scripts.
Secrets
Section titled “Secrets”- Never commit env files;
.gitignoreexcludes them. - Never put a service-role key in a
VITE_variable. - If you find a committed secret, report its path and stop. Don’t paste the value anywhere.
- The emails and passwords in
README.mdandsupabase/seed.sqlare deliberate local seed accounts, not leaked secrets.
Generated and vendored files
Section titled “Generated and vendored files”.naso/tooling/is vendored; never edit it by hand.- The list between the decision markers in
docs/decisions/README.mdis generated.
Commits
Section titled “Commits”Commit subjects follow type(scope): summary, for example:
feat(tester): add retest badge to queue rowsfix(api): scope task request attachments to ownerdocs(readme): update local portsTypes in use: feat, fix, refactor, chore, docs, modify.
- Don’t bypass the pre-commit hook; fix what it reports and commit again. It blocks lines shaped like secrets and reports files outside your declared scope (
NASO_SCOPE). - Ask which branch a change belongs on; there’s no written branching rule.
Before you call it done
Section titled “Before you call it done”npm run lintpasses.npm run buildsucceeds if you touched build-relevant code.npm run validate:corepasses if you touched workflows, RLS or RPCs.- Update the docs in
docs/and this site if behaviour changed.