From f5896aa575b0aac71917052d12038974f5e950a3 Mon Sep 17 00:00:00 2001 From: NeilClack Date: Thu, 16 Jul 2026 20:23:59 -0500 Subject: [PATCH] Update AGENTS.md --- AGENTS.md | 612 +++++++++++++++++++----------------- AGENTS.md.bak | 363 +++++++++++++++++++++ docs/ai/api.md | 235 ++++++++++++++ docs/ai/svelte-patterns.md | 184 +++++++++++ docs/ai/testing-patterns.md | 241 ++++++++++++++ 5 files changed, 1351 insertions(+), 284 deletions(-) create mode 100644 AGENTS.md.bak create mode 100644 docs/ai/api.md create mode 100644 docs/ai/svelte-patterns.md create mode 100644 docs/ai/testing-patterns.md diff --git a/AGENTS.md b/AGENTS.md index 4006341..45a22ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,363 +1,407 @@ -# portal.Rhemawavetech.com +You're right — I gave you the optimized root content earlier, but I did not provide it cleanly as a single copy/paste artifact. Here it is. -SvelteKit 2 + Svelte 5 (runes) + Tailwind CSS v4 + Prisma 7 + PostgreSQL + Better-Auth. +Save this as: -## Quick start +```text +AGENTS.md +``` + +at your project root: + +````text +# Rhemawave Portal AI Guide + +## Mission + +You are working on portal.Rhemawavetech.com. + +Act as a senior engineer making complete, production-ready changes. + +Do not stop after analysis. +Do not provide a plan and wait. +Do not describe what should be done instead of doing it. + +When asked to make a change: + +1. Inspect the relevant code. +2. Understand the existing implementation. +3. Make the change. +4. Validate the result. +5. Fix any problems introduced. +6. Report what changed and what was verified. + +A task is incomplete until the implementation is finished and verified. + +--- + +# Working Style + +## Focused Context + +Do not load the entire repository into context. + +Use a focused approach: + +1. Identify relevant files. +2. Read only necessary code. +3. Find existing patterns. +4. Make the smallest correct change. + +Avoid: +- broad rewrites +- unrelated cleanup +- speculative refactors +- introducing unnecessary dependencies + +Prefer: +- small diffs +- existing conventions +- explicit implementations + +--- + +# Development Loop + +Always follow this workflow: + +## 1. Understand + +Before editing: + +- Locate relevant files. +- Inspect current behavior. +- Find similar working examples. +- Identify constraints. + +## 2. Implement + +Make the smallest correct change. + +Follow existing architecture. + +Do not replace working patterns with new abstractions unless required. + +## 3. Verify + +Run appropriate checks: ```sh -npm install # engine-strict=true in .npmrc -npm run prepare # svelte-kit sync (generate types) -npm run dev # vite dev on :5173 -``` +npm run check +npm run lint +npm run test +```` -Requires `.env` with at least `DATABASE_URL`, `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL`. +Use narrower commands when appropriate. -## Commands +## 4. Complete -| npm run ... | What it does | -| -------------------------- | ------------------------------------------------ | -| `check` | `svelte-kit sync && svelte-check` (typecheck) | -| `lint` | `prettier --check . && eslint .` (order matters) | -| `format` | `prettier --write .` | -| `test` | `vitest --run` (single run) | -| `test:unit` | `vitest` (watch mode) | -| `migrate -- --name ` | Create a Prisma migration | -| `deploy` | Apply pending migrations | -| `build` | Production build (adapter-node) | +If validation fails: -## Testing quirks +* Diagnose the failure. +* Fix the issue. +* Run validation again. -- **Two Vitest projects**: `client` (Playwright/Chromium headless, `src/**/*.svelte.{test,spec}.{js,ts}`) and `server` (node env, everything else — `src/**/*.{test,spec}.{js,ts}` excluding `.svelte.*`). Tests co-located beside source files. -- **`expect: { requireAssertions: true }`** — every test must assert something. No assertions = test fails. -- **All route tests mock `$lib/db`** (Prisma client) and `$lib/auth-client`/`$lib/auth` via `vi.mock()`. Stub factories in `src/lib/test-utils.ts`. -- **No `.env.test`** — mocking `$lib/db` makes real DB unnecessary. -- **Mock `$app/forms`** for any component using `use:enhance` — provide `enhance: () => () => {}` (action → destroy fn). -- **Mock TipTap** for client tests rendering ProclamationForm — the Svelte 5 component mock pattern is a function `($anchor: Element) => { …; $anchor.replaceWith(div); }`. - -## Architecture notes - -- **Route groups**: `(protected)/` requires auth + profile; `(user)/` is user-facing; `admin/` requires roleId <= 3 (STAFF+). -- **Admin forms**: Each route has its own form component (e.g. `ProclamationForm.svelte`, `CategoryForm.svelte`). The generic `AdminForm.svelte` / `AdminPage.svelte` were **deleted**. Registry config (`registry.ts`) still provides metadata, list fields, search fields, relation options, etc., but forms are hand-written per route. -- **Prisma client** generated to `src/lib/generated/prisma/` (not `node_modules/.prisma/client`). Lazy singleton via Proxy in `src/lib/db.ts`. -- **Better-Auth** server instance in `src/lib/auth.ts`, browser client in `src/lib/auth-client.ts`. Session fetched in `src/hooks.server.ts`. -- **Roles**: integer hierarchy (SUPER=1, ADMIN=2, STAFF=3, USER=4). `canAccessAdmin` checks roleId <= 3. -- **Payments are stubbed** (`src/lib/payments.ts` — setTimeout + console.log). -- **API keys**: SHA-256 hashed before storage (`ApiKey` model); only prefix (11 chars) stored for identification. `/api/v1/*` requires `Authorization: Bearer `, except `/api/v1/automations/` (GHL webhook, no API key needed). Rotation: normal (48h grace via `expiresAt`) or emergency (immediate via `revokedAt`). Super-only admin page at `/admin/api-keys`. - -## Formatting conventions - -- Tabs, single quotes, no trailing commas, 100 print width (enforced by Prettier). -- Svelte 5 runes only (`$state`, `$derived`, `$effect`, `$props`). No legacy stores (`writable`, `derived`, etc.). +Never finish with known failures. --- -## Svelte 5 Runes Gotchas & Patterns +# Project Overview -### `$state` initialization vs `$effect` sync +Application: -State variables declared with `$state('')` are set once on component mount. To keep them in sync with a changing `$props()` value, use a **separate `$effect`**: +portal.Rhemawavetech.com -```svelte - +Package manager: + +npm + +--- + +# Quick Start + +Install: + +```sh +npm install ``` -This is the **only correct pattern** for form fields that edit an existing record. Do NOT initialize `$state` directly from the prop — that only captures the initial value and won't re-sync when the parent changes the record (e.g. switching from one row to another in a modal). +Generate types: -### `$effect` ordering matters - -Effects run in declaration order. If you have multiple effects, the first one's writes are applied before the second one runs. This matters for the `dirty` flag: - -```svelte -let dirty = $state(false); - -// Runs first — resets dirty on mount / record change -$effect(() => { - dirty = false; -}); - -// Runs second — syncs field values -$effect(() => { - name = String(record.name ?? ''); - // … -}); +```sh +npm run prepare ``` -Both effects run synchronously during mount, so by the time the browser paints, all state is consistent. +Run development: -### `bind:value` on `` elements when the state is set from a `$effect`. Svelte 5's select binding does not respond to `$state` changes triggered inside `$effect`. Instead: - -**Use `selected` on ` - {#each options as opt (opt.value)} - - {/each} - +```sh +npm run dev ``` -This pattern applies to ALL `` works fine - -Unlike `` works correctly with `$effect`-driven state. Feel free to use it for text inputs. - -### `$props()` destructuring - -Always destructure `$props()` with defaults: - -```svelte -let { - record = {} as Record, - fieldErrors = {} as Record, - isSuper = false, -}: { record?: Record; … } = $props(); -``` - -The type annotation on the destructuring object is REQUIRED for Svelte 5 to infer prop types. Default values are used when the parent doesn't pass that prop. - -### Component mocking for client tests - -When mocking a Svelte 5 component in `vi.mock`, use a function that takes `($anchor, props)`: - -```ts -vi.mock('$lib/components/tiptap/TipTap.svelte', () => ({ - default: ($anchor: Element, _props: Record) => { - const div = document.createElement('div'); - div.setAttribute('data-testid', 'tiptap-mock'); - $anchor.replaceWith(div); - } -})); +```text +DATABASE_URL +BETTER_AUTH_SECRET +BETTER_AUTH_URL ``` --- -## Testing Patterns & Gotchas +# Architecture Map -### What tests to write +## Routes -For every admin route, write BOTH: +Protected routes: -1. **Server test** (`page.server.spec.ts`): Test the `load` function and all `actions` (upsert, delete). Cover role-based access control (Super vs non-Super content stripping, rejection of unauthorized mutations). Mock `$lib/db` and `$lib/svelte-admin/crud`. - -2. **Client test** (`Component.svelte.spec.ts`): Test the form component rendering in Playwright/Chromium. Cover: - - All fields render - - Pre-fill on edit (name input, select values) - - Conditional field visibility (e.g. content hidden for non-Super) - - Submit button disabled/enabled by dirty state - - No visual regression testing — only functional assertions. - -### Available Playwright/Vitest Browser Locator API - -These methods are available on `page` from `vitest/browser`: - -| Method | Works? | Example | -| ------------------- | ------ | --------------------------------------------- | -| `getByRole` | ✅ | `page.getByRole('textbox', { name: 'Name' })` | -| `getByText` | ✅ | `page.getByText('Content')` | -| `getByPlaceholder` | ✅ | `page.getByPlaceholder('Email Address')` | -| `getByAltText` | ✅ | `page.getByAltText('Sign In with Google')` | -| `getByTestId` | ✅ | `page.getByTestId('tiptap-mock')` | -| `getByLabel` | ❌ | Not available in this version | -| `getByDisplayValue` | ❌ | Not available in this version | - -For label-identified elements, use `getByRole` with `name` option: - -- Text inputs → `getByRole('textbox', { name: /^Name$/ })` -- Selects → `getByRole('combobox', { name: /^Frequency$/ })` -- Buttons → `getByRole('button', { name: 'Save' })` - -**Always use regex `{ name: /^Exact$/ }`** for role queries to avoid strict-mode violations from substring matches (e.g. `'Category'` matching both `'Category'` and `'Sub-Category'`). - -### Querying select values in client tests - -Since `bind:value` on selects doesn't work with `$effect`-driven state, and we use `selected` on `