Files
jpmschweitzerandClaude c1cedddd08 docs: replace AGENTS.md with a CLAUDE.md written for this repo
One agent doc per repo, and it is CLAUDE.md. Two agent docs drift, and
the one nobody read is always the one holding the rule that mattered —
this repo had a CLAUDE.md whose entire content was an instruction to go
read the other file.

Composed fresh rather than reformatted. Everything factual carries over;
the structure follows what someone working here actually needs first.

Three corrections made while carrying content across:

  - The release flow instructed `git add -A`. That is denied by policy
    and sweeps in whatever else is dirty, including secrets. Now: stage
    by name.
  - The feature-branch mandate is gone. Linear history everywhere, no
    per-repo exceptions as of 2026-08-08.
  - The 8778/8089 split is stated explicitly rather than left implicit
    in two separate sections. 8778 is wakeup.sh's reload server, 8089 is
    the container — testing the wrong one silently exercises the wrong
    build. Both ports verified against wakeup.sh and the deployed stack
    before writing them down.

Adds the live double-ingestion defect (T-1) where someone touching
ingestion will see it, including the part that is not yet established:
whether it merely wastes GPU or actually corrupts Qdrant and Neo4j.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-09 00:00:10 +02:00

102 lines
4.0 KiB
Markdown

# CLAUDE.md — Library Desk
FastAPI service that ingests Wiki.js content and serves retrieval over it. Talks to
Postgres (Wiki.js DB + `LISTEN/NOTIFY`), Qdrant (vectors), Neo4j (graph), Ollama
(embeddings) and Redis (job manager). Deployed on tower-of-joy at **:8089**.
## Ports — these differ, deliberately
| | Port | How |
|---|---|---|
| Local dev | **8778** | `./wakeup.sh`, uvicorn reload mode, logs to `logs/server.log` |
| Production | **8089** | container; health at `http://192.168.86.149:8089/health` |
Testing `localhost:8089` on the dev box hits the *container*, not your reload server.
## Work tracking
Work lives in **pql**, not a markdown file. `TODO.md` was migrated and removed 2026-08-08.
```bash
pql ticket list # open work
pql plan whatsnext # next unblocked item, with context
pql ticket show T-1 # detail
pql ticket new bug "title" # file new work
```
Tickets travel with the repo — `.pql/changelog/` is committed and replayed by the git
hooks; the databases are ignored and rebuildable with `pql plan rebuild`. Do not add a
TODO section to a markdown file.
## Known live defect
**Every wiki page change is ingested twice** (T-1, proven against v1.9.1). The Dockerfile
runs `uvicorn --workers 2`, `startup_event` creates a `WikiChangeListener` per worker, and
Postgres delivers `NOTIFY` to *every* listening session. The debounce in
`wiki_change_listener.py` is an in-process dict and cannot dedupe across workers.
Relevant when touching ingestion: duplicate work reaches Ollama, Qdrant and Neo4j. Whether
it merely wastes GPU or actually corrupts state is **not yet established** — check before
assuming either.
## Working here
**Plan, act, reflect.** Outline which files you will touch and the side effects before
writing. Change in small atomic steps. Afterwards, verify: did existing tests break, and
does the new behaviour have a test?
**Test locally first — the build-deploy loop is slow.** `./wakeup.sh` auto-reloads on code
changes (not on `requirements.txt` changes). Deploy only when a feature is complete and
tested.
Run tests through the venv explicitly, to avoid environment mismatch:
```bash
.venv/bin/python -m pytest tests/
```
Copy `.env.example` to `.env` and configure Ollama, Redis, Neo4j, Qdrant and Wiki.js hosts.
## Git
- **History is linear — no merge commits.** Work on `main`, or a short-lived branch that is
fast-forwarded and deleted. (A feature-branch mandate was retired 2026-08-08 to match the
workspace convention, which now has no per-repo exceptions.)
- **Conventional Commits**: `feat:`, `fix:`, `refactor:`, `docs:`, `chore:`.
- **Atomic commits** — one logical change each.
- **Stage explicitly. Never `git add -A`** — it is denied by policy, and it sweeps in
whatever else is dirty, including secrets.
- Update `CHANGELOG.md` with every user-facing change, under `[Unreleased]` in `Added` /
`Changed` / `Fixed`.
## Releasing
Ask whether a deploy is wanted first — it is not automatic.
1. Bump the version in `pyproject.toml` (patch for fixes, minor for features).
2. Move `[Unreleased]` entries into a dated version section in `CHANGELOG.md`.
3. Stage the changed files by name, commit, tag `vX.Y.Z`, `git push origin main --tags`.
4. Gitea CI builds the image on the tag; Watchtower deploys it.
5. Verify: `curl http://192.168.86.149:8089/health`.
## Architecture
Group by **domain, not by file type**. A single large `routers/` folder is the thing to
avoid.
```text
src/
├── auth/
│ ├── router.py # endpoints
│ ├── schemas.py # pydantic models
│ ├── service.py # business logic
│ ├── dependencies.py # module-scoped dependencies
│ └── config.py # module-scoped settings
└── main.py # app entry point
```
Reference: [FastAPI best practices](https://github.com/zhanymkanov/fastapi-best-practices).
The live contract is always `http://localhost:8089/openapi.json` (70 paths) — generated from
running code, so it cannot drift the way this file can.