docs(claude): record plan 002 phase 2 in the project status

Why:
- The status section described plan 002 as Phase 1 only. The /v1/points read
  routes, the query service, and the points scopes had shipped but were listed
  under "not built yet", so a fresh session would start from a wrong map of the
  codebase.

Changes:
- Record the five read routes, the points:read gating, and the 404-not-403
  mapping.
- Call out the three route-level rules that fail quietly when broken: /count and
  /search must precede /{point_id}, file_id is required on the listing, and the
  search query is Persian-folded before matching.
- Point at tests/support/point_contract.py as the place new repository behaviour
  belongs, since it runs against both the fake and real Qdrant.
- Narrow the "not built yet" list to Phases 3-6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 16:04:59 +03:30
parent ba7921dd4e
commit b25c15fefa

View File

@@ -37,9 +37,9 @@ driving `tests/e2e/test_compose_smoke.py` against a real uvicorn process, which
skips itself unless `SMOKE_BASE_URL` is set. That maps to plan 001 Phases 1-6 skips itself unless `SMOKE_BASE_URL` is set. That maps to plan 001 Phases 1-6
done. done.
Plan 002 (`/v1/points` CRUD and keyword search) is **Phase 1 done**. Landed: the Plan 002 (`/v1/points` CRUD and keyword search) is **Phases 1-2 done**. Phase 1
`PointRepository` port (`src/application/ports/point_repository.py`) with its landed the `PointRepository` port (`src/application/ports/point_repository.py`)
`Point` read model (`src/application/points/point.py`), the Qdrant adapter with its `Point` read model (`src/application/points/point.py`), the Qdrant adapter
(`src/infrastructure/qdrant/point_repository.py`), request/response schemas (`src/infrastructure/qdrant/point_repository.py`), request/response schemas
(`src/api/schemas/points.py`), and lifespan wiring. This port is **separate from (`src/api/schemas/points.py`), and lifespan wiring. This port is **separate from
`PointStorage`**, which stays exactly the two bulk operations ingestion `PointStorage`**, which stays exactly the two bulk operations ingestion
@@ -59,9 +59,31 @@ and ordered listing paginates by `order_id` value rather than offset (Qdrant
returns no page offset under `order_by`, and an offset cursor skips or repeats returns no page offset under `order_by`, and an offset cursor skips or repeats
rows under a concurrent insert). rows under a concurrent insert).
Not built yet: plan 002 Phases 2-6 — the `/v1/points` routes themselves, soft Phase 2 added the **read routes**: `GET /v1/points/{point_id}`,
delete with neighbour relinking, create/replace/patch, reorder and batch, and `GET /v1/points?file_id=...`, `GET /v1/points/count`, `GET /v1/points/search`,
the `api_request_logs`/`point_audit_events` tables — and `src/agent/`. and `GET /v1/files/{file_id}/points`, over `src/application/points/queries.py`
(`src/api/routers/points.py`). All are gated on `points:read`, which — with
`points:write` — is now in `DEFAULT_SCOPES`; `GET /v1/files/{file_id}/points`
uses `points:read` rather than `files:write`, so the scope follows the data
rather than the URL prefix. `PointNotFoundError` maps to `404` in
`src/api/errors.py`, never `403`. Three route-level rules are load-bearing:
`/count` and `/search` are declared **before** `/{point_id}` (FastAPI matches in
declaration order, so reordering them makes `/v1/points/count` a `422`),
`file_id` is **required** on the listing (the cursor is an `order_id` value and
`order_id` is unique only within one file), and `search_points` folds the query
with `normalize_persian_text` before matching, because ingestion letter-folds
content and an unfolded Arabic-keyboard query would return an empty result set
silently rather than erroring (ADR-0002).
Also worth knowing before touching the points tests: `tests/support/point_contract.py`
holds **one** scenario suite run against both `FakePointRepository` (unit) and
`QdrantPointRepository` (integration), so new repository behaviour belongs there
rather than in one of the two runners — that is what keeps the fake from drifting
more permissive than the real store.
Not built yet: plan 002 Phases 3-6 — soft delete with neighbour relinking,
create/replace/patch, reorder and batch, and the
`api_request_logs`/`point_audit_events` tables — and `src/agent/`.
Architecture decisions live in `docs/adr/` (18 ADRs plus the 0000 template; Architecture decisions live in `docs/adr/` (18 ADRs plus the 0000 template;
0001–0004 are `Accepted` — 0004 amended by 0018; 0014 is `Superseded by 0017`; 0001–0004 are `Accepted` — 0004 amended by 0018; 0014 is `Superseded by 0017`;