diff --git a/CLAUDE.md b/CLAUDE.md index edcaf36..d76bc9a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -63,6 +63,18 @@ services// Host-specific runtime config and secrets live at `/opt/homelab/config//` on the target node (not in Git). Docker Compose overrides are version-controlled at `hosts//runtime//docker-compose.override.yml` in this repo and applied during deployment. +## Shared Python Libraries (packages/) + +Reusable Python packages that are not deployed services live in `packages//`. They have a `pyproject.toml` and `src/` layout but no `docker-compose.yml` or `service.yaml`. + +Services and jobs that depend on them install via Dockerfile: +```dockerfile +COPY packages// /packages// +RUN pip install /packages// +``` + +First library: `packages/kb-mail/` — KB envelope model, asyncpg DB helpers, append-only .eml archive helper. + ## Agent System Architecture The platform uses a multi-agent model with **human-in-the-loop** for destructive actions: diff --git a/docs/kb/kb-00-overview.md b/docs/kb/kb-00-overview.md index e99fb0a..b884492 100644 --- a/docs/kb/kb-00-overview.md +++ b/docs/kb/kb-00-overview.md @@ -103,6 +103,24 @@ Hardening homelabu / stabilizacja control-plane — osobny wątek. --- +## Konwencja packages/ + +Reużywalne biblioteki Python (nie deploy-osobnych serwisów) żyją w `packages//`. +Instalacja w Dockerfile serwisu/joba: `pip install /repo/packages//`. +Biblioteki nie mają `docker-compose.yml` ani `service.yaml` — to nie są serwisy. + +Pierwsza biblioteka: `packages/kb-mail/` — model koperty, helpery DB (asyncpg), helper archiwum. + +--- + ## Następny krok -Etap 1 maili: **zamroź kopertę + postaw Postgres+pgvector + szkielet repo + bulk Gmail historyczny.** Szczegóły w `kb-01-email-design.md`. +~~Etap 1 maili: zamroź kopertę + postaw Postgres+pgvector + szkielet repo.~~ **ZROBIONE** (2026-06-17). + +- ✅ `services/kb-postgres` — pgvector/pgvector:pg16 na SOLARIA (:5433), `init/001_envelope.sql` +- ✅ Zamrożona koperta — tabela `envelope` + `@dataclass Envelope` (tz-aware) +- ✅ `packages/kb-mail` — `envelope` / `db` (asyncpg) / `archive` (append-only .eml) +- ✅ 15 testów unit + 5 integration (mark `integration`, wymaga KB_TEST_DSN) + +**Etap 2: jednorazowy bulk importer Gmail** (mbox/Takeout → archiwum). +Szczegóły kolejności: `kb-01-email-design.md` §8. diff --git a/docs/sessions/2026-06-17-kb-foundations.md b/docs/sessions/2026-06-17-kb-foundations.md new file mode 100644 index 0000000..9aacdd9 --- /dev/null +++ b/docs/sessions/2026-06-17-kb-foundations.md @@ -0,0 +1,131 @@ +# Sesja 2026-06-17 — KB foundations (etap 1 maili) + +## Cel + +Zbudowanie fundamentu filaru maili: pgvector spine na SOLARIA, zamrożona koperta, pakiet domeny `kb-mail`, testy. Bez live ingestu, bez bulk Gmaila, bez indexera — sam fundament pod kolejne etapy. + +--- + +## Architektura KB — ustalona w tej sesji (docs) + +### 4 warstwy × 4 filary + rdzeń + +``` +[ 4. Agent interdyscyplinarny ] NL query + analityka (LLM) ← wspólne +[ 3. Join + Enrich ] entity resolution · graf · OwnTracks ← wspólne +[ 2. Preprocess + Index ] → pgvector / → SQL ← per filar +[ 1. Ingest ] Maile Dokumenty Zdjęcia Transakcje ← per filar +``` + +OwnTracks = **klucz czasoprzestrzenny w warstwie 3**, nie osobny filar (ciągła funkcja czas→miejsce). + +### Decyzje zamknięte + +- Spine: **Postgres + pgvector** (nie Qdrant) na SOLARIA +- Embed: **bge-m3** (multilingual, długi kontekst, lepszy pod polski) +- Maile: **dwa żywe źródła** — Fastmail (JMAP, primary) + Gmail (IMAP; konto zostaje jako śmieciowe/2FA) +- Bulk historyczny Gmail (jednorazowy) — etap 2 +- Reguła: **archiwizuj wszystko, indeksuj selektywnie** (filtr na wejściu do indeksu, nie archiwum) +- Załączniki: **II tura** (MVP = czysty tekst + nagłówki) +- Warstwa 3 startuje jako **cienki graf encji**; federacja przy zapytaniu — później +- Dokumenty: **Nextcloud** (drive, WebDAV) + **Paperless-ngx** (skany/faktury/OCR) +- Koperta: zamrożona addytywna — `id / source / ts(UTC) / geo / raw_ref / entities[]` + +### Decyzje otwarte + +- **Transakcje (filar #4):** OPEN ISSUE — agregator PSD2 (GoCardless / Tink) dla mBank + Revolut, ale zgoda SCA wygasa co 90 dni → brak pełnego bezobsługowego sync; fallback: CSV/MT940. Do decyzji przy starcie filaru #4. + +--- + +## Etap 1 — ZBUDOWANY + +### services/kb-postgres + +- Obraz: `pgvector/pgvector:pg16` +- SOLARIA, port **5433** (5432 zarezerwowany na potencjalny lokalny postgres) +- Named volume: `kb_postgres_data` +- Init SQL (`init/001_envelope.sql`): `CREATE EXTENSION vector` + tabela `envelope` +- Per-host override: `hosts/solaria/runtime/kb-postgres/docker-compose.override.yml` — `mem_limit: 4g` +- `inventory/topology.yaml` + `hosts/solaria/services.yaml` — wpisy dodane + +### Zamrożona koperta (001_envelope.sql + Envelope dataclass) + +```sql +CREATE TABLE envelope ( + id TEXT PRIMARY KEY, + source TEXT NOT NULL, -- fastmail | gmail | ... + ts TIMESTAMPTZ NOT NULL, + geo JSONB, -- null dla maili; warstwa 3 uzupełnia + raw_ref TEXT NOT NULL, -- ścieżka do .eml w archiwum + entities JSONB NOT NULL DEFAULT '[]' +); +``` + +`Envelope` dataclass waliduje `ts.tzinfo is not None` w `__post_init__`. + +### packages/kb-mail — nowa konwencja shared lib + +Lokalizacja: `packages//` (nie `services/`) — biblioteki reużywalne przez joby. +Instalacja w Dockerfile: `COPY packages/kb-mail/ /packages/kb-mail/ && pip install /packages/kb-mail/`. + +| Moduł | Co robi | +|-------|---------| +| `envelope.py` | `@dataclass Envelope`, walidacja tz-aware ts | +| `db.py` | `insert_envelope` (ON CONFLICT DO NOTHING) + `get_envelope` (asyncpg) | +| `archive.py` | `save_eml` — append-only, `asyncio.to_thread` na write, `FileExistsError` na duplikat | + +structlog JSON w każdym module (`structlog.get_logger(__name__)`); konfiguracja po stronie konsumenta (biblioteka nie konfiguruje structlog). + +### Testy + +- 15 unit testów (nie-integration): model, archiwum, sanity SQL — wszystkie zielone bez DB +- 5 integration testów (`@pytest.mark.integration`): round-trip DB, duplikat, geo, entities — wymagają `KB_TEST_DSN` +- `conftest.py`: fixture `db_conn` = asyncpg connection + BEGIN/ROLLBACK na każdy test + +### Poprawki spójności (code-review sesji) + +1. **README deploy**: pierwotna wersja miała `docker compose --env-file /opt/homelab/config/…` — pomijała override file i miała złą ścieżkę. Naprawiono: README dokumentuje trzy ścieżki (standardowa przez `deploy.sh solaria`, first-time setup `.env` obok compose file, manual z dwoma `-f`), zgodnie z `deploy-node.sh` L65–72. +2. **.gitignore**: reguła `*.env` (L3) już łapie `services/kb-postgres/.env` — potwierdzono `git check-ignore`. Nic nie dodano. + +--- + +## Higiena git + +- Cała praca w worktree `task/kb-foundations` — master czysty przez cały czas +- 2 commity na branchu po zakończeniu sesji + +--- + +## Commits + +``` +31c64d5 feat(kb-mail): fundament — pgvector spine, koperta, archiwum, pakiet domeny + docs(kb): etap 1 fundament done + session log 2026-06-17 +``` + +## Files changed (etap 1) + +``` +services/kb-postgres/docker-compose.yml +services/kb-postgres/service.yaml +services/kb-postgres/env.example +services/kb-postgres/healthcheck.sh +services/kb-postgres/init/001_envelope.sql +services/kb-postgres/README.md +hosts/solaria/runtime/kb-postgres/docker-compose.override.yml +hosts/solaria/services.yaml +inventory/topology.yaml +packages/kb-mail/pyproject.toml +packages/kb-mail/src/kb_mail/{__init__,envelope,db,archive}.py +packages/kb-mail/tests/{conftest,test_envelope,test_archive,test_db,test_migration}.py +docs/kb/kb-00-overview.md (etap 1 done, konwencja packages/) +CLAUDE.md (sekcja Shared Python Libraries) +docs/sessions/2026-06-17-kb-foundations.md +``` + +## Następny krok + +**Etap 2: jednorazowy bulk importer Gmail** — mbox/Takeout → archiwum. +Wejście: plik `.mbox` lub katalog Maildir z eksportu Google Takeout. +Wyjście: `.eml` w archiwum + wiersze `envelope` w DB. +Jako one-shot job (nie serwis), reużywa `packages/kb-mail`.