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>
102 lines
4.0 KiB
Markdown
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.
|