---
title: Documentation drift
description: Ten specific claims in CLAUDE.md that the code no longer supports, which claims still hold, and the tooling that keeps these docs from drifting too.
---

`CLAUDE.md` was last verified on **2026-08-13**. The Core MVP collaboration work has landed since, and ten of `CLAUDE.md`'s specific claims are now false. It has not been corrected: until it is, this site is the source of truth for anything on the list below. Anything downstream of it — onboarding notes, the Linear backlog, the recommendations doc — inherits the same errors.

:::warning[The list below is not exhaustive]
It covers the claims that were checked. Others off the list are also wrong — `CLAUDE.md` names `src/components/shared/CreateProjectModay.tsx` as an existing (mis-spelled) component, and no such file exists anywhere under `frontend/src`; project creation is inline in `WorkspacePage`. Verify any specific `CLAUDE.md` claim against source before relying on it, whether or not it appears here.
:::

## Corrections

| `CLAUDE.md` says | Source says |
| --- | --- |
| OAuth is broken end to end; the frontend never exchanges the code | Wired. Buttons navigate to the provider, `LoadingPage` POSTs `?code` to `POST /api/auth/oauth/exchange` |
| No frontend route guards; every page renders unauthenticated | `RequireAuth` and `RedirectIfAuthenticated` wrap every private route |
| No `/me` call anywhere | `AuthProvider` calls `GET /api/auth/user` on mount |
| Logout is client-side only | `serverLogout()` revokes before clearing storage |
| 7 of 14 pages are 100% mock data (~4,800 LOC) | 17 routes, 16 pages; only `SettingsPage` is placeholder copy. Pages total 2,638 LOC |
| 17 of 35 app endpoints have no frontend consumer | 19 of 74 routes. The surface more than doubled |
| Three migrations exist | Four — `CoreMvpCollaboration` was added |
| No frontend test framework configured | Vitest, a setup file, and three test files |
| Modules: Auth, Collection, Assets, Upload, ActivityLogs, Organization, Plan, Seeder, Shared | Also Project, Task, TaskComment, Dashboard, AssetVersion, and a `ProfileController` |
| Collection `description` is a phantom field | A real column on `collections`, validated at ≤2000 characters in `CollectionsDto` |

## Still accurate

`CLAUDE.md`'s operations and security claims were re-checked against source on 2026-09-09 and still hold. These docs reproduce them:

- The Supabase connection traps and the RLS lockdown requirement.
- The `dev` → `staging` → `main` branch model and the drift guard.
- Branch protection is unenforced in both repos.
- Tokens live in `localStorage` and are XSS-exfiltratable.
- `npm run lint` mutates files; CI calls `eslint` directly.
- `ssl.rejectUnauthorized: false` applies outside production.
- A local `.env` tends to fall behind `.env.example` as new variables are added.
- Nothing is deployed (re-checked 2026-09-09).

## Backend docs

| File | Problem |
| --- | --- |
| `backend/docs/DB.md` | Predates the organization tables entirely, and describes a one-to-one User↔Plan relation. The code uses `ManyToOne` |
| `backend/docs/LOGS.md` | Lists activity endpoints under `/activity-logs/…`. The actual route is `/api/timeline` |
| `backend/README.md` | Documents only Auth and Collection, and still describes the retired `@feat/` branch convention |
| `frontend/README.md` | Says React 18+. It is React 19 |

## The spec's Swagger metadata — fixed

Generating the reference from the live server confirmed the controllers and the documented contract agree, but it also exposed how thin the Swagger metadata was. On 2026-09-09 `blume check` reported **10 duplicate sidebar labels** in the generated reference, from two causes:

- **Missing summaries.** The collaboration controllers — `ProjectController`, `TaskController`, `TaskCommentController`, `OrganizationController`, `DashboardController` — carried no `@ApiOperation({ summary })`, so every operation fell back to its raw path. Three routes share `/api/projects/{id}` and three share `/api/tasks/{id}`, so the sidebar showed the same label three times.
- **Reused summaries.** `GET /api/assets/logs` and `GET /api/assets/:id/asset-logs` both passed `swaggerAssetLogsResponse`, so both rendered as "Retrieve asset activity logs".

Both are now closed. Every operation carries a distinct `@ApiOperation({ summary })`:

```ts
@ApiOperation({ summary: 'Move a task to another column or position' })
@Patch('tasks/:id/position')
move(/* … */) {}
```

The two asset-log routes keep the shared response schema and override only the summary:

```ts
@ApiOperation({
  ...swaggerAssetLogsResponse,
  summary: "Retrieve the caller's asset activity logs",
})
```

**All 73 operations in the published spec now have a summary, and none is duplicated** — `blume check` reports zero warnings. That improves Swagger UI, the generated reference, `llms.txt`, and search results together.

:::note[Keep it that way]
A new controller method without `@ApiOperation({ summary })` silently reintroduces a raw-path label, and a second route reusing an existing swagger constant reintroduces a duplicate. `blume check` catches both — run it after any controller change.
:::

## Keeping these docs from drifting

The generated [API reference](/reference) is machine-derived rather than written by hand, so it never disagrees with the controllers it was generated from. It is still a snapshot: it goes stale the moment a controller changes and nobody regenerates `openapi.json` — see the note at the end of this page. Everything in these guides can drift, and will.

Three mechanical checks, all shipped with Blume:

```bash
cd docs
npx blume check       # build plus type-check, and the nav diagnostics below
npx blume validate    # internal, anchor, asset, and external links
npx blume audit       # SEO and site-health findings, mapped to source files
npx blume eval        # an agent answers questions using only these docs
```

:::warning[Stop the dev server first, or pass `--isolated`]
All four refuse to run while `blume dev` holds the `.blume/` runtime, failing with *"a blume dev server is running … checking would corrupt its .blume runtime."* `--isolated` builds against `.blume-verify/` instead and leaves the running server alone. Note also that GNU `timeout` is not installed on macOS by default, so wrapping these in `timeout 180 …` fails with `command not found` rather than doing anything useful.
:::

`blume eval` is the interesting one for drift specifically: it tests whether the documentation is *sufficient* to answer a question, which catches the class of staleness where a page is still technically true but no longer describes the system anyone is working on.

:::note[Regenerate the spec when a controller changes]
```bash
cd backend && npm run start:dev   # terminal 1
cd docs    && npm run spec        # terminal 2
```
Run the script from `docs/` rather than piping `curl` from `backend/` — that writes into `backend/docs/` and leaves the reference untouched. Otherwise the reference documents an API that no longer ships.
:::
