NutriApp — Agent Behaviour Rules
Claude Code: read this at the start of every session alongside CLAUDE.md. These rules govern autonomous decision-making, when to stop and ask, and the exact handover protocol with Cursor.
Autonomy Levels
Proceed without asking:
- Writing new files that follow an established pattern already in the codebase
- Running tests (
vitest run,playwright test) - Running lint and type-check (
eslint --fix,tsc --noEmit) - Installing packages already listed in the approved stack
- Modifying Drizzle schema and generating migrations (dev branch only)
- Writing or updating translation keys — always update both locale files in the same operation
- Refactoring within a single file where observable behaviour does not change
- Updating
.cursor/cursor-tasks.mdto mark tasks[READY]or[DONE] - Adding or updating tests for code you just wrote
Ask before proceeding:
- Installing a package NOT in the approved stack
- Deleting any file
- Applying Drizzle migrations to staging or production Neon branches
- Changing any security-related code (auth middleware, sanitisation, rate limiting, credit deduction logic)
- Modifying
src/lib/ai/client.ts - Any change that touches the Stripe payment or webhook flow
- Changing the on-signup provisioning logic (user_profiles or user_credits)
- Writing a database query that does NOT filter by
userId(unless explicitly public data — confirm before proceeding)
Always stop and prompt the human:
- A required environment variable is missing from
.env.local:⚠️ HUMAN INPUT NEEDED Key required: [KEY_NAME] Where to get it: [URL] Add to .env.local, then reply "ready" to continue. - A phase test gate has failed and the fix would require an architectural change
- Two valid approaches exist with meaningfully different long-term trade-offs
- End of any phase — confirm before starting the next phase
- Any security decision not covered by
docs/security.md - A Cursor task is complete and needs Claude Code review before marking done
Phase Discipline
- Read
docs/phases.md(or.claude/phases.md) at the start of each session - Identify the current phase and work only on tasks within it
- When all Claude Code tasks in a phase are complete, run the full test gate
- If all tests pass: update phase status in
phases.md, then stop and notify the human before starting the next phase - If any test fails: fix it before declaring the phase complete — never skip
- Never begin Phase N+1 without explicit human confirmation
Neon Branch Discipline
- Local development: always use
DATABASE_URL(dev Neon branch) - Never use
DATABASE_URL_PRODin any local or test context - Schema changes: edit
src/lib/db/schema.ts→ generate → migrate (dev only) - CI applies migrations to
stagingbranch automatically - Production migrations require explicit human sign-off
Environment Variable Protocol
When any required env var is missing, output this exact block and stop:
⚠️ HUMAN INPUT NEEDED
Key required: [KEY_NAME]
Where to get it: [exact URL or instruction]
Once added to .env.local, reply "ready" to continue.
Do not attempt to work around a missing key. Do not use a fallback value. Do not proceed until the human replies "ready".
Error Handling Protocol
- Read the full error output — never truncate
- If it is an environment or config issue → output
⚠️ HUMAN INPUT NEEDED, stop - If it is a code issue → attempt one fix, re-run, check result
- If still failing after one fix attempt → output the full error and your analysis, then await human input. Do not loop.
File Discipline
- Never modify Drizzle migration files that have already been applied (identified by timestamp prefix). Create new migrations instead.
- When adding a translation key: update BOTH locale files in the same operation — never commit one without the other
- When adding a new Hono route: also add its rate limit config to
src/lib/redis/client.tsand its entry todocs/security.md - When adding a new public route: update the sitemap generator and
check
robots.txt
Testing Protocol
After completing any set of tasks:
vitest run— fix all failures before continuingeslint .— fix all errors (warnings acceptable, but note them)tsc --noEmit— fix all type errors- Phase gates only:
playwright test
Never commit code with failing unit tests or TypeScript errors.
═══════════════════════════════════════════════
CURSOR HANDOVER PROTOCOL
═══════════════════════════════════════════════
Cursor runs Opus models and handles complex, visually rich UI components. Claude Code handles all backend, data fetching, business logic, and structurally simple UI. This division is strict.
Claude Code owns — never delegate to Cursor:
- All Hono API routes and middleware
- All Drizzle schema and migrations
- The AI client abstraction (
src/lib/ai/client.ts) - Auth flow and session management
- Security layer (sanitisation, rate limiting, access control)
- The full test suite
- CI/CD configuration
- All React Query hooks in
src/hooks/ - All Zustand stores in
src/store/ - i18n configuration and locale files
- Credits ledger and billing logic
- Components where the primary complexity is data or business logic
- Simple structural UI: form shells, basic nav links, toggles, badges that don't require animation or multi-state Tailwind composition
Cursor owns — complex UI only:
Components that are:
- Visually rich with multiple interactive states
- Animation-heavy (scanner overlay, chatbot drawer, onboarding flow)
- Layout-complex across mobile and desktop breakpoints
- Explicitly marked
[CURSOR]inphases.md - Listed as
✅ READYin.cursor/cursor-tasks.md
Mandatory prerequisites before any Cursor task is marked READY:
Claude Code must complete ALL of these before marking a task ready:
- Props type defined in
src/types/components.ts - React Query hook written and tested in
src/hooks/ - API endpoint working and returning the correct response shape
- At least one reference component exists in the codebase for Cursor to follow
- Task entry written in
.cursor/cursor-tasks.mdwith the full template below
Cursor task entry template (copy exactly):
### [ID] — ComponentName
**Status:** ✅ READY
**File to create:** `src/components/ComponentName/index.tsx`
**Reference component:** `src/components/[ExistingComponent]/index.tsx`
**Props** (already in `src/types/components.ts`):
\`\`\`ts
type ComponentNameProps = {
// exact type here — do not change it
};
\`\`\`
**Data hook:** `useHookName()` from `src/hooks/useHookName.ts`
Returns: `{ data, isLoading, error, mutate }`
**Design brief:**
[Visual direction: aesthetic tone, motion style, colour token usage,
which design system tokens to apply from tailwind.config.ts]
**States to implement:**
- Loading: [description — use skeleton from P6-01 if available]
- Empty: [description]
- Error: [description]
- Default: [description]
- [any other interaction states]
**Translation keys** (already added to both locale files by Claude Code):
- `section.key` — "English value"
**Accessibility requirements:**
- [specific aria roles, keyboard behaviour, focus management]
**Cursor must not:**
- Use useEffect for data fetching — use the hook listed above
- Hardcode any user-visible string — use useTranslation()
- Add new npm dependencies without checking with Claude Code first
- Change the props interface
After Cursor delivers a component — Claude Code review steps:
eslint src/components/ComponentName/— fix any violationstsc --noEmit— fix type errorsvitest run— fix integration failures- Write or verify component test file exists
- Mark
[DONE]in.cursor/cursor-tasks.md - Tick the checkbox in
phases.md - If the component has accessibility requirements: run axe-core on it
Communication Style
- Use checkboxes to show progress within a phase
- Flag blockers immediately — do not work around them silently
- When making a non-obvious architectural decision, leave a brief inline comment in the code explaining why
- Never invent API behaviour — if unsure how an external API works, say so and reference the documentation URL rather than guessing