# Hallucination dogfood

> Why `.izri/izri.yml` for Izri itself looks the way it does, and what
> the deployer has to provision.

## TL;DR

Izri is tested like any other project that declares runtime services in its `.izri/izri.yml`:

```yaml
services:
  postgres: { image: postgres:15, env: { POSTGRES_PASSWORD: postgres } }
  redis:    { image: redis:7 }
```

The runner provisions both containers via [testcontainers](https://www.npmjs.com/package/testcontainers) (3.8M weekly downloads — ecosystem standard) before `runtime.start` fires. Well-known service names produce conventional env vars (`DATABASE_URL` for postgres, `REDIS_URL` for redis), so the SUT's start command needs no surgical wiring.

## Single requirement: Docker socket access

The runner image needs `docker` available and the Docker daemon reachable — same as any CI environment that runs container-based services (GitHub Actions, GitLab CI, CircleCI all require this). Two mechanisms:

- **Docker socket mount** (`-v /var/run/docker.sock:/var/run/docker.sock`) — easiest. The runner uses the host's Docker daemon. Containers it starts are siblings, not children.
- **Docker-in-Docker** (`--privileged` + dind sidecar) — fully isolated but heavier.

Both are deployment concerns; testcontainers auto-discovers either. The reaper testcontainers ships with cleans up orphaned containers on runner crash, so SIGKILL won't leak state.

## End-to-end flow

```
runner job
  → clone PR
  → services: spin up postgres + redis via testcontainers
              → exposes DATABASE_URL, REDIS_URL in env overlay
  → start:    install + migrate + dev:packages/api/web
  → ready:    http://localhost:5173 (2xx)
  → test:     playwright --reporter=junit, PLAYWRIGHT_COVERAGE=1
              (monocart-reporter wraps page.coverage; emits LCOV)
  → results:  apps/web/test-results/junit.xml
  → coverage: apps/web/test-results/lcov.info
  → services: stop + remove containers (finally block; idempotent)
```

## Auto-exposed env vars

The runner exposes connection info to the SUT via two layers:

**Generic (every service)**:

- `IZRI_SERVICE_<NAME>_HOST` — typically `localhost`
- `IZRI_SERVICE_<NAME>_PORT_<INTERNAL>` — mapped host port for each exposed container port

**Conventional (well-known names)**:

| Service name | Auto-exposed env var | Format |
|---|---|---|
| `postgres` / `postgresql` | `DATABASE_URL` | `postgresql://postgres:<password>@<host>:<port>/<db>` |
| `mysql` | `DATABASE_URL` | `mysql://root:<password>@<host>:<port>/<db>` |
| `redis` | `REDIS_URL` | `redis://<host>:<port>` |
| `mongo` / `mongodb` | `MONGO_URL` | `mongodb://<host>:<port>` |

Password / DB defaults come from the service's `env:` block when set, otherwise sensible defaults (`postgres`/`postgres` for Postgres, etc.). The SUT reads `DATABASE_URL` directly; no service-name-aware code in the user's app required.

## Adding a new service Izri (or any project) needs

Just declare it:

```yaml
services:
  elasticsearch:
    image: elasticsearch:8.11.0
    env: { discovery.type: single-node, xpack.security.enabled: 'false' }
    ports: [9200]
    waitFor: { log: 'started' }
```

For unknown names there's no `*_URL` convenience — your start command references it via `${IZRI_SERVICE_ELASTICSEARCH_HOST}:${IZRI_SERVICE_ELASTICSEARCH_PORT_9200}`.

## Known limitations

- **Backend coverage** for Izri itself — Slice 0 captures frontend (Playwright) only. `NODE_V8_COVERAGE` would require the API process to be a Node subprocess of the test runner; Playwright's `webServer` would have to be the spawn point. Frontend-only for v1.
- **Source-map resolution edge cases** — monocart's `sourcePath` transform strips the bundled-host prefix back to `apps/web/...`. If a React Router build emits chunks with a path the transform doesn't expect, those entries land in `notInstrumented`. The `entryFilter` glob mitigates by ignoring anything outside `apps/web/app/**`.
- **Concurrent runner replicas** — services are per-job (testcontainers gives each container a unique name) so replicas don't collide; but image pulls add startup latency. Warm caches on the host help.
