diff --git a/README.md b/README.md
index 876c24a..aff8274 100644
--- a/README.md
+++ b/README.md
@@ -23,7 +23,6 @@ Requires Node.js 24+ and [pnpm](https://pnpm.io) 10+.
```bash
pnpm install
-pnpm dev # Vite dev server on :5173
```
To also run the API locally you need PostgreSQL and a `DATABASE_URL` in
@@ -35,6 +34,18 @@ pnpm --filter @t19/server db:seed # optional: replace the catalog with the
pnpm --filter @t19/server dev # GraphQL Yoga on :3001/graphql
```
+Keep the API running in one terminal, then start the frontend in another:
+
+```bash
+pnpm dev # Vite dev server on :5173, client only
+```
+
+Open to use the application.
+
+The frontend communicates with the GraphQL backend through Apollo Client. During local development, Vite forwards API requests to the backend running on port 3001. On the VM, Apache handles this routing instead.
+
+To use a different GraphQL endpoint, set `VITE_GRAPHQL_URL` in `client/.env.local`.
+
`db:seed` is a reset command, not a loader. It replaces the catalog and,
through foreign-key cascades, deletes the reviews and crate items hanging
off it. Do not run it against a database holding user content you want.
@@ -75,23 +86,54 @@ All four must pass. CI runs the same thing.
| Path | What it is |
| ---------------- | --------------------------------------------------------------------------------------------------------------- |
-| `client/` | React + Vite. Still the M1 skeleton; #9 wires the search view to the API |
+| `client/` | React + Vite + Apollo Client. Album and artist search connected to the GraphQL API |
| `server/` | GraphQL Yoga + Drizzle over PostgreSQL. `searchAlbums` with filtering, facets and keyset paging. Live on the VM |
| `schema.graphql` | The contract both sides work against |
| `docs/` | Architecture and data model ADRs, UI specification, workflow, AI log, VM runbook |
-Implemented: the PostgreSQL schema, with tracks, simulated users, reviews
-and crate items, and trigger-maintained full-text search over titles
-(weight A) and artist names (weight B). On top of it, album search by word,
-genre and decade facets, keyset paging, four sort modes, and the `X-User-Id`
-contract for the simulated user.
+**Implemented:** PostgreSQL database with tables for albums, artists, genres,
+tracks, users, reviews and crate items. The GraphQL backend supports album
+searches, genre and decade filtering, pagination and sorting by `RELEVANCE`,
+`TITLE_ASC` and `RELEASE_YEAR_DESC`. The schema also defines
+`AVERAGE_RATING_DESC`, but this currently returns an error until reviews are
+implemented.
+
+The database tables for reviews and crate items exist, but the corresponding
+GraphQL functionality is not yet implemented.
-Planned: filters and sorting in the UI, the reviews and wantlist
+The frontend supports searching by album title or artist name, with a 300 ms
+debounce to avoid unnecessary requests. Users can also submit a search
+immediately using Enter or the Search button. The interface provides loading
+feedback, error handling and a message when no albums are found.
+
+Currently, the frontend displays the first 20 matching albums, sorted by
+relevance. Filtering, sorting, pagination controls and album details are not
+yet available in the UI.
+
+**Planned:** filters and sorting in the UI, the reviews and wantlist
mutations, accessibility and bærekraft (M3), then the Playwright `e2e/`
suite (M4). The GraphQL operations behind those are in `schema.graphql`,
but `searchAlbums` is the only query with a resolver so far, along with the
`Album` fields it returns.
+Simulated user identity is defined in the GraphQL schema and architecture
+documentation, but runtime handling of `X-User-Id` is not yet implemented.
+
+## Try the application
+
+Open or your local frontend and try
+these searches. Expected counts depend on the sample catalog from
+`pnpm --filter @t19/server db:seed`; a different database may return different
+results.
+
+| Search | Expected matches | Albums |
+| ----------- | ---------------- | ------------------------------------------ |
+| `blue` | 4 | Kind of Blue, Blue Train, Blue, Blue Lines |
+| `coltrane` | 2 | A Love Supreme, Blue Train |
+| `radiohead` | 2 | OK Computer, Kid A |
+
+Search is case-insensitive: `BLUE` should return the same matches as `blue`.
+
## Infrastructure
The database and the GraphQL API run directly on the group VM, not in
diff --git a/docs/AI-USAGE.md b/docs/AI-USAGE.md
index 399bc46..88b7c18 100644
--- a/docs/AI-USAGE.md
+++ b/docs/AI-USAGE.md
@@ -36,17 +36,23 @@ the part that matters most.
| 2026-10-01 | erikhfj | opencode | Simplified this log to a table at the group's request |
| 2026-10-01 | robertky | Codex (ChatGPT) | Assisted with formulating good reviews of the architecture and UI-specification PRs. The group decided to include decade filtering; Rachel implemented the corresponding schema and documentation update in #38. |
| 2026-10-07 | erikhfj | opencode | Simulated user identity for #10: a `User` type and the `X-User-Id` header contract, read into a per-request GraphQL context so that no field in `schema.graphql` takes a `userId` argument |
+| 2026-10-07 | rachelks | ChatGPT | Assisted with frontend styling, UI decisions and troubleshooting for #9 (PR #47). |
| 2026-10-09 | robertky | Codex (ChatGPT) | AI was a good sparring partner when I drafted the initial #5 PostgreSQL migration. It gave me ideas on how to structure and improve the tables, relation tables, constraints, indexes, full-text search triggers and TypeScript migration runner. |
| 2026-10-09 | erikhfj | opencode | VM runbook for #8, and converted `~/T19-Project-2` from a hand-copied prototype into a real git clone of `main` with a deploy SSH key. The gitignored `.env` was checksummed across `git checkout --force` to prove a deploy does not wipe `DATABASE_URL` |
| 2026-10-10 | evenkkl | Claude | Assisted with implementing the GraphQL Yoga server with `searchAlbums` for #6, and formulated an easy to understand PR description. Guided the reset of the prototype tables in the VM database (backup taken first). |
| 2026-10-10 | erikhfj | opencode | Wrote out the facet-count trade-off on #13: OR within a category pairs with per-dimension exclusion, AND with global counts. Posted as an analysis for the group to settle, not as a decision |
| 2026-10-10 | erikhfj | opencode | Forced `source-map-js` to 1.2.2 through `pnpm.overrides` for #43, after the Dependabot alert |
+| 2026-10-10 | rachelks | ChatGPT | Assisted with reviewing PR #42, identifying an issue with the backend deployment workflow that led to #44. |
| 2026-10-10 | erikhfj | opencode | Traced the deploy gap rachelks raised on #42 and opened #44: CI built `server/` in the runner workspace while `crate-api.service` runs `dist/index.js` out of `~/T19-Project-2/server`, so a restart kept bringing back a 23 September prototype |
| 2026-10-12 | erikhfj | opencode | **Tool output corrected by a person.** The draft identity contract said `displayName`, derived server-side from the UUID, stops one user writing as another. It does not: the client can still send any UUID. robertky caught it on review and the wording was fixed in #40 |
| 2026-10-13 | evenkkl | Claude | Fixed the review feedback on #6: cursor values validated against the sort, `reviewCount` returns 0 until reviews exist. The group decided genre filters combine with AND; Claude made the filter and the genre counts match that. |
+| 2026-10-15 | rachelks | Codex (ChatGPT) | Assisted with reviewing PRs #45 and #46, identifying issues with deployment checks, database migrations and FTS triggers. Erik and Robert addressed the issues before merging. |
| 2026-10-15 | erikhfj | opencode | Rachel's four review points on #45: the smoke query omitted the required `sort` field so it could never have passed, the deploy step skipped instead of failing, and the body check grepped for the word "errors" instead of parsing JSON. Two bugs in my own first attempt: a multi-line `python3 -c` cannot live inside a YAML block scalar, and `\"` in bash single quotes is a literal backslash |
+| 2026-10-15 | rachelks | Codex (ChatGPT) | Assisted with reviewing M2 documentation for #48, identifying outdated setup instructions and missing frontend information. |
| 2026-10-15 | erikhfj | opencode | Documentation pass for #48: this log and the README brought up to date with what has actually landed |
| 2026-10-15 | erikhfj | opencode | Folded #46 into the README, and added `db:migrate` to the deploy on #45 after #46 pointed out it never ran: a deploy that lands migration files without applying them leaves the schema behind the code. Also reconciled `DATABASE_URL`, which the systemd unit reads from the checkout root and the server package scripts read from `server/.env` |
+| 2026-10-16 | rachelks | Codex (ChatGPT) | Assisted with updating README and VM setup documentation for #48, clarifying implemented functionality and local development instructions. |
+| 2026-10-16 | evenkkl | Claude | Assisted with reviewing PRs #50 and #51, checking the changes against the code and identifying areas for improvement. |
## Notes
diff --git a/docs/VM-setup.md b/docs/VM-setup.md
index c3a916c..33b438b 100644
--- a/docs/VM-setup.md
+++ b/docs/VM-setup.md
@@ -13,14 +13,33 @@ DATABASE_URL=postgres://t19:@127.0.0.1:5432/crate
```bash
pnpm install
pnpm --filter @t19/server db:migrate # create the catalog tables
-pnpm --filter @t19/server db:seed # replace the catalog with the sample albums
pnpm --filter @t19/server build
pnpm --filter @t19/server start # http://localhost:3001/graphql
```
-`db:seed` is a development/reset command. It replaces the catalog and, through
-foreign-key cascades, deletes its associated reviews and crate items. Do not run
-it against a database with user content you intend to keep.
+### Optional sample data
+
+To reset the database with the small sample catalog:
+
+```bash
+pnpm --filter @t19/server db:seed
+```
+
+**Warning:** This replaces the existing catalog and deletes associated
+reviews and crate items. Do not run it against a database containing data
+you want to keep.
+
+## Frontend GraphQL endpoint
+
+The frontend uses Apollo Client with `/graphql` as its default endpoint.
+On the VM, Apache proxies `/graphql` to the API on port 3001; the frontend's
+`/project2/` asset base does not change that endpoint.
+
+For local development, run `pnpm --filter @t19/server dev` and `pnpm dev` in
+separate terminals, after configuring the database and applying migrations.
+Open . Vite proxies `/graphql` to
+`http://127.0.0.1:3001`, so both processes must be running. To override the
+frontend endpoint, set `VITE_GRAPHQL_URL` in `client/.env.local`.
## Smoke suite