Read-only recon pod fazę 5. Ustala że architektura jest już w dużej mierze zatwierdzona (kb-m5-faza3.md §8, 2026-07-15/17) i że blokada "pełna wiki po fazie mailowej" jest dziś zdjęta (korpus sięga 2026-08-26 po naprawie incydentu 20-dni-ciszy). Dociąga trzy luki nieadresowane w tamtym szkicu: fallback chunk-id→envelope_id przy re-chunkingu, inwariant 7 (rozdział retrievalu kompilacji od retrievalu zapytań, mitygacja self-citation) i sekcję "niepewne/sprzeczne". Lista 17 encji zalążkowych z SELECT-ów na żywym korpusie (top nadawcy/domeny wśród zaembedowanych kopert, streszczenia paperless). Plan iteracji: Etap 1 = 3 strony proof (1 sesja), Etap 2 = lint + skala do 17. Decyzje operatora (a)-(h). Co: kb/audits/wiki-kompilat-recon-2026-08-26.md Co nie: implementacja — nic zdeployowane ani skommitowane poza tym dokumentem. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWrHr2z1tGhFrGUvPSx2vU
847 lines
47 KiB
Markdown
847 lines
47 KiB
Markdown
---
|
||
okf: "0.1"
|
||
type: audit
|
||
visibility: private
|
||
status: active
|
||
updated: 2026-08-26
|
||
as_of: 2026-08-26
|
||
links:
|
||
- ../phases/kb-m5-faza3.md
|
||
- ../phases/kb-m5-faza-mailowa.md
|
||
- ../services/narty27.md
|
||
- ../services/kb-query.md
|
||
- ../services/kb-site.md
|
||
- ../subsystems/kb-mail-pillar.md
|
||
- ../subsystems/kb-overview.md
|
||
- ../subsystems/kb-documents-pillar.md
|
||
- ../subsystems/recon-multiagent.md
|
||
- ../decisions/architektura-2026-07-28.md
|
||
- ../decisions/backlog-aktywne.md
|
||
- ../incidents/2026-08-26-mail-sync-20-dni-ciszy.md
|
||
- ../audits/mail-sync-2026-08-06.md
|
||
- ../runbooks/mail-sync-run.md
|
||
- ../services/job-mail-imap-sync.md
|
||
- ../services/pkg-kb-retrieval.md
|
||
---
|
||
|
||
# Recon — wiki-kompilat (faza 5), 2026-08-26
|
||
|
||
Read-only recon. Zakres: przygotowanie fazy 5 subsystemu B (KB) — syntezy wiki nad
|
||
warstwą dowodową RAG/pgvector. Zlecenie: zbadać 9 punktów (lokalizacja, bootstrap
|
||
encji, tryb kompilatora, format linku dowodowego, anty-propagacja, integracja z
|
||
kb-query, zależności/kolizje, plan iteracji, decyzje operatora) i wynieść wniosek
|
||
per punkt. **Nic nie zostało zaimplementowane, zdeployowane ani skommitowane poza
|
||
tym dokumentem.**
|
||
|
||
Dowody: repo (worktree `task/wiki-kompilat-recon`, cięty z mastera @ `4fa10f0`),
|
||
pilot `~/narty-2027/saalbach-kb` na SOLARII (lokalny odczyt), żywa baza `kb` na
|
||
PIHA przez `ssh piha 'docker exec kb-postgres psql ...'` (SELECT-y, zero zapisów),
|
||
`systemctl status` na PIHA dla stanu timerów.
|
||
|
||
**Znalezisko nadrzędne, zanim cokolwiek innego:** to nie jest recon od zera.
|
||
`kb/phases/kb-m5-faza3.md` §8 zawiera **już zatwierdzony przez operatora szkic
|
||
architektoniczny** wiki-kompilatu (decyzja 2026-07-15, opisana jako „NIE podlega
|
||
zmianie") plus rozwinięcie wykonawcze (§8.2, 2026-07-17) z konkretnymi
|
||
rozstrzygnięciami: osobne repo `kb-wiki`, struktura katalogów, format frontmattera
|
||
z blokiem `sources`, inline-przypisy `[^envelope_id#chunk_id]`, trzypoziomowa
|
||
kaskada retrievalu `wiki → summary → chunk`, kompilacja przez CC/API, lint
|
||
okresowy, pętla zwrotna. Zadanie tego reconu — tak jak je czytam — to **zweryfikować
|
||
te ustalenia w świetle tego, co się wydarzyło od 07-17** (pilot narty27, cała faza
|
||
mailowa, incydent 20 dni ciszy), dociągnąć luki, których tamten szkic nie
|
||
rozstrzygał (chunk-id vs re-chunking, self-citation, konkretna lista encji z
|
||
prawdziwych danych), i złożyć to w plan pierwszego etapu. Poniżej cytuję zamiast
|
||
odtwarzać, i mówię wprost, gdzie się zgadzam, a gdzie proponuję dociągnięcie.
|
||
|
||
---
|
||
|
||
## 1. Stan korpusu na dziś (2026-08-26) — czy blokada z faza3 jest zdjęta
|
||
|
||
`kb/phases/kb-m5-faza3.md:628`, cytat ze szkicu operatora: *„Pełna wiki po fazie
|
||
mailowej (przyrostówka) — wcześniej kompilat byłby fotografią przeszłości."*
|
||
`kb/audits/mail-sync-2026-08-06.md` §7 nazywa to *jedyną znalezioną jawną
|
||
zależność „X czeka na przyrostówkę" w całym KB*.
|
||
|
||
Zmierzone dziś na żywej bazie:
|
||
|
||
```sql
|
||
SELECT source, max(ts)::date AS latest, count(*) FROM envelope GROUP BY 1;
|
||
```
|
||
|
||
| source | latest | count |
|
||
|---|---|---|
|
||
| gmail | **2026-08-26** | 227 280 |
|
||
| fastmail | **2026-08-26** | 105 |
|
||
| paperless | 2026-07-13 | 191 |
|
||
|
||
```sql
|
||
SELECT count(*) FROM document_chunk WHERE embedding IS NULL AND excluded_reason IS NULL;
|
||
-- → 0
|
||
```
|
||
|
||
Korpus mailowy sięga **dziś**, nie 2026-06-19 jak w audycie 08-06 — incydent
|
||
[20 dni ciszy](../incidents/2026-08-26-mail-sync-20-dni-ciszy.md) naprawiony tego
|
||
samego dnia co to zlecenie domknął przyrostówkę: kursor dogonił IMAP, 548
|
||
zaległych kopert wchłonięte, zero błędów. `kb-mail-sync.timer` (co godzinę) i
|
||
`kb-ingest.timer` (co 2h, embed) oba `active (waiting)` na PIHA, żywe od
|
||
2026-08-06, bez przerwy odkąd monitoring realnie działa (od tego samego
|
||
incydentu). Embed backlog = **0** — nic nie czeka na wektor.
|
||
|
||
**Wniosek: blokada z faza3 jest zdjęta dziś, nie wcześniej.** To jest właściwy
|
||
moment na ten recon — zrobiony tydzień wcześniej trafiłby na korpus wciąż
|
||
urwany, a wiki-kompilat skompilowałby na trwałe fotografię z czerwca. Warto
|
||
odnotować kruchość tego stanu: przyrostówka działa dzięki jednemu `chown` i
|
||
jednemu `/-/reload`, oba ręczne, oba bez automatyzacji (R1/R2 z incydentu —
|
||
poprawna własność katalogów przy `sudo`-runach i reload reguł Prometheusa w
|
||
deployu — zostają otwarte). Faza 5 dziedziczy to ryzyko: gdyby przyrostówka
|
||
znów ucichła cicho, wiki-kompilat kompilowałby dalej z coraz starszej wody, bez
|
||
sygnału. Patrz §7 niżej (interakcja z monitoringiem `KbMailSyncStale`).
|
||
|
||
**191 kopert paperless** nie rosną od 2026-07-13 — to osobny fakt: paperless nie
|
||
ma przyrostówki (dokumenty trafiają tam ręcznie), niezwiązany z blokadą mailową.
|
||
Nie wstrzymuje fazy 5.
|
||
|
||
---
|
||
|
||
## 2. Punkt 1 — Lokalizacja wiki: `kb/wiki/` w repo vs osobne repo
|
||
|
||
### Co już zdecydowano
|
||
|
||
`kb-m5-faza3.md` §8.2: *„Repozytorium (rozstrzygnięcie punktu 6 szkicu): osobne
|
||
repo `kb-wiki` — decyzja 7, uzasadnienie w §2. Klon roboczy na PIHA:
|
||
`/opt/homelab/data/kb-wiki/`."* — i dalej: *„D7 — rekomendacja: osobne
|
||
`kb-wiki`"* w podsumowaniu (§12). To jest już podjęta decyzja, nie propozycja.
|
||
|
||
### Weryfikacja na wzorcu pilota
|
||
|
||
narty27 (`~/narty-2027/saalbach-kb`) **potwierdza ten wzorzec empirycznie**:
|
||
`kb/services/narty27.md` — *„Content is personal and lives outside the repo… It
|
||
is never committed — not to this repo, not to any other."* Źródło leży w osobnym
|
||
lokalnym gicie na SOLARII, poza `homelab-codex`, infra (`services/narty27`)
|
||
zostaje w `homelab-codex`. Dokładnie ten sam split, jaki faza3 projektuje dla
|
||
`kb-wiki`: **treść osobno, infrastruktura hostująca w repo.**
|
||
|
||
### Rekomendacja: potwierdzam D7 (osobne repo), z jednym doprecyzowaniem
|
||
|
||
Zgadzam się z decyzją bez zmian co do kierunku. Powody, które faza3 już podała
|
||
i które podtrzymuję:
|
||
|
||
1. **Cykl życia inny niż kodu infry.** Commity do `kb-wiki` będą częste,
|
||
drobne, generowane maszynowo (`compile: <strona> ← <envelope_ids>`) —
|
||
zalałyby historię `homelab-codex-ws`, gdzie commituje operator ręcznie i
|
||
rzadko (`CLAUDE.md`: *„Primary control node — only node where commits are
|
||
made"*).
|
||
2. **Widoczność `visibility` per strona nie mapuje się na model `homelab-codex`.**
|
||
`check_okf.py` tego repo ma zamkniętą listę `TYPES` i regułę `POINTER_GLOBS`/
|
||
`EXCLUDE_DIRS` dopasowaną do tego, co tu jest — dodanie setek stron-encji
|
||
(firmy, osoby, sprawy) do `kb/` zaszumiłoby indeks tego repo i pomieszałoby
|
||
dwa różne reżimy publikacji (kb-site publikuje `visibility: public` z `kb/`;
|
||
`kb-wiki` potrzebuje **własnego**, bo część stron — dane osobowe, umowy,
|
||
ludzie — nigdy nie powinna nawet teoretycznie wpaść w tę samą ścieżkę co
|
||
dokumentacja infrastruktury).
|
||
3. **Rozmiar.** Korpus mailowy to 227k+ kopert; nawet skromna kompilacja
|
||
(setki stron) to inny rząd wielkości commitów niż `homelab-codex-ws` widział
|
||
kiedykolwiek. Osobne repo izoluje ten wzrost od repo, które operator
|
||
przegląda i na którym pracują agenty infrastrukturalne.
|
||
|
||
**Doprecyzowanie względem faza3:** szkic nie rozstrzyga *dostępu CC w
|
||
worktree*. `homelab-codex-ws` ma wzorzec `agent.sh new` → worktree per task,
|
||
każdy dostaje pełne repo `homelab-codex-ws` sklonowane z gita. `kb-wiki` **nie
|
||
jest** tym repo — sesja kompilująca stronę potrzebuje **dwóch** klonów
|
||
jednocześnie: `homelab-codex-ws` (żeby czytać `packages/kb-retrieval`, DSN,
|
||
`.env` na PIHA) i `kb-wiki` (żeby pisać stronę). To nie jest problem dla
|
||
worktree pojedynczej sesji CC odpalonej ręcznie na PIHA/SOLARII z dwoma
|
||
katalogami obok siebie — **jest** problemem dla wzorca `scripts/dev/agent.sh
|
||
new`, który zna tylko jedno repo. Rekomendacja: kompilacja wiki **nie** idzie
|
||
przez `agent.sh`-owe worktree `homelab-codex-ws` — to osobny tryb pracy
|
||
(sesja CC z dwoma katalogami roboczymi, jak dziś narty27 na SOLARII), do
|
||
zapisania w `kb-wiki/_meta/conventions.md`, nie w tym repo.
|
||
|
||
### Publish.sh i public/private per strona
|
||
|
||
`kb-site` (`services/kb-site/`) publikuje wyłącznie `kb/**/*.md` z
|
||
`visibility: public`, fail-closed, przez token w URL (`kb-e2a24af3.okit.pl`).
|
||
`kb-wiki` leżący poza `kb/` **nie wejdzie** do tego pipeline'u automatycznie —
|
||
i to dobrze, bo strony wiki (dane o ludziach, umowach, finansach) mają zupełnie
|
||
inny profil ryzyka publikacji niż dokumentacja infrastruktury. Jeśli/kiedy
|
||
pojawi się potrzeba publicznej (lub choćby LAN-owej) wystawki `kb-wiki`, to
|
||
osobny generator + osobny serwis (wzorzec `narty27`: nginx + named volume,
|
||
`exposure: private` domyślnie), **nie** rozszerzenie `gen_pages.py`. Na tym
|
||
etapie (proof-of-concept, 3–10 stron) nie ma potrzeby stawiać hostingu w ogóle
|
||
— `git log`/`cat` na PIHA wystarczy do przeglądu.
|
||
|
||
---
|
||
|
||
## 3. Punkt 2 — Bootstrap: analiza korpusu i lista encji zalążkowych
|
||
|
||
### Zapytania i wyniki (żywa baza, 2026-08-26)
|
||
|
||
**Wolumen per rok/źródło** (`envelope`, pełne dane w §1 wyżej dla świeżości;
|
||
poniżej rozkład historyczny):
|
||
|
||
```sql
|
||
SELECT source, date_trunc('year', ts) AS yr, count(*) FROM envelope GROUP BY 1,2 ORDER BY 1,2;
|
||
```
|
||
|
||
Szczyt 2011–2012 (~23k/rok — dawny pracodawcy przez `outbox.pl`), potem
|
||
stabilne 9–13k/rok 2013–2025. 2 559 kopert `ts`=epoch 1970 (daty nieodzyskane,
|
||
opisane w `kb-overview.md` backlog).
|
||
|
||
**Top nadawcy wśród kopert, które realnie mają zaembedowany, nie-wykluczony
|
||
chunk** (czyli faktycznie trafiają w wyniki `/search` — to jest właściwe sito
|
||
pod encje, nie surowy wolumen, który zdominowany jest przez newslettery
|
||
wykluczone z indeksu):
|
||
|
||
```sql
|
||
SELECT h->'from'->>'name', h->'from'->>'address', count(DISTINCT e.id),
|
||
min(e.ts)::date, max(e.ts)::date
|
||
FROM envelope e
|
||
JOIN jsonb_array_elements(e.entities) h ON h->>'type' = 'headers'
|
||
WHERE EXISTS (SELECT 1 FROM document_chunk c
|
||
WHERE c.envelope_id = e.id AND c.embedding IS NOT NULL AND c.excluded_reason IS NULL)
|
||
GROUP BY 1,2 ORDER BY 3 DESC LIMIT 40;
|
||
```
|
||
|
||
| Nadawca | Adres | Koperty | Zakres |
|
||
|---|---|---|---|
|
||
| oskar kapala | oskar.kapala@gmail.com | 10 021 | 2005–2020 |
|
||
| Oskar Kapala | oskar.kapala@outbox.pl | 6 399 | (epoch)–2014 |
|
||
| Kasia Lorenc-Kapala | katalia@gmail.com | 3 385 | 2007–2026 |
|
||
| Oskar Kapala | oskar.kapala@gmail.com | 2 910 | 2010–2026 |
|
||
| System Synergia | robot2@robot.librus.pl | 2 818 | 2018–2026 |
|
||
| Allegro | powiadomienia@allegro.pl | 2 170 | 2008–2026 |
|
||
| Tomasz Anuszewski | tomasz.anuszewski@outbox.pl | 1 913 | 2011–2012 |
|
||
| Dziennik Bankier.pl (×2 adresy) | …bankier.pl | 1 736 + 1 676 | 2007–2014 |
|
||
| RSW_TECH_TEAM | rsw_tech_team@outbox.pl | 1 628 | 2011–2012 |
|
||
| Groupon | info@news.groupon.pl | 1 447 | 2011–2012 |
|
||
| Miron Mironiuk | m@cosmose.co | 1 126 | 2015–2016 |
|
||
| kontakt@mbank.pl | | 1 099 | 2005–2026 |
|
||
| Jan Boboli | jan.boboli@outbox.pl | 969 | 2011–2013 |
|
||
| Paweł Cesar Sanjuan Szklarz | paweld2@gmail.com, pawel@cosmose.co | 757+555+418+376 | 2005–2024 |
|
||
| InPost | info@paczkomaty.pl | 722 | 2017–2026 |
|
||
| Bogumil Jakubiak | B.Jakubiak@icm.edu.pl | 686 | 2004–2009 |
|
||
| sOKratis Liliana Banaszak | liliana.banaszak@sokratis.pl | 679 | 2009–2026 |
|
||
| Strava | update.strava.com / strava.com | 591+395 | 2017–2026 |
|
||
|
||
**Domeny** (§2, po odsianiu marketingu/newsletterów po heurystyce
|
||
`noreply|notification|robot|mailer`): `outbox.pl` (39,4k — domena prywatna
|
||
operatora, alias `oskar.kapala@outbox.pl` + koledzy z tej samej domeny =
|
||
prawdopodobnie dawny pracodawca/hosting), `gmail.com`, `cosmose.co` (2 982,
|
||
firma — Miron Mironiuk + Paweł Szklarz), `icm.edu.pl`/`students.mimuw.edu.pl`
|
||
(uczelnia/ICM UW), `mbank.pl`/`bankier.pl` (finanse), `sokratis.pl`.
|
||
|
||
**Paperless — 191 kopert, w większości streszczone** (`document_summary`,
|
||
162 `claude-haiku-4-5` + 155 `gemma3:12b`, prawie 1:1 pokrycie). Próbka
|
||
streszczeń pokazuje wyraźne klastry tematyczne:
|
||
|
||
| Klaster | Liczba (próbka 40 najnowszych) | Przykład |
|
||
|---|---|---|
|
||
| FIRST LEGO League / Fundacja Future Minds | 8 | zgody na wizerunek, karty wyników, harmonogramy |
|
||
| Regulaminy OLX (pakiety, ochrona kupującego, ads) | 5 | umowa serwisowa |
|
||
| Revolut (krypto, regulaminy, cennik) | 4 | umowa finansowa |
|
||
| Spółdzielnia Mieszkaniowa „Przy Metrze" | 2 | RODO, dane kontaktowe |
|
||
| Ubezpieczenia (PZU OC, itp.) | 2 | polisa |
|
||
| Wynajem auta (Dollar, Madryt) | 2 | rezerwacja |
|
||
| Faktura (ZigBee, Oskar Kapała Rozwiązania IT — działalność gospodarcza) | 1 | dowód zakupu |
|
||
|
||
### Kryterium kolejności
|
||
|
||
Trzy sygnały, w tej kolejności ważności:
|
||
|
||
1. **Obecność w indeksowanym (nie-wykluczonym) korpusie** — encja musi mieć
|
||
realne pokrycie w `document_chunk` z wektorem, inaczej strona kompiluje się
|
||
z pustki. To jedyny twardy filtr (zastosowany w zapytaniu wyżej).
|
||
2. **Wielowątkowość źródeł** — encje, które łączą paperless (dokument formalny:
|
||
umowa, polisa, regulamin) *i* gmail (korespondencja o tej samej sprawie),
|
||
są lepszymi kandydatami na `sprawy`/`umowy` niż encje widoczne tylko w
|
||
jednym źródle — bo demonstrują dokładnie to, co ma udowodnić wiki-kompilat
|
||
(Karpathy-wzorzec: strona syntezuje, nie kopiuje jednego dokumentu).
|
||
3. **Rozstrzygalność, nie wolumen.** `outbox.pl`/`Tomasz Anuszewski`/`Jan
|
||
Boboli`/`RSW_TECH_TEAM` mają duży wolumen (2011–2012), ale to zamknięty w
|
||
czasie epizod (prawdopodobnie dawny pracodawca) — dobry kandydat na
|
||
`podmiot`, zły na pierwszy PoC, bo wymaga ustalenia kontekstu (kim była ta
|
||
firma), którego recon nie ma. Odłożone na drugą turę, nie na pierwszą.
|
||
|
||
### Proponowana lista 10–20 encji zalążkowych
|
||
|
||
| # | Strona | Typ | Uzasadnienie (z danych) |
|
||
|---|---|---|---|
|
||
| 1 | `fll-2025-26` | sprawa | 8/40 streszczeń paperless; żywa sprawa (2026), krzyżuje szkołę + fundację + dziecko |
|
||
| 2 | `future-minds` (fundacja) | podmiot | organizator FLL, NIP w danych, kontakt jawny w treści dokumentów |
|
||
| 3 | `spoldzielnia-przy-metrze` | podmiot | nieruchomość, RODO + dane kontaktowe, aktywna (bieżąca korespondencja o pożarówce 2026) |
|
||
| 4 | `revolut` | podmiot | 4 dokumenty regulaminowe w próbce, konto aktywne (kontekst kryptowalut) |
|
||
| 5 | `mbank` | podmiot | 1 099 kopert 2005–2026, ciągłość 20 lat, kontakt@mbank.pl + dziennik bankier.pl (powiązana marka) |
|
||
| 6 | `pzu` | podmiot | kandydat wymieniony wprost w szkicu operatora (§8.1 przykład), polisa OC w paperless |
|
||
| 7 | `cosmose` (firma) | podmiot | 2 982 kopert, dwóch nazwanych korespondentów (Mironiuk, Szklarz) — test cross-osoba w jednym podmiocie |
|
||
| 8 | `miron-mironiuk` | osoba | 1 126 kopert, jeden zidentyfikowany adres, powiązanie z `cosmose` |
|
||
| 9 | `pawel-cesar-sanjuan-szklarz` | osoba | 4 warianty adresu/nazwy (757+555+418+376 kopert) — dobry test entity resolution na aliasach tej samej osoby |
|
||
| 10 | `librus-synergia` | temat | 2 818 kopert 2018–2026, szkoła dzieci, ciągły strumień — test strony-tematu o dużym, jednorodnym źródle |
|
||
| 11 | `inpost-paczkomaty` | temat | 722+681 (dwa nadawcy tej samej usługi) — test scalania dwóch adresów jednej usługi w jedną stronę |
|
||
| 12 | `strava` | temat | 591+395, dwa adresy tej samej usługi, 2017–2026 ciągłe |
|
||
| 13 | `bogumil-jakubiak` | osoba | ICM UW, 2004–2009 — zamknięty epizod, test strony `status: archived` |
|
||
| 14 | `wynajem-samochodow` (Dollar/Madryt) | temat | rezerwacja 2026, dwa powiązane dokumenty paperless (potwierdzenie różnymi kanałami tej samej rezerwacji) — test dedupu treści |
|
||
| 15 | `dzialalnosc-gospodarcza` (Oskar Kapała Rozwiązania IT) | podmiot | faktura w paperless, własna działalność — inny rejestr niż korespondencja prywatna |
|
||
| 16 | `sokratis` | podmiot | 679 kopert 2009–2026, ciągłość 17 lat, nazwany kontakt (Liliana Banaszak) |
|
||
| 17 | `allegro` | temat | 2 170 kopert 2008–2026 — najdłuższa ciągła relacja handlowa w korpusie |
|
||
| 18–20 | (druga tura, po pierwszej weryfikacji) | — | `outbox.pl`/dawny pracodawca po ustaleniu kontekstu; `PZU`↔`mbank` cross-link jako pierwszy test grafu `sprawy` łączących dwa `podmioty` |
|
||
|
||
17 pozycji zamiast równych 20 — reszta świadomie zostawiona na drugą turę
|
||
zamiast dopełniana sztucznie (Bogumil Jakubiak/ICM to jedyny archiwalny/
|
||
zamknięty przypadek na liście — reszta to żywe encje; warto mieć chociaż jeden
|
||
przykład `status: archived` w PoC, żeby lint (inwariant 3) miał co sprawdzać
|
||
na obu stanach od pierwszego dnia).
|
||
|
||
---
|
||
|
||
## 4. Punkt 3 — Tryb kompilatora: batch / pipeline / hybryda
|
||
|
||
### Co już zdecydowano
|
||
|
||
Faza3 §8.2, inwariant 2: *„Kompilację i lint robi CC/zewnętrzne API… Sesja
|
||
CC/API dostaje wyniki retrievalu dla encji… pisze/aktualizuje stronę,
|
||
commituje do kb-wiki."* To opisuje **batch przez sesje CC** — potwierdzone też
|
||
przez operatora w zleceniu tego reconu („Utrzymanie: CC/API — lokalny model za
|
||
słaby — rozstrzygnięte"). Nie podważam tego kierunku; on jest już przesądzony.
|
||
Pytanie, które faza3 zostawia otwarte, to **rytm**: czy kompilacja jest
|
||
wyłącznie ręcznie wyzwalanym batchem, czy dostaje automatyczny trigger, i jak
|
||
zamyka się pętla zwrotna (inwariant 4: *„dobre odpowiedzi z zapytań wracają do
|
||
wiki jako nowe strony"*).
|
||
|
||
### Trzy tryby, ocenione pod kątem tego, co ta faza faktycznie robi
|
||
|
||
**(A) Czysty batch (sesja CC, ręcznie wyzwalana).** Operator albo agent
|
||
odpala sesję z listą encji, sesja robi N zapytań do `hybrid_retrieve`/
|
||
`cascade_retrieve`, pisze/aktualizuje strony, commituje. Koszt: **przewidywalny
|
||
i widoczny w moment wywołania** — tyle, ile ta jedna sesja zużyje tokenów API.
|
||
Częstotliwość: kiedy operator zdecyduje (np. raz na kilka sesji roboczych KB).
|
||
|
||
**(B) Job z pipeline'em eskalacji (per-query, automatyczny).** Każde
|
||
zapytanie do `kb-query` /search, które trafia próg (`dist`, heurystyki),
|
||
eskaluje do API i **automatycznie** zapisuje/aktualizuje stronę wiki z
|
||
odpowiedzi. To bezpośrednio realizuje pętlę zwrotną (inwariant 4) bez udziału
|
||
człowieka w każdym pojedynczym zapisie.
|
||
|
||
**(C) Hybryda: bootstrap batch, utrzymanie pipeline.** Pierwsza fala stron
|
||
(PoC, potem szersza kompilacja korpusu) idzie trybem (A) — bo trzeba pokryć
|
||
setki/tysiące encji z pustego stanu, co jest z natury wsadowe i wymaga
|
||
przeglądu jakości. Po ustabilizowaniu wiki, dopisywanie/aktualizacja idzie
|
||
trybem (B), zdarzeniowo, przy okazji zapytań `/search`, które i tak przechodzą
|
||
przez `kb-query`.
|
||
|
||
### Rekomendacja: (C), z (B) odłożonym poza zakres tej fazy
|
||
|
||
Uzasadnienie:
|
||
|
||
1. **Skala nie pozwala na (B) od startu.** `kb-query` dziś **nie robi syntezy
|
||
odpowiedzi** — to jawnie zapisane w `kb/services/kb-query.md` (*„This is a
|
||
search API, not chat: no answer synthesis over results, that's phase 5"*).
|
||
Automatyczny zapis strony z „dobrej odpowiedzi" wymaga najpierw tej
|
||
syntezy (LLM nad wynikami retrievalu) — a to jest osobny, jeszcze
|
||
niezbudowany komponent, w planie `kb-m5-faza-mailowa.md` §11 zapisany jako
|
||
*„Synteza odpowiedzi + polityka eskalacji | faza 5 | po fazie 3/4"*.
|
||
Innymi słowy: (B) zakłada istnienie warstwy, którą dopiero ta faza ma
|
||
zbudować. Zaczynanie od (B) to budowanie pipeline'u nad nieistniejącym
|
||
sygnałem.
|
||
2. **Bootstrap wymaga przeglądu, nie automatyzacji.** 17 stron z §3 to
|
||
pierwsza próba entity resolution na korpusie, który nigdy wcześniej nie był
|
||
tak czytany — pierwsze przebiegi *będą* mylić się (aliasy tej samej osoby
|
||
pod różnymi adresami, jak `paweld2@gmail.com` vs `pawel@cosmose.co`, patrz
|
||
#9). Batch z operatorem czytającym diff przed commitem jest tu właściwym
|
||
trybem; automatyczny zapis bez przeglądu ryzykuje właśnie to, co inwariant
|
||
3 (lint) ma łapać *po fakcie* — lepiej łapać *przed* pierwszym zapisem.
|
||
3. **Koszt.** Batch: koszt jednej sesji, znany z góry (faza3 §11 szacuje
|
||
kompilację 3–5 stron proof jako „pomijalna" wobec ~1.5–4 USD pilota
|
||
streszczeń). Pipeline per-query: koszt proporcjonalny do ruchu na
|
||
`kb-query`, dziś zerowego (serwis bez UI logowania, brak ruchu
|
||
produkcyjnego opisanego w żadnym audycie) — budowanie licznika kosztów pod
|
||
ruch, którego jeszcze nie ma, jest przedwczesne.
|
||
|
||
**Wniosek: w tej fazie — wyłącznie (A)/(C)-batch.** Pipeline eskalacji (B) to
|
||
konsekwencja *syntezy odpowiedzi*, która sama jest poza zakresem tego reconu
|
||
(`kb-m5-faza-mailowa.md` §11) — wpisuję go do §8 (plan iteracji) jako **osobny,
|
||
późniejszy etap**, nie część pierwszego kroku.
|
||
|
||
### Trigger i częstotliwość dla batcha
|
||
|
||
Rekomendacja: **ręczny**, przywiązany do sesji roboczych KB, nie do ticku
|
||
ingestu. Uzasadnienie: `kb-ingest`/`kb-mail-sync` tykają bez nadzoru
|
||
(systemd timer) właśnie dlatego, że są deterministyczne i tanie (lokalny
|
||
embed). Kompilacja wiki zużywa API i wymaga jakościowego osądu przy każdym
|
||
zapisie na tym etapie — sprzeczne z „bez nadzoru". Automatyczny trigger „po
|
||
ticku ingestu" dodałby niekontrolowany koszt API do procesu, który dziś nie ma
|
||
żadnego budżetu ani limitu (`packages/kb-retrieval`/`kb-query` nie mają
|
||
pojęcia o kosztach API — nigdzie w repo nie ma licznika wydatków Anthropica).
|
||
|
||
---
|
||
|
||
## 5. Punkt 4 — Format linku dowodowego i odporność na re-chunking
|
||
|
||
### Co już zdecydowano
|
||
|
||
Faza3 §8.2 daje **dokładną składnię**, cytuję w całości bo to bezpośrednia
|
||
odpowiedź na pytanie zlecenia:
|
||
|
||
```markdown
|
||
---
|
||
sources:
|
||
- envelope_id: paperless:119
|
||
chunks: [4211, 4213] # document_chunk.id
|
||
- envelope_id: "CABtrY-...@mail.gmail.com"
|
||
chunks: [] # koperta bez chunków (np. sam manifest/headers)
|
||
---
|
||
|
||
Treść… składka 1 234 zł/rok [^paperless:119#4212].
|
||
```
|
||
|
||
Dwa poziomy: `sources:` we frontmatterze (agregat, dla lintu — sprawdza czy
|
||
*strona* ma dowody) i inline `[^envelope_id#chunk_id]` przy każdym fakcie
|
||
liczbowym (dla spot-checku — sprawdza czy *zdanie* ma dowód). To jest już
|
||
odpowiedź na „envelope_id vs chunk-id": **oba, na dwóch poziomach
|
||
granularności**, nie albo-albo.
|
||
|
||
### Luka, którą faza3 zostawia: `document_chunk.id` (bigint PK) nie jest stabilny
|
||
|
||
To jest realny problem, którego szkic nie adresuje wprost. `document_chunk.id`
|
||
to `bigint GENERATED … nextval(...)` — PK generowany przy insercie. Re-chunking
|
||
(zmiana `chunk_size`/`chunk_overlap`, zmiana modelu embeddingu, poprawka
|
||
chunkera) w obecnym pipeline'u (`jobs/mail-body-ingest`, `kb/phases/kb-m5-faza3.md`
|
||
§1.6, Decyzja 1) **usuwa i wstawia na nowo** wiersze `document_chunk` dla
|
||
przetwarzanej koperty — nowe `id`. Strona wiki napisana dziś z przypisem
|
||
`[^paperless:119#4212]` po re-chunkowaniu paperless wskazuje na `id`, który już
|
||
nie istnieje.
|
||
|
||
Sprawdziłem: nie ma w repo żadnego mechanizmu "kotwiczenia" chunka niezależnego
|
||
od PK (np. hash treści, offset w dokumencie źródłowym). `chunk_index` (kolumna
|
||
w `document_chunk`) jest stabilniejszy semantycznie (n-ty fragment tej koperty)
|
||
ale też **nie** jest gwarantowany stabilny — zmiana `chunk_size` przesuwa
|
||
granice i renumeruje wszystkie chunki tej koperty.
|
||
|
||
### Ocena: to nie jest luka krytyczna, bo lint już ją łapie — ale warto dociągnąć degradację
|
||
|
||
Inwariant 3 (lint) faza3 §8.2 już projektuje mechanizm wykrywania: *„spot-check
|
||
N losowych przypisów `[^...]` vs treść chunka w bazie"*. Martwy `chunk_id` po
|
||
re-chunkingu to dokładnie to, co ten spot-check złapie — `SELECT text FROM
|
||
document_chunk WHERE id = 4212` zwróci zero wierszy, lint zgłasza to jako
|
||
znalezisko w `_meta/lint-reports/`. **Mechanizm wykrywający już istnieje w
|
||
projekcie**, więc rekomendacja to nie „zbuduj nowy system kotwiczenia" tylko
|
||
**doprecyzowanie zachowania przy trafieniu martwego linku**:
|
||
|
||
**Rekomendacja — dwupoziomowy fallback, nie twardy błąd:**
|
||
|
||
1. Link **prymarny** to `[^envelope_id#chunk_id]` — precyzyjny, ale kruchy.
|
||
2. Gdy `chunk_id` nie istnieje (po re-chunkingu), **fallback do
|
||
`envelope_id`** — dowód wskazuje na kopertę jako całość ("ten fakt pochodzi
|
||
z tej wiadomości", zamiast "z tego dokładnie fragmentu"). Zdegradowane, ale
|
||
wciąż audytowalne, bo `envelope_id` **jest** stabilny (`envelope.id` to
|
||
Message-ID albo `sha256-…`, nigdy nie zmienia się po insercie —
|
||
`kb-mail-pillar.md` §3, kontrakt zamrożony).
|
||
3. Lint raportuje **liczbę zdegradowanych przypisów** jako osobną metrykę w
|
||
`_meta/lint-reports/` (nie tylko listę zerwanych linków) — to jest wczesny
|
||
sygnał "re-chunking coś ruszył, strony trzeba przejrzeć", zanim ktokolwiek
|
||
zauważy to ręcznie.
|
||
4. **Re-chunking = trigger do przeglądu, nie do automatycznej naprawy.**
|
||
Automatyczne przepisanie `chunk_id` na nowy najbliższy semantycznie chunk
|
||
wymagałoby ponownego embeddingu i porównania — kosztowne i ryzykowne (może
|
||
przypisać fakt do złego fragmentu po cichu). Degradacja do `envelope_id` +
|
||
ręczny przegląd przy najbliższej kompilacji tej strony jest tańsza i
|
||
bezpieczniejsza.
|
||
|
||
Konsekwencja praktyczna: re-chunking całego korpusu mailowego (227k kopert)
|
||
jest zdarzeniem rzadkim i świadomym (widziane już raz — Decyzja 1 fazy3,
|
||
`UNIQUE+model`), nie czymś co dzieje się przy okazji. Skala degradacji przy
|
||
takim zdarzeniu jest więc *skokowa i rzadka*, nie ciągła — fallback +
|
||
metryka w locie wystarczą, nie trzeba temu poświęcać osobnej infrastruktury.
|
||
|
||
---
|
||
|
||
## 6. Punkt 5 — Anty-propagacja: datowanie, sprzeczności, aktualizacja
|
||
|
||
### Co już zdecydowano
|
||
|
||
Frontmatter niesie `compiled_at`/`updated_at` (datowanie na poziomie strony).
|
||
Inwariant 3: lint okresowy łapie *„sprzeczności między stronami (fakty o tej
|
||
samej encji)"*. Inwariant 4: pętla zwrotna dopisuje nowe strony/aktualizacje z
|
||
dobrych odpowiedzi zapytań.
|
||
|
||
### Luki, które faza3 nie precyzuje i które zlecenie wprost pyta
|
||
|
||
**(1) Sekcja „niepewne/sprzeczne" na poziomie strony.** Dziś nic w
|
||
projektowanym frontmatterze/strukturze nie ma miejsca na "wiem, że dwa źródła
|
||
mówią co innego, i to jest w rękopisie widoczne". Lint wykrywa sprzeczność
|
||
*między stronami* — ale co z sprzecznością *wewnątrz* jednej kompilacji (np.
|
||
paperless:119 podaje jedną kwotę składki, wcześniejszy mail podaje inną —
|
||
podwyżka, błąd, czy różne polisy)?
|
||
|
||
**Rekomendacja:** sekcja `## Niepewne / sprzeczne` jako **konwencja treści**
|
||
strony (nie pole frontmattera — to jest proza, nie metadane), umieszczana
|
||
zawsze na końcu strony, gdy występuje. Format:
|
||
|
||
```markdown
|
||
## Niepewne / sprzeczne
|
||
|
||
- Składka OC: [^paperless:24#N] podaje kwotę roczną bez liczby, [^<msg-id>#M]
|
||
(mail 2025-03) wspomina "1 450 zł" — nie jest jasne, czy to ta sama polisa
|
||
czy poprzedni rok. **Nie rozstrzygane automatycznie** — do potwierdzenia
|
||
przy następnej kompilacji tej strony.
|
||
```
|
||
|
||
Lint (spot-check) traktuje tę sekcję jako **oczekiwaną**, nie jako defekt —
|
||
strona bez tej sekcji nie jest "lepsza", strona z niewykrytą sprzecznością w
|
||
treści głównej (fakt podany jako pewny, gdy dowody się rozjeżdżają) jest
|
||
gorsza. To jest różnica między *ukrywaniem* niepewności (zły kompilat) a jej
|
||
*nazwaniem* (dobry kompilat) — i to jest właśnie mechanizm, który ma
|
||
zapobiegać propagacji błędu: LLM kompilujący wprost pisze "nie wiem", zamiast
|
||
zgadywać i zamrażać zgadywankę jako fakt.
|
||
|
||
**(2) Polityka aktualizacji przy nowym, sprzecznym dowodzie.** Scenariusz ze
|
||
zlecenia: nowy mail przeczy istniejącej stronie. Dwie opcje:
|
||
|
||
- **Nadpisać** stronę nowym stanem (traktować najnowszy dowód jako
|
||
prawdziwy) — ryzyko: jeśli nowy mail jest błędny/nieaktualny/o innej
|
||
sprawie, kasuje się poprawną wcześniejszą treść bez śladu.
|
||
- **Dopisać do „Niepewne/sprzeczne"** i **nie nadpisywać** faktu w treści
|
||
głównej, dopóki sprzeczność nie zostanie rozstrzygnięta (przez kolejną
|
||
kompilację z jeszcze jednym dowodem, albo ręcznie przez operatora).
|
||
|
||
**Rekomendacja: druga opcja, z jednym wyjątkiem.** Domyślnie *dopisz, nie
|
||
nadpisuj* — bo strona ma być audytowalnym destylatem, nie najświeższą
|
||
migawką, a "nowszy mail" nie zawsze znaczy "poprawniejszy" (może być o innej
|
||
polisie, literówka nadawcy, spam). Wyjątek: gdy nowy dowód jest **tego samego
|
||
typu i wprost aktualizuje poprzedni** (np. "nowy cennik od 1.09" jawnie
|
||
zastępujący poprzedni, nie sprzeczny z nim) — to nie jest sprzeczność, to
|
||
aktualizacja, i idzie do treści głównej z nową datą `updated_at`, a stary fakt
|
||
przechodzi do sekcji „Historia" (jeśli strona jej potrzebuje) albo po prostu
|
||
znika, zastąpiony — to jest zwykła kompilacja, nie przypadek antypropagacji.
|
||
Rozróżnienie "to jest aktualizacja" vs "to jest sprzeczność" robi **sesja
|
||
kompilująca (CC/API)**, nie automat — zgodnie z inwariantem 2 (kompilacja to
|
||
zadanie dla modelu, nie dla reguły).
|
||
|
||
**(3) Datowanie twierdzeń w treści, nie tylko w frontmatterze.** Rekomendacja
|
||
dodatkowa: każdy fakt z konkretną datą ważności (cena, status sprawy, "aktualnie
|
||
w toku") niesie datę **przy fakcie**, nie tylko w `updated_at` strony — bo
|
||
strona może zbierać fakty z różnych momentów (mail z 2019 o starej umowie +
|
||
mail z 2026 o nowej), a jedna data na całą stronę zaciera to rozróżnienie.
|
||
Przykład: *„Składka 1 234 zł/rok (stan na 07.2026) [^paperless:119#4212]."*
|
||
To jest rozszerzenie konwencji przypisu z §5, nie nowy mechanizm.
|
||
|
||
---
|
||
|
||
## 7. Punkt 6 — Integracja z kb-query: retrieval, ryzyko sprzężenia
|
||
|
||
### Co już zdecydowano
|
||
|
||
Inwariant 5 (faza3 §8.1): *„Wiki wchodzi do retrievalu jako dodatkowe źródło…
|
||
kaskada: wiki → summary → chunk."* §8.2 rozwija: strona wiki = koperta
|
||
`source='wiki'`, `id='wiki:<ścieżka>'`, chunkowana i embedowana tym samym
|
||
jobem, **bez nowego schematu bazy** — reużywa istniejący kontrakt koperty.
|
||
|
||
### Co to znaczy konkretnie w kodzie dzisiejszego `packages/kb-retrieval`
|
||
|
||
Sprawdziłem `retrieval.py`: dziś są trzy tory (`flat`/`cascade`/`hybrid`), z
|
||
`cascade_retrieve` jako **dwupoziomowym** (`document_summary` → `document_chunk`)
|
||
i `hybrid_retrieve` jako cascade + równoległy skan `summaryless_sources`
|
||
(`gmail`, `fastmail`) scalany po `dist`. **Nie ma dziś trzeciego poziomu.**
|
||
Realizacja inwariantu 5 wymaga:
|
||
|
||
1. Nowej tabeli/kolekcji `document_summary`-podobnej dla wiki, **albo**
|
||
traktowania stron wiki jak paperless — envelope z summary (skoro strony
|
||
są krótkie, 1–3 chunki, `document_summary` dla całej strony ma sens
|
||
semantyczny inaczej niż dla 187k-wierszowego newslettera).
|
||
2. Nowej funkcji `wiki_retrieve` w `kb_retrieval/retrieval.py`, wołanej
|
||
**przed** `cascade_retrieve`, z wynikiem scalanym analogicznie do
|
||
`hybrid_retrieve` (ten sam embedder, ten sam cosine space — merge to sort,
|
||
nie renormalizacja, dokładnie jak dziś).
|
||
3. Zmiany w `search.py`/`app/main.py` `kb-query`, żeby nowy tryb (`mode`)
|
||
albo rozszerzenie `hybrid` obejmował wiki — i w `app/links.py`
|
||
(`build_result`) żeby wynik ze źródła `wiki` miał sensowny `link`
|
||
(`raw_ref` do pliku w `kb-wiki`, nie URL — bo `kb-wiki` nie jest hostowane
|
||
publicznie, patrz §2).
|
||
|
||
To jest **realna zmiana kodu** w serwisie produkcyjnym (`kb-query`), nie tylko
|
||
w nowym repo `kb-wiki` — warto to mieć jawnie w planie iteracji (§8), bo
|
||
inwariant 5 bez tego pozostaje deklaracją, nie działaniem.
|
||
|
||
### Ryzyko sprzężenia (self-citation / citogenesis) — luka w szkicu faza3
|
||
|
||
Zlecenie pyta wprost o to ryzyko i faza3 **go nie adresuje**. Mechanizm
|
||
ryzyka: kompilacja strony X czyta wyniki retrievalu (inwariant 2) → jeśli
|
||
retrieval w tym momencie już przeszukuje `source='wiki'` (inwariant 5, w pełni
|
||
wdrożony) → strona X może dostać jako "dowód" **inną stronę wiki**, która sama
|
||
została skompilowana z niepewnych/błędnych przesłanek → błąd się utrwala i
|
||
**wzmacnia** zamiast zanikać, bo kolejna kompilacja cytuje już nie surowy
|
||
mail, tylko wcześniejszą (błędną) syntezę, z pozorem niezależnego
|
||
potwierdzenia. To jest dokładnie mechanizm Wikipedia-citogenesis, przeniesiony
|
||
na kompilator jednoosobowy.
|
||
|
||
**Rekomendacja — rozdzielić dwie ścieżki retrievalu, które dziś inwariant 5
|
||
zlewa w jedną:**
|
||
|
||
1. **Retrieval na potrzeby `/search` (użytkownik, faza 5 synteza
|
||
odpowiedzi)** — tu wiki **powinna** wchodzić do kaskady (inwariant 5 ma
|
||
rację: skompilowana wiedza jest cenniejsza niż surowy chunk dla kogoś, kto
|
||
pyta "co wiem o PZU").
|
||
2. **Retrieval na potrzeby *kompilacji* nowej/aktualizowanej strony (sesja
|
||
CC/API pisząca `kb-wiki`)** — tu retrieval **musi** wykluczać
|
||
`source='wiki'` (parametr `exclude_sources=('wiki',)` w
|
||
`cascade_retrieve`/`hybrid_retrieve`, jedna linia zmiany analogiczna do
|
||
dzisiejszego `summaryless_sources`). Kompilacja zawsze czyta wyłącznie
|
||
warstwę dowodową (mail, paperless — evidence layer z ustaleń wyjściowych
|
||
operatora), nigdy inne strony wiki jako źródło faktów. Strony **mogą**
|
||
linkować się nawzajem przez `[[...]]` (graf, nawigacja) — ale to nie jest
|
||
to samo co "cytowanie jako dowód faktu"; link `[[pzu]]` w treści strony
|
||
`polisa-oc-auto` to odsyłacz do powiązanej strony, nie przypis źródłowy —
|
||
przypisy źródłowe (`[^...]`) zawsze wskazują `envelope_id`, nigdy
|
||
`wiki:...`.
|
||
|
||
Ten podział jest tani do wymuszenia (jeden parametr) i eliminuje ryzyko przy
|
||
źródle, zamiast polegać wyłącznie na lincie żeby je złapać po fakcie.
|
||
Rekomendacja: dopisać to jako **inwariant 7** przy zatwierdzaniu planu, nie
|
||
jako implementacyjny szczegół — to jest decyzja architektoniczna tej samej
|
||
wagi co inwarianty 1–6 z faza3 §8.1.
|
||
|
||
---
|
||
|
||
## 8. Punkt 7 — Zależności i kolizje
|
||
|
||
### Multiagent recon / dyspozytor
|
||
|
||
Sprawdzone bezpośrednio w `kb/subsystems/recon-multiagent.md` i potwierdzone
|
||
przez `kb/audits/mail-sync-2026-08-06.md` §5 (który zrobił dokładnie to samo
|
||
ćwiczenie dla przyrostówki): **dyspozytor subsystemu B nie istnieje jeszcze w
|
||
kodzie** — jest zdefiniowany jako *osobny projekt* w
|
||
`kb/decisions/architektura-2026-07-28.md` (*„B — do-the-work: dyspozytor zadań
|
||
(agent)… Osobny wysiłek, osobny projekt"*), planowany na PIHA. `kb-query`
|
||
występuje w reconie multiagentowym wyłącznie jako wiersz inwentarza, poza
|
||
zakresem control-plane/Telegram.
|
||
|
||
**Wniosek: zero zależności blokujących w żadną stronę**, z tym samym
|
||
zastrzeżeniem, jakie mail-sync-recon już zanotował dla siebie: nowy automat
|
||
(tu: ewentualny periodyczny lint albo — gdyby kiedyś powstał — pipeline
|
||
eskalacji z §4) **dokłada się do „shadow set"** bytów poza GitOps-ową detekcją
|
||
dryfu (`kb-ingest`, `stability-agent`, teraz `kb-mail-sync`, potencjalnie
|
||
`kb-wiki`-lint). Nie blokuje nic dzisiaj; warto, żeby trafiło świadomie do
|
||
otwartego pytania nr 5 tamtego reconu, a nie przez przeoczenie — dokładnie tak
|
||
samo jak zanotował to mail-sync-recon dla siebie.
|
||
|
||
**Telegram/approval flow** (`CLAUDE.md` „Action approval flow") dotyczy
|
||
wyłącznie akcji `container_restart`/`redeploy`/`disk_cleanup`/`alert_only` z
|
||
control-plane. Kompilacja wiki nie generuje żadnej z tych akcji — nie ma i nie
|
||
powinno być punktu styku. Jedyny sensowny alert, gdyby faza 5 dostała
|
||
automatyzację (§4 tryb B, poza zakresem tej fazy), byłby analogiczny do
|
||
`KbMailSyncStale`: "kompilator/lint nie chodzi", nie coś wymagające approvalu
|
||
operatora.
|
||
|
||
### kb-site publish
|
||
|
||
Rozstrzygnięte w §2: `kb-wiki` leży poza `kb/**/*.md`, więc `gen_pages.py`/
|
||
`publish.sh` **nie widzą go w ogóle** — zero kolizji, bo zero styku. Gdyby w
|
||
przyszłości ktoś położył treść `kb-wiki` wewnątrz `kb/` (odwrotnie od
|
||
rekomendacji §2), *wtedy* powstałaby realna kolizja: `gen_pages.py --check`
|
||
skanuje wygenerowany output pod kątem wycieków (IP, tokeny, ścieżki hosta) —
|
||
ale nie skanuje pod kątem **danych osobowych** (PESEL, adresy, numery umów),
|
||
bo nie taki był jego cel. To jest dodatkowy, mocny argument za §2 (osobne
|
||
repo): trzymanie wiki-encji poza `kb/` eliminuje całą tę klasę ryzyka
|
||
strukturalnie, zamiast polegać na tym, żeby nikt nigdy nie oznaczył strony o
|
||
PZU jako `visibility: public` przez pomyłkę.
|
||
|
||
### Follow-upy fazy mailowej — czy brudne chunki psują kompilację
|
||
|
||
Z `kb/incidents/2026-08-26-mail-sync-20-dni-ciszy.md` §6 (`Nie w zakresie tej
|
||
naprawy`, bez zmian od 08-06): **charset/mojibake w co najmniej 12 kopertach**
|
||
oraz **NUL w nagłówkach `jsonb`** pozostają otwarte. Oceniam wpływ na
|
||
wiki-kompilat:
|
||
|
||
- **Skala jest znikoma** — 12 kopert na 227 576 (≈0,005%). Statystycznie
|
||
prawdopodobieństwo, że pierwsza fala 17 stron z §3 natrafi akurat na jedną
|
||
z tych 12, jest bliskie zeru (żadna z zidentyfikowanych encji §3 nie
|
||
pochodzi z okresu/nadawcy powiązanego ze znanymi przypadkami mojibake wg
|
||
audytu 08-06).
|
||
- **Mechanizm szkody, gdyby trafiła:** kompilująca sesja CC dostaje chunk z
|
||
zepsutym kodowaniem jako "dowód" — najgorszy scenariusz to nie błędny fakt
|
||
(model raczej rozpozna nieczytelny tekst i pominie/zaznaczy niepewność, patrz
|
||
§6 wyżej), tylko **marnotrawstwo** — chunk wygląda jak szum, model go
|
||
ignoruje, dowód dla realnego faktu ginie z pola widzenia.
|
||
- **NUL w `entities` jsonb jest poważniejszy strukturalnie** — jeśli sesja
|
||
kompilująca odpytuje `entities` bezpośrednio (nie tylko `document_chunk.text`),
|
||
NUL bajt w jsonb może wywrócić parser po stronie klienta (Pythonowy
|
||
`json`/`asyncpg` różnie reaguje na ` |