homelab-codex-ws/kb/audits/wiki-kompilat-recon-2026-08-26.md
oskar efd9ad9443 docs(kb): faza5-wiki Etap 1 + odkrycie: kb-wiki proof-of-concept już istniał od 2026-07-21 na Forgejo, audyt 08-26 tego nie wykrył
kb/phases/kb-m5-faza5-wiki.md dokumentuje: (1) tę sesję (2026-08-27) budującą
niezależny lokalny bootstrap kb-wiki (3 strony: fll-2025-26, mbank,
pawel-cesar-sanjuan-szklarz) nie wiedząc o realnym repo oskar/kb-wiki na
Forgejo (5 stron, od 2026-07-21, dokumentowanym już w kb-m5-faza4.md i
docs/sessions/2026-07-21.md); (2) jak doszło do odkrycia (audyt 08-26 nie
sprawdził Forgejo, tylko repo lokalne + PIHA); (3) porównanie obu kompilacji
fll-2025-26 (komplementarne, nie sprzeczne); (4) rekoncyliację jako decyzję
operatora, nie tej sesji — lokalny bootstrap nigdy nie pushowany do żadnego
remote.

Jednolinijkowa notka przy audycie §9/Etap 1 wskazuje na pełne rozliczenie.
2026-08-27 16:59:08 +02:00

48 KiB
Raw Permalink Blame History

okf type visibility status updated as_of links
0.1 audit private active 2026-08-27 2026-08-26
../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:

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
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 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, 310 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):

SELECT source, date_trunc('year', ts) AS yr, count(*) FROM envelope GROUP BY 1,2 ORDER BY 1,2;

Szczyt 20112012 (~23k/rok — dawny pracodawcy przez outbox.pl), potem stabilne 913k/rok 20132025. 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):

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 20052020
Oskar Kapala oskar.kapala@outbox.pl 6 399 (epoch)2014
Kasia Lorenc-Kapala katalia@gmail.com 3 385 20072026
Oskar Kapala oskar.kapala@gmail.com 2 910 20102026
System Synergia robot2@robot.librus.pl 2 818 20182026
Allegro powiadomienia@allegro.pl 2 170 20082026
Tomasz Anuszewski tomasz.anuszewski@outbox.pl 1 913 20112012
Dziennik Bankier.pl (×2 adresy) …bankier.pl 1 736 + 1 676 20072014
RSW_TECH_TEAM rsw_tech_team@outbox.pl 1 628 20112012
Groupon info@news.groupon.pl 1 447 20112012
Miron Mironiuk m@cosmose.co 1 126 20152016
kontakt@mbank.pl 1 099 20052026
Jan Boboli jan.boboli@outbox.pl 969 20112013
Paweł Cesar Sanjuan Szklarz paweld2@gmail.com, pawel@cosmose.co 757+555+418+376 20052024
InPost info@paczkomaty.pl 722 20172026
Bogumil Jakubiak B.Jakubiak@icm.edu.pl 686 20042009
sOKratis Liliana Banaszak liliana.banaszak@sokratis.pl 679 20092026
Strava update.strava.com / strava.com 591+395 20172026

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 (20112012), 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 1020 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 20052026, 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 20182026, 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, 20172026 ciągłe
13 bogumil-jakubiak osoba ICM UW, 20042009 — 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 20092026, ciągłość 17 lat, nazwany kontakt (Liliana Banaszak)
17 allegro temat 2 170 kopert 20082026 — najdłuższa ciągła relacja handlowa w korpusie
1820 (druga tura, po pierwszej weryfikacji) outbox.pl/dawny pracodawca po ustaleniu kontekstu; PZUmbank 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ę 35 stron proof jako „pomijalna" wobec ~1.54 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:

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

## 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_summarydocument_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, 13 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 16 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 `` w zależności od ścieżki). To nie zostało zweryfikowane w tym reconie (nie testowałem odczytu takiego wiersza) — flaguję jako [do weryfikacji], nie jako potwierdzony problem.

Rekomendacja: nie blokować pierwszego etapu na R1R3 z §6 incydentu (charset/NUL) — skala nie uzasadnia opóźnienia PoC. Warto natomiast, żeby lint (inwariant 3) przy pierwszym uruchomieniu zrobił jednorazowy skan document_chunk/envelope.entities pod kątem znaków zastępczych (<EFBFBD>) i `` w źródłach, które faktycznie zasiliły PoC (17 stron §3) — tanie zabezpieczenie, żeby nie odkryć problemu dopiero przy spot-checku tygodnie później.


9. Punkt 8 — Plan iteracji

Wzorzec: faza mailowa (kb-m5-faza-mailowa.md §10 „Plan implementacji" + §12 kryterium ukończenia per krok) i mail-sync-recon (§6 „Podsumowanie i zakres pracy" — tabela kroków z rodzajem zmiany). Stosuję ten sam format: kroki z zależnościami, DoD per krok, pierwszy etap ograniczony do 12 sesji.

Etap 0 — Zamknięcie decyzji (ten dokument + zatwierdzenie, 0 sesji kodu)

Operator przyklepuje §11 (Decyzje) poniżej. Bez tego nic dalej nie rusza — identyczny wzorzec jak Decyzje (a)-(g) w mail-sync-recon, które zatwierdzono w całości przed implementacją (kb-m5-faza-mailowa.md nagłówek statusu).

Etap 1 — Szkielet kb-wiki + 3 strony proof (cel: 1 sesja)

Wykonany 2026-08-27 — z zastrzeżeniem: ten audyt nie wykrył, że proof-of-concept (5 stron) już istniał od 2026-07-21 na Forgejo (oskar/kb-wiki, poza zestawem dowodów tego reconu). Pełne rozliczenie i rekoncyliacja: kb/phases/kb-m5-faza5-wiki.md.

Zakres, celowo mniejszy niż faza3 §8.2 (35 stron) i mniejszy niż lista z §3 (17 encji) — pierwszy etap ma zweryfikować mechanizm, nie pokryć korpus:

# Element DoD
1 Repo kb-wiki (git, poza homelab-codex-ws), struktura katalogów wg faza3 §8.2, _meta/conventions.md (format frontmattera + inline-przypisów z §5, w tym reguła fallback z §5, sekcja „Niepewne/sprzeczne" z §6, rozdział retrievalu z §7 inwariant 7) Repo istnieje, conventions.md kompletny, czytelny bez kontekstu tej sesji
2 Walidator kb-wiki/check_okf.py — wariant lekki, wzorowany na ~/narty-2027/saalbach-kb/check_okf.py (frontmatter parsowalny + type niepuste), rozszerzony o sprawdzenie bloku sources (niepusty, każdy envelope_id istnieje w bazie — może wymagać KB_DSN) python3 check_okf.py PASS na repo z krokiem 3
3 3 strony proof, kompilowane ręcznie sesją CC z wyników hybrid_retrieve/cascade_retrieve na encjach #1 (fll-2025-26), #5 (mbank), #9 (pawel-cesar-sanjuan-szklarz) z §3 — świadomie zróżnicowane typy (sprawa/podmiot/osoba-z-aliasami) i źródła (paperless/gmail-długi-ciąg/gmail-multi-adres) Każda strona: frontmatter zgodny z konwencją, ≥1 przypis inline, sources: niepuste, przechodzi walidator
4 Ręczny spot-check (inwariant 3, wersja manualna — pełny lint to Etap 2) — operator albo agent czyta 3 strony vs źródłowe chunki, potwierdza brak halucynacji Zapisany w _meta/lint-reports/2026-08-XX.md — pierwszy raport, nawet jeśli ręczny

Dlaczego te trzy, nie 17: pawel-cesar-sanjuan-szklarz (#9) testuje entity resolution na aliasach — jeśli to zawiedzie, cała reszta listy z §3 (gdzie kilka pozycji ma podobny problem, np. cosmose↔#7/#8) wymaga przeprojektowania konwencji zanim zainwestuje się w kolejne 14 stron. Trzy strony, świadomie różne, są tańszym testem niż siedemnaście podobnych.

Etap 2 — Lint automatyczny + rozszerzenie do pełnej listy §3 (2. sesja)

Zależy od Etapu 1 (mechanizm zweryfikowany). Zakres:

  1. kb-wiki/lint.py — implementacja inwariantu 3 w kodzie (nie ręcznie): linki [[...]] do nieistniejących stron, strony-sieroty, spot-check N losowych przypisów (zapytanie do document_chunk po id, fallback do envelope_id z §5 gdy id martwy — z metryką degradacji), sprzeczności między stronami (heurystyka: te same encje w sources: różnych stron z rozbieżnymi liczbami przy tym samym typie faktu — pełna detekcja semantyczna to zadanie dla CC, nie dla skryptu; skrypt robi tylko pre-filtr kandydatów).
  2. Rozszerzenie o pozostałe encje z §3 (do 17), plus jednorazowy skan pod kątem <EFBFBD>/`` (§8 „follow-upy fazy mailowej") na źródłach, które faktycznie zasiliły te strony.
  3. DoD etapu: lint.py uruchamiany ręcznie (nie timer — zgodnie z §4, automatyzacja poza zakresem), raport w _meta/lint-reports/, zero krytycznych znalezisk nierozwiązanych (martwe linki, sieroty) — degradacje chunk-id i sekcje „niepewne" to oczekiwany, nie błędny stan.

Poza zakresem pierwszych dwóch etapów (świadomie odłożone)

Temat Gdzie wraca
Integracja source='wiki' w packages/kb-retrieval (§7, inwariant 5 + 7) Etap 3 — wymaga działającego korpusu wiki (≥Etap 2) zanim ma sens dodawać go do retrievalu produkcyjnego
Synteza odpowiedzi (/search → odpowiedź w naturalnym języku) Osobna faza, kb-m5-faza-mailowa.md §11 już to tak kotwiczy
Pipeline eskalacji per-query (tryb B z §4) Po syntezie odpowiedzi — bez niej nie ma sygnału do eskalacji
Automatyzacja lintu (timer) Po Etapie 2, gdy wiadomo ile realnie kosztuje jeden przebieg
Ewentualny hosting/publikacja kb-wiki Nie zaplanowane — patrz §2, dziś brak potrzeby
Druga tura encji (#18-20 z §3, outbox.pl/dawny pracodawca) Po ustaleniu kontekstu firmy — osobne mini-śledztwo

10. Punkt 9 — Decyzje operatora

Status: (a)-(h) ZATWIERDZONE przez operatora w całości 2026-08-27, bez zmian — wzorzec identyczny jak decyzje (a)-(g) w mail-sync-2026-08-06.md (zatwierdzone 2026-08-06 w całości, patrz kb-m5-faza-mailowa.md nagłówek statusu).

Wzorzec (a)-(g) z kb/audits/mail-sync-2026-08-06.md §4.

(a) Wersja OKF dla kb-wiki: pin v0.1 (jak pilot) vs start od razu na v0.2

Rekomendacja: pin v0.1, z migracją zaplanowaną, nie natychmiastową.

  • Pilot narty27 potwierdził w praktyce, że pinowanie v0.1 działa i że zmiana jest tania: docs/sessions/2026-07-31-kb-f4-final-narty27.md„OKF v0.1 działa w praktyce, a pinowanie wersji okazało się słuszne — spec ewoluuje (v0.2: timestampgenerated: {by, at}, provenance first-class). Przy fazie 5 rozważyć start od razu na v0.2 albo pin v0.1 z zaplanowaną migracją." To jest bezpośrednia rekomendacja z pilota, cytowana w zleceniu tego reconu.
  • Ale: kb-wiki ma silniejszą potrzebę provenance niż narty27 (który jest planowaniem wyjazdu, nie audytowalnym kompilatem nad wrażliwym korpusem). Provenance first-class w v0.2 (generated: {by, at}) pokrywa się częściowo z tym, co faza3 §8.2 już projektuje osobno (compiled_by/compiled_at w sources-owym frontmatterze) — czyli potrzeba, którą v0.2 adresuje, została już zaadresowana lokalną konwencją, niezależnie od wersji OKF. To osłabia argument "musimy mieć v0.2 od razu", bo pole compiled_by robi tę samą robotę.
  • Decydujący argument za v0.1: homelab-codex samo jest dziś na v0.1 (scripts/kb/check_okf.py, PINNED_OKF = "0.1"). Gdyby kb-wiki (osobne repo, ale koncepcyjnie ta sama rodzina dokumentów co kb/) startował na v0.2, powstałyby dwie różne wersje specyfikacji w tym samym ekosystemie narzędzi — dwa parsery, dwie reguły walidacji, mylące przy przenoszeniu wzorców między repo (dokładnie to, co uzasadnia rekomendację pilota "pin + zaplanowana migracja" zamiast "zawsze najnowsza wersja"). Spójność z resztą ekosystemu waży więcej niż wyprzedzenie o jedną wersję specyfikacji, którą i tak trzeba będzie kiedyś zmigrować w kb/ też.
  • Migracja zaplanowana: gdy homelab-codex/kb/ migruje na v0.2 (osobna decyzja, poza zakresem tego reconu), kb-wiki migruje w tym samym oknie — jedna zamiana pola (timestampgenerated: {by,at}), zgodnie z oceną pilota że to tania zmiana.

(b) Repo kb-wiki — potwierdzenie D7 z faza3

Rekomendacja: potwierdzić bez zmian (§2). Jedyne doprecyzowanie: kompilacja nie idzie przez agent.sh-owy worktree — osobny tryb pracy z dwoma klonami obok siebie, do zapisania w kb-wiki/_meta/conventions.md.

(c) Tryb kompilatora — potwierdzenie batch/hybryda, pipeline eskalacji poza zakres

Rekomendacja: (§4) batch (CC/API, ręczny trigger) dla bootstrapu i utrzymania w tej fazie; pipeline eskalacji per-query (tryb B) świadomie odłożony do momentu, gdy istnieje synteza odpowiedzi w kb-query — bez niej nie ma sygnału, który miałby eskalować.

(d) Fallback chunk-id → envelope_id przy re-chunkingu

Rekomendacja: (§5) dwupoziomowy link z degradacją do envelope_id, lint raportuje liczbę zdegradowanych przypisów jako osobną metrykę, re-chunking traktowany jako trigger do ręcznego przeglądu, nie do automatycznej naprawy.

(e) Sekcja „Niepewne/sprzeczne" i polityka aktualizacji — dopisać do konwencji

Rekomendacja: (§6) sekcja jako konwencja treści (nie pole frontmattera), domyślna polityka „dopisz, nie nadpisuj" przy sprzecznym nowym dowodzie, wyjątek dla jawnych aktualizacji tego samego faktu, rozróżnienie robi sesja kompilująca.

(f) Inwariant 7 — rozdział retrievalu kompilacji od retrievalu zapytań

Rekomendacja: (§7) dopisać do inwariantów faza3 §8.1 jako inwariant 7: kompilacja strony wiki nigdy nie czyta source='wiki' jako dowód (exclude_sources=('wiki',)), tylko /search (warstwa użytkownika, po zbudowaniu syntezy odpowiedzi) widzi wiki w kaskadzie. Mitygacja self-citation/citogenesis przy źródle, nie tylko przez lint po fakcie.

(g) Zakres i kolejność Etapu 1 — 3 strony proof, nie 17

Rekomendacja: (§9) Etap 1 = mechanizm (3 strony celowo zróżnicowane: sprawa/podmiot/osoba-z-aliasami), Etap 2 = skala (do 17) + lint w kodzie. Rozszerzenie listy encji (#18-20, outbox.pl) odłożone do osobnego mini-śledztwa kontekstu firmy.

(h) Follow-upy fazy mailowej (charset/NUL) — nie blokują, jednorazowy skan przy Etapie 2

Rekomendacja: (§8) nie opóźniać PoC na R1R3 z incydentu 20-dni-ciszy; dopisać jednorazowy skan <EFBFBD>/`` do zakresu Etapu 2 (lint), nie jako osobny, wcześniejszy krok.


11. Podsumowanie

Korpus mailowy dogonił dziś — blokada z kb-m5-faza3.md §8.1 jest zdjęta (§1). Architektura wiki-kompilatu jest w dużej mierze już zaprojektowana i zatwierdzona (kb-m5-faza3.md §8, decyzja 2026-07-15/17) — ten recon weryfikuje ją na aktualnym stanie repo, dociąga trzy realne luki, których tamten szkic nie adresował (chunk-id vs re-chunking §5, self-citation przy retrievalu kompilacyjnym §7 — nowy inwariant 7, sekcja niepewne/sprzeczne §6), i daje 17-pozycyjną listę encji zalążkowych wyprowadzoną z realnych SELECT-ów na żywym korpusie zamiast z trzech przykładów w szkicu (§3). Rekomendowany pierwszy etap to 3 strony proof w 1 sesji (świadomie mniej niż faza3 §8.2 sugerowała, bo pierwszy test ma zweryfikować mechanizm entity-resolution na trudnym przypadku — aliasach — zanim zainwestuje się w skalę), z jawnym odłożeniem pipeline'u eskalacji i integracji retrievalu produkcyjnego do kolejnych etapów.

Decyzje: (a)-(h) w §10 — ZATWIERDZONE przez operatora w całości 2026-08-27, bez zmian.