docs(ops): add the operator runbook and record Phase 6 as complete

Why:
- the slice had no operator documentation: no tuning guidance, no failure
  procedure, no statement of the proxy timeout requirement.

Changes:
- add docs/runbook.md: startup, deployment steps, provisioning, the INGESTION_*
  tuning table with each bound's status code, the proxy read-timeout rule,
  /healthz vs /readyz, failure investigation by real event name plus job/event
  SQL, retry semantics, and alert thresholds
- link it from the README and note provisioning there
- mark plan 001 Phase 6 done and refresh CLAUDE.md's status paragraph

Impact:
- documentation only

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Ali Zarinkolah
2026-08-20 22:26:58 +03:30
parent 7c1fe79f1c
commit 3d9269e54f
4 changed files with 339 additions and 10 deletions

View File

@@ -27,9 +27,16 @@ Also working: `tenant_domains` plus `/v1/domains` (`src/application/domains/`),
a strict per-tenant allowlist — `POST /v1/files` rejects an unregistered or
disabled `domain` with `400` before anything is written, and domain management
sits behind its own `domains:read`/`domains:write` scopes, never `files:write`.
Not built yet: `/v1/points` CRUD and keyword search (plan 002), and
`src/agent/`. That maps to plan 001 Phases 1-5 done, Phase 6 (runbook, e2e
tests, Compose smoke test) not started.
Also working: the operator runbook (`docs/runbook.md`), tenant/API-key/domain
provisioning (`uv run python -m src.cli.provision_tenant` — the third deployment
step, since nothing over HTTP can create the first tenant), a Testcontainers
e2e suite in the default pytest run (`tests/e2e/test_ingestion_slice.py`:
duplicate upload, retry after failure, tenant isolation, capacity, timeout,
parse and Qdrant failure), and the one Compose-based test — `scripts/smoke.sh`
driving `tests/e2e/test_compose_smoke.py` against a real uvicorn process, which
skips itself unless `SMOKE_BASE_URL` is set. Not built yet: `/v1/points` CRUD
and keyword search (plan 002), and `src/agent/`. That maps to plan 001 Phases
1-6 done.
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`;
@@ -285,9 +292,14 @@ belongs, not the row-level function underneath it.
- Test naming: `test_<unit>_<scenario>_<outcome>`, Arrange–Act–Assert.
- Layout mirrors architecture: `tests/unit/{application,agent}`,
`tests/integration/{postgres,minio,qdrant}`, `tests/e2e/`.
- Integration tests use **Testcontainers** (never a developer's local
services or Langfuse-owned storage/credentials) — this is the standard
automated mechanism, not Docker Compose. Isolate data per test via unique
- Integration **and e2e** tests use **Testcontainers** (never a developer's
local services or Langfuse-owned storage/credentials) — this is the standard
automated mechanism, not Docker Compose. Compose is reserved for exactly one
thing: the serialized operational smoke test of the *running web process*
(`scripts/smoke.sh`), which is gated out of `uv run pytest`. Shared container
fixtures live in `tests/support/containers.py`, registered from the root
`tests/conftest.py` via `pytest_plugins` (a non-root conftest cannot declare
it). Isolate data per test via unique
keys/queue/collection names; parallel integration execution is disabled
until fixture isolation is proven safe.
- Pytest never calls a live/paid model provider in routine runs — that's