> ## Documentation Index
> Fetch the complete documentation index at: https://docs.theaitracker.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Development

> Day-to-day commands for building, testing and formatting the project.

## Common commands

| Command                | What it does                                           |
| ---------------------- | ------------------------------------------------------ |
| `composer dev`         | Serve + queue listener + Vite, all at once             |
| `composer dev:ssr`     | Same, plus Pail log tailing and the Inertia SSR server |
| `composer test`        | Clears config, then runs the PHPUnit suite             |
| `npm run dev`          | Vite dev server only                                   |
| `npm run build`        | Production asset build                                 |
| `npm run typecheck`    | `tsc --noEmit` across the frontend                     |
| `npm run format`       | Prettier write over `resources/`                       |
| `npm run format:check` | Prettier check — use this before pushing               |
| `npm run docs:dev`     | Mintlify preview of this documentation                 |

## Frontend layout

The React app is an Inertia SPA served by Laravel. Everything lives under `resources/js`:

```
resources/js/
├── app.tsx        Inertia entry point
├── pages/         One component per Inertia page (Visibility, Prompts, Setup, …)
├── layouts/       Shell layouts (dashboard chrome, auth, marketing)
├── components/    Shared and shadcn/ui components
├── hooks/
├── lib/
└── types/
```

Controllers render pages by name, so `Inertia::render('Visibility')` maps to
`resources/js/pages/Visibility.tsx`. Route helpers come from Ziggy, so named Laravel routes
are callable from TypeScript as `route('prompts.show', id)`.

<Warning>
  Prettier runs with `prettier-plugin-organize-imports` and `prettier-plugin-tailwindcss`.
  Both rewrite code, so run `npm run format` rather than hand-sorting imports or class lists
  — otherwise CI's `format:check` will disagree with you.
</Warning>

## Backend layout

```
app/
├── Http/Controllers/   Inertia page controllers + JSON endpoints
├── Models/             Eloquent models (Campaign, Prompt, PromptRun, …)
├── Services/           Provider clients and domain logic
│   ├── LLM/            One provider class per LLM
│   ├── BrandSentiment/ Topic selection through to sentiment scoring
│   └── Onboarding/      Generators used by the setup wizard
├── Jobs/               Queued fetch/process work
├── Console/Commands/   Scheduled and manual entry points
├── Queries/            Read-model query objects
└── Policies/
```

Routes are split by concern in `routes/`: `web.php` holds the dashboard shell and requires
`campaigns.php`, `onboarding.php`, `settings.php`, `billing.php`, `admin.php`,
`notifications.php` and `auth.php`.

## Working with the queue

Almost all provider work is queued. `QUEUE_CONNECTION` defaults to `database`, so a worker
must be running for prompt batches, sentiment fetches and traffic imports to make progress:

```bash theme={null}
php artisan queue:listen --tries=1
```

`composer dev` already starts one. When a job fails, `failed_jobs` holds the exception —
`php artisan queue:retry all` re-dispatches after a fix.

## Tests

Tests are written with Pest (`tests/Pest.php`) and split into `tests/Unit` and
`tests/Feature`.

```bash theme={null}
composer test                              # whole suite
php artisan test --filter=PromptsPageTest  # one class
php artisan test tests/Feature             # one directory
```

`phpunit.xml` *intends* to pin the test environment to in-memory SQLite
(`DB_DATABASE=:memory:`), an array cache/session and the `sync` queue driver. The `sync`
queue means jobs run inline, so a test that dispatches a prompt batch executes it during the
assertion rather than leaving it queued.

Feature tests extend `Tests\TestCase` and use `RefreshDatabase` (wired up in
`tests/Pest.php`). `TestCase::registerSqliteRegexpFunction()` registers a userland `regexp()`
function on SQLite connections, because production runs MySQL and queries such as
`App\Queries\TrafficQueries` rely on the `REGEXP` operator that SQLite does not implement
natively.

<Warning>
  **The SQLite pinning does not currently take effect.** On a machine whose `.env` sets
  `DB_CONNECTION=mysql`, the suite connects to that MySQL database instead of in-memory
  SQLite — `RefreshDatabase` then migrates against your development database, and the run
  fails during migration with `SQLSTATE[42000] ... 1071 Specified key was too long`. Setting
  `DB_CONNECTION`/`DB_DATABASE` as shell variables and adding a `.env.testing` both fail to
  override it, so something in the boot path is pinning the connection.

  Until this is tracked down, do not assume the suite is isolated from your development
  database. Point `.env` at a throwaway database before running the tests locally.
</Warning>
