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

4.0 KiB

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.

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:

.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.

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.

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.