kb-wiki/_meta/conventions.md
oskar 9fc6703e2f docs(kb): scal decyzje (d)-(f) audytu 08-26 do _meta/conventions.md
Rekoncyliacja faza 5 etap 1 (kb-m5-faza5-wiki.md §4), krok 2 z 3. Trzy
decyzje z kb/audits/wiki-kompilat-recon-2026-08-26.md, już wypracowane i
zastosowane w bootstrapie sesyjnym (~/kb-wiki-etap1-sesja-2026-08-27/
_meta/conventions.md §5/§7/§8), scalone tu bez duplikowania tego, co w tym
repo już było (6 typów wliczając `meta` i sekcja "Brak danych w KB" — obie
już scalone wcześniej, commity 156925c i 3bd2441):

(d) Fallback envelope-only w przypisach — `[^envelope_id]` bez `#chunk_id`,
gdy dowód nie ma konkretnego chunka albo chunk przestał istnieć po
re-chunkingu (`document_chunk.id` niestabilny między przebiegami embeddingu).
Dopisane do sekcji "Przypisy inline i cytowanie faktów".

(e) Sekcja "Niepewne / sprzeczne" jako nazwana sekcja końca strony (już
używana de facto na żywych stronach: pzu.md, warta.md, mbank.md — teraz
sformalizowana pisemnie) + polityka aktualizacji "dopisz, nie nadpisuj" przy
nowym sprzecznym dowodzie, z wyjątkiem dla jawnych aktualizacji tego samego
faktu. Nowa sekcja, między "Sprzeczności między źródłami" a "Wersjonowanie
dokumentów źródłowych".

(f) Inwariant 7 — izolacja retrievalu kompilacji od source='wiki'
(mitygacja self-citation/citogenesis), CC/API-only jako wykonawca kompilacji.
Nowa sekcja przed "Integracja z retrievalem".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7AnxL6pbgySpEgcCwAfww
2026-08-27 19:55:57 +02:00

13 KiB
Raw Permalink Blame History

title type status tags sources compiled_by compiled_at updated_at
Konwencje kb-wiki meta active
claude-sonnet-5 2026-07-21 2026-08-27

Konwencje kb-wiki

Ten dokument definiuje format stron tej wiki. Rozwinięcie decyzji architektonicznych z homelab-codex-ws, docs/kb/modules/05-faza3-plan.md §8 (szkic operatora, NIE zmieniamy stamtąd decyzji — tylko doprecyzowujemy wykonanie). To repo jest read-write tylko dla kompilatora (CC/API); baza źródłowa (kb-postgres@PIHA) jest zawsze read-only z tej strony.

Struktura katalogów

Typy stron = katalogi; płasko wewnątrz każdego, graf robią linki [[...]], nie hierarchia plików.

Katalog Zawartość
podmioty/ Firmy i instytucje: ubezpieczyciele, banki, urzędy — encje trwałe, nie procesy
osoby/ Tożsamości/aliasy osób fizycznych
sprawy/ Procesy w czasie: sezony, remonty, procesy decyzyjne, porównania ofert
umowy/ Konkretne, zawarte kontrakty/polisy — jeden dokument prawny lub jego wersje
tematy/ Przekrojowe kategorie łączące wiele podmiotów/spraw (np. „ubezpieczenia auto”)
_meta/ Ten plik, raporty lintu

Wybór katalogu dla nowej strony: jeśli encja to firma/instytucja → podmioty/; jeśli to coś co się dzieje/działo w czasie (sezon, spór, zakup) → sprawy/; jeśli to konkretna umowa prawna → umowy/; jeśli łączy wiele podmiotów pod wspólnym parasolem tematycznym → tematy/.

Frontmatter

---
title: <nazwa strony, czytelna>
type: podmiot            # podmiot | osoba | sprawa | umowa | temat | meta
status: active            # active | archived
tags: [ubezpieczenie]     # słownik wspólny z jobs/documents-ingest/tags-vocab.yaml
                          # (homelab-codex-ws) — nie wymyślamy nowego słownika dla wiki
sources:
  - envelope_id: paperless:119
    chunks: [4211, 4213]           # document_chunk.id (klucz PK), NIE chunk_index
  - envelope_id: "CABtrY-...@mail.gmail.com"
    chunks: []                     # koperta bez chunków (np. sam manifest)
compiled_by: claude-sonnet-5       # model/sesja, która skompilowała/ostatnio zmieniła stronę
compiled_at: 2026-07-20            # data pierwszej kompilacji
updated_at: 2026-07-20             # data ostatniej zmiany
---

sources agreguje wszystkie koperty, z których cokolwiek na stronie pochodzi — niezależnie od tego, ile razy dana koperta jest cytowana w treści. To jest poziom audytu „skąd strona czerpie w ogóle”; poziom „ten konkretny fakt” robią przypisy inline (niżej).

Przypisy inline i cytowanie faktów

Każdy fakt liczbowy lub identyfikujący (kwota, data, numer polisy/umowy, adres, próg punktowy) dostaje przypis w miejscu użycia:

składka 1 234 zł/rok [^paperless:119#4212]

Format: [^<envelope_id>#<document_chunk.id>]. Jeden fakt może mieć więcej niż jeden przypis, jeśli potwierdzają go niezależnie różne chunki/koperty — wtedy oba się wypisuje: [^paperless:14#965][^paperless:22#2081].

Fallback envelope-only (decyzja d, audyt 08-26): document_chunk.id jest kruchy — re-chunking (zmiana chunk_size/modelu embeddingu) usuwa i wstawia wiersze na nowo, stary id przestaje istnieć. Gdy dowód nie ma konkretnego chunka (sam nagłówek koperty) albo chunk już nie istnieje po re-chunkingu, przypis degraduje się do samej koperty: [^<envelope_id>], bez #<chunk_id>. envelope_id jest stabilny (Message-ID/sha256-…, nigdy nie zmienia się po insercie) — degradacja zostaje audytowalna, tylko mniej precyzyjna. Lint raportuje liczbę zdegradowanych przypisów jako osobną metrykę (sygnał, że re-chunking coś ruszył); re-chunking jest triggerem do ręcznego przeglądu stron, nie do automatycznego remapowania (wymagałby ponownego embeddingu i ryzykowałby przypisanie faktu do złego fragmentu po cichu).

Fakty opisowe/narracyjne (np. „PZU oferuje kilka wariantów ubezpieczenia") niosące niską specyficzność mogą dzielić jeden przypis na koniec akapitu zamiast przypisu po każdym zdaniu — przypis jest obowiązkowy per sekcja, nie per zdanie, ale musi dawać się prześledzić do konkretnego chunka.

Zakaz dopowiadania: jeśli fakt nie występuje w żadnym pobranym chunku/streszczeniu, strona pisze wprost „brak danych w KB" zamiast go zgadywać z wiedzy ogólnej modelu.

Sprzeczności między źródłami

Gdy dwa dokumenty (typowo: dwie wersje tego samego OWU z różnymi datami obowiązywania, albo dwie oferty konkurencyjne) podają różne wartości dla tego samego faktu, strona nie wybiera cicho jednej wersji. Sprzeczność jest flagowana jawnie w tekście, z obiema wartościami i obu przypisami, np.:

Udział własny w AC: 300 zł wg OWU obowiązującego od 21.03.2026 [^paperless:14#871],
ale 500 zł wg wcześniejszej wersji OWU [^paperless:17#XXXX] — **rozbieżność między
wersjami, nie literówka**; przy sporze o odszkodowanie decyduje wersja obowiązująca
w dniu zdarzenia.

Sekcja „Niepewne / sprzeczne" i polityka aktualizacji (decyzja e, audyt 08-26)

Sekcja wyżej pokazuje jak flagować sprzeczność w treści. To formalizuje ją jako nazwaną sekcję na końcu strony (już używana na żywych stronach: pzu.md, warta.md, mbank.md, ...), gdy sprzeczności nie da się rozstrzygnąć przy kompilacji — nazwa i miejsce ujednolicone, żeby czytelnik/lint wiedział, gdzie szukać:

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

Kolejność na końcu strony, gdy obie sekcje występują: „Niepewne / sprzeczne" przed „Brak danych w KB" (niżej) — dowody, które się kłócą, nie to samo co brak dowodów w ogóle. Strona bez tej sekcji nie jest „lepsza" — strona z niewykrytą sprzecznością w treści głównej jest gorsza; to mechanizm anty-propagacji (kompilator pisze „nie wiem" zamiast zgadywać i zamrażać zgadywankę jako fakt).

Polityka aktualizacji przy nowym, sprzecznym dowodzie: dopisz, nie nadpisuj. Domyślnie nowy sprzeczny dowód trafia do „Niepewne/sprzeczne", fakt w treści głównej nie jest nadpisywany, dopóki sprzeczność nie zostanie rozstrzygnięta. 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, bez sprzeczności) — to nie jest sprzeczność, to aktualizacja: idzie do treści głównej z nową updated_at. Rozróżnienie „aktualizacja" vs „sprzeczność" robi sesja kompilująca (CC/API), nie reguła automatyczna.

Wersjonowanie dokumentów źródłowych

Ubezpieczenia i regulaminy w korpusie mają wielokrotne wersje z różnymi datami „obowiązuje od". Strona encji nie kompiluje jednej płaskiej prawdy — jeśli źródła mają rozbieżne daty obowiązywania, strona wymienia wersje osobno (np. podsekcja per data) zamiast nadpisywać starszą nowszą bez adnotacji. To był konkretny finding z pilota dedupa (docs/kb/eval/retrieval-pilot-2026-07-16.md, docs/kb/modules/05-faza3-plan.md §3.2) — dokumenty bywają też duplikatami binarnymi (excluded_reason='duplicate' w bazie), co jest czymś innym niż kolejna wersja: duplikat pomijamy w kompilacji (baza już to wyklucza z retrievalu), wersję z inną datą obowiązywania — nie.

Sekcja „Brak danych w KB" (rozszerzenie, faza 5 etap 1 rekoncyliacja, 2026-08-27)

„Zakaz dopowiadania" wyżej mówi, że pojedynczy fakt bez pokrycia w źródłach pisze się wprost jako „brak danych w KB" w miejscu użycia. To rozszerzenie formalizuje to jako osobną sekcję na końcu strony, odróżnioną od „Niepewne / sprzeczne":

  • „Niepewne / sprzeczne" — dowody istnieją, ale się nie zgadzają (dwa chunki, dwie wartości, dwa przypisy).
  • „Brak danych w KB" — zapytano retrievalem o coś istotnego dla tej encji, i żaden pobrany chunk/streszczenie tego nie potwierdza. Nie jest to błąd kompilacji — jest to jawne przyznanie granicy tego, co korpus dziś wie, zamiast milczącego pominięcia pytania.

Format — krótka lista na końcu strony, po „Niepewne / sprzeczne" (jeśli obie występują):

## Brak danych w KB

- Nazwisko trenera drużyny (OCR nieczytelny w `paperless:175`).
- Wynik końcowy/klasyfikacja drużyny w całym turnieju (KB ma tylko wyniki cząstkowe).

Praktyczna wartość: kolejna kompilacja/aktualizacja tej strony (albo lint) wie od razu, czego szukać przy następnym retrievalu, zamiast rekonstruować to z pamięci sesji albo przeczytać stronę i domyślać się, co zostało pominięte a co nigdy nie było sprawdzone. Pierwszy przykład użycia (przed formalizacją tej reguły): sprawy/fll-2025-26.md, sekcja „Brak danych w KB", kompilacja 2026-07-21.

Linkowanie między stronami

[[nazwa-pliku-bez-rozszerzenia]] — link do innej strony wiki po nazwie pliku (bez katalogu, nazwy plików są unikalne w całym repo). Linkujemy tam, gdzie treść się faktycznie styka (wspólny podmiot, wspólna sprawa, przywoływany dokument) — nie tworzymy linków na wyrost do stron, które jeszcze nie istnieją, chyba że świadomie oznaczamy brakujące pojęcie (patrz niżej), bo taki link psuje inwariant lintu „brak stron-sierot i wiszących linków" dopóki strona docelowa nie powstanie.

Brakujące pojęcia (przyszłe strony)

Jeśli podczas kompilacji natrafiamy na encję wartą osobnej strony, ale poza zakresem bieżącej kompilacji (np. inny ubezpieczyciel wspomniany punktowo, inna sprawa), nie tworzymy linku [[...]] do niej (byłby wiszący). Zamiast tego kandydatka trafia do listy „brakujące pojęcia" w najbliższym raporcie lintu (_meta/lint-reports/), żeby nie zgubić się w wątku.

Zasady lintu

Okresowy lint (ręczny w tej fazie, _meta/lint-reports/YYYY-MM-DD.md) sprawdza:

  1. Strony-sieroty — strona bez żadnego linku przychodzącego z innej strony (poza INDEX.md, który z definicji linkuje do wszystkiego).
  2. Wiszące linki[[cel]] bez odpowiadającego pliku cel.md w repo.
  3. Brakujące pojęcia — encje wspomniane w treści (nie w linku) a nieopisane osobną stroną; kandydaci na przyszłe strony, nie błąd.
  4. Spot-check — dla losowej próbki przypisów [^envelope_id#chunk_id] weryfikacja, że treść chunka w bazie faktycznie potwierdza przywołany fakt.
  5. Sprzeczności międzystronicowe — ten sam fakt o tej samej encji opisany różnie na dwóch stronach (nie mylić z rozbieżnością międzywersyjną w obrębie jednej strony, która jest już jawnie flagowana w treści per wyżej).

Kompilacja: CC/API-only i inwariant 7 — izolacja retrievalu (decyzja f, audyt 08-26)

Kompilację i lint robi wyłącznie CC/zewnętrzne API (patrz akapit wstępny) — lokalny model za słaby na wielostronicowe operacje syntezy.

Inwariant 7: retrieval na potrzeby kompilacji strony wiki nigdy nie czyta source='wiki' jako dowodu — zawsze wyklucza wiki (exclude_sources=('wiki',) albo równoważny filtr SQL), czyta wyłącznie warstwę dowodową (mail, paperless). Tylko retrieval na potrzeby /search (warstwa użytkownika, po zbudowaniu syntezy odpowiedzi — poza zakresem tej fazy) widzi wiki w kaskadzie. Mitygacja self-citation/citogenesis: bez tego rozdziału kompilacja strony X mogłaby cytować inną stronę wiki (samą skompilowaną z niepewnych przesłanek) jako „dowód", i błąd wzmacniałby się z pozorem niezależnego potwierdzenia.

W praktyce (dopóki source='wiki' nie istnieje w bazie) retrieval kompilacyjny i tak nie może dziś trafić na wiki — ale każde zapytanie SQL/cascade_retrieve/hybrid_retrieve użyte przy kompilacji strony musi jawnie nieść ten filtr od pierwszego dnia, nie dopisany post-factum, gdy wiki już będzie źródłem w bazie.

Strony mogą linkować się nawzajem przez [[nazwa-strony]] (graf, nawigacja) — to nie jest to samo co cytowanie jako dowód faktu. Przypisy źródłowe ([^...]) zawsze wskazują envelope_id (warstwa dowodowa), nigdy inną stronę wiki.

Integracja z retrievalem (poza zakresem tej fazy)

Docelowo strony wiki wchodzą do kb-postgres jako koperty source='wiki' (id='wiki:<katalog>/<plik>'), embedowane tym samym pipeline'em co chunki dokumentów — kaskada retrievalu rozszerza się wtedy o trzeci poziom: wiki → streszczenie → chunk. Nie jest to realizowane w tej fazie (proof-of-concept ręcznej kompilacji 35 stron) — patrz docs/kb/modules/05-faza3-plan.md §8.2 w homelab-codex-ws po szczegóły.