Compare commits

..

No commits in common. "a540a99a1bddfff101879b1386d4d0412a9b72f0" and "c4127b9f35339e7c6d71559a545359031ff0fa9b" have entirely different histories.

6 changed files with 1 additions and 745 deletions

View file

@ -7,14 +7,10 @@ Spis wg typu (katalogu), utrzymywany przy każdej kompilacji. Format stron:
- [[pzu]] — PZU (ubezpieczyciel): OWU PZU Auto/OC, karty produktu, oferta T1531938417
- [[warta]] — WARTA (ubezpieczyciel): OWU AC Standard/Komfort/Moje Auto, wyliczenie oferty
- [[mbank]] — mBank: relacja bankowa 20052026 (karty kredytowe, wyciągi, kredyt,
konto dla dziecka), najdłuższa ciągła relacja instytucjonalna w korpusie mailowym
## osoby/
- [[pawel-cesar-sanjuan-szklarz]] — znajomy/współpracownik od studiów na MIMUW
(2004) po dziś, pod co najmniej 6 adresami e-mail w różnych okresach życia —
pierwszy test entity-resolution na aliasach w tej wiki
(brak stron w tej fazie)
## sprawy/

View file

@ -74,18 +74,6 @@ Format: `[^<envelope_id>#<document_chunk.id>]`. Jeden fakt może mieć więcej n
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
@ -108,40 +96,6 @@ wersjami, nie literówka**; przy sporze o odszkodowanie decyduje wersja obowiąz
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ć:
```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.
```
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
@ -215,32 +169,6 @@ Okresowy lint (ręczny w tej fazie, `_meta/lint-reports/YYYY-MM-DD.md`) sprawdza
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'`

View file

@ -1,119 +0,0 @@
---
title: Lint — rekoncyliacja Etap 1 (2026-08-27)
type: meta
status: active
tags: [meta, lint, rekoncyliacja]
sources: []
compiled_by: claude-sonnet-5
compiled_at: 2026-08-27
updated_at: 2026-08-27
---
# Lint — rekoncyliacja Etap 1 (2026-08-27)
Lint po zamknięciu rekoncyliacji dwóch niezależnych bootstrapów wiki-kompilatu
(`homelab-codex-ws:kb/phases/kb-m5-faza5-wiki.md`): lipcowy proof-of-concept
(`oskar/kb-wiki`, Forgejo, 5 stron) + sesyjny bootstrap
`~/kb-wiki-etap1-sesja-2026-08-27/` (3 strony, nigdy nie pushowany). Stan po
rekoncyliacji: **7 stron**`sprawy/fll-2025-26.md` scalone (dwie
kompilacje tej samej encji, commit `c4127b9`, wcześniej niż ten lint),
`podmioty/mbank.md` i `osoby/pawel-cesar-sanjuan-szklarz.md` przeniesione bez
kolizji (czysty dodatek — nie istniały w lipcowym repo).
## 1. `check_okf.py` — automatyczna część
Walidator sesyjny przeniesiony do tego repo (`check_okf.py`, patrz commit
oddzielny i `_meta/conventions.md` §10) — lipcowy proof-of-concept nie
zostawił skomitowanego narzędzia. Adaptowany do rzeczywistego layoutu:
`type` rozszerzone o `meta`, `README.md`/`INDEX.md`/`_meta/lint-reports/*.md`
wyjęte z wymogu frontmattera stron-konceptów (dokumentacja/raporty, nie
skompilowane encje).
```
$ python3 check_okf.py
Repo: /home/oskar/kb-wiki
Sprawdzono plików .md: 11
koncepty: 11, zarezerwowane: 0
per type: meta=1, osoba=1, podmiot=3, sprawa=3
przypisów sources: 74 (4 envelope-only / bez chunków)
ZGODNE z OKF v0.1 (kb-wiki): frontmatter parsowalny, type/status poprawne,
sources niepuste, wszystkie 74 przypisów zweryfikowane w bazie.
```
**74/74** par `(envelope_id, chunk_id)` z bloków `sources:` (agregat
frontmattera, wszystkie 6 stron-konceptów) zweryfikowanych wprost w `kb`
(SELECT-only, `KB_DSN`) — każdy `envelope_id` istnieje w `envelope`, każdy
`chunk_id` istnieje w `document_chunk` **i** należy do zadeklarowanego
`envelope_id`.
## 2. Przypisy inline `[^envelope_id#chunk_id]` — pełny przebieg, nie próbka
Rozszerzenie względem lintu 07-21 (ręczny spot-check 2 twierdzeń) i lintu
sesyjnego 08-27 (pełny ręczny przebieg, ale tylko 3 strony,
`~/kb-wiki-etap1-sesja-2026-08-27/_meta/lint-reports/2026-08-27.md`) — tu:
**wszystkie** przypisy inline na **wszystkich** 7 stronach-koncepcjach tego
repo po scaleniu, jednym przebiegiem skryptu (jednorazowy, nieskomitowany —
ta sama konwencja co narzędzie sesyjne 08-27, nie jest to `check_okf.py`,
który sprawdza tylko blok `sources:`).
```
Znaleziono 150 przypisów inline w stronach-konceptach.
Per plik:
osoby/pawel-cesar-sanjuan-szklarz.md: 13
podmioty/mbank.md: 12
podmioty/pzu.md: 28
podmioty/warta.md: 16
sprawy/fll-2025-26.md: 45
sprawy/ubezpieczenie-auto-ga431ja-2026.md: 11
sprawy/wspolnota-mieszkaniowa-targowa-2a.md: 25
Zdegradowane (envelope-only, bez #chunk_id): 0
ZGODNE: wszystkie 150 przypisów inline zweryfikowane wprost w bazie.
```
**150/150** — envelope_id istnieje, chunk_id istnieje i należy do wskazanego
envelope_id, dla każdego pojedynczego przypisu w treści (nie tylko agregatu
`sources:`). Zero przypisów zdegradowanych do fallback envelope-only
(`_meta/conventions.md`, sekcja fallback, decyzja d) na tym etapie.
## 3. Linki `[[...]]` — strony-sieroty / linki donikąd
Wiszące linki: **0**. Wszystkie realne cele `[[...]]` w treści (`pzu`,
`warta`, `ubezpieczenie-auto-ga431ja-2026`, `wspolnota-mieszkaniowa-targowa-2a`,
`fll-2025-26`, `mbank`, `pawel-cesar-sanjuan-szklarz`) odpowiadają istniejącym
plikom. Trzy dopasowania dodatkowe (`cel`, `nazwa-strony`,
`nazwa-pliku-bez-rozszerzenia`) to przykłady składni w prozie
`_meta/conventions.md`, nie prawdziwe linki — pominięte.
Linki do `mbank.md` i `pawel-cesar-sanjuan-szklarz.md` przy przenoszeniu
przepisane ze składni sesyjnej (`[[../podmioty/mbank]]`, z prefiksem
katalogu) na rzeczywistą konwencję tego repo (`[[mbank]]`, bez katalogu —
`_meta/conventions.md` „Linkowanie między stronami"). Treść merytoryczna obu
stron nie zmieniona.
`sprawy/fll-2025-26.md` przestało być sierotą **przed** tym lintem (link
przychodzący z `podmioty/mbank.md` i `osoby/pawel-cesar-sanjuan-szklarz.md`,
sekcja „Powiązane" obu stron — potwierdzone „sprawdzone wprost, zero
wspólnego dowodu", link mimo braku dowodu jest świadomą decyzją sesji
sierpniowej, nie tej rekoncyliacji).
## 4. Sprzeczności międzystronicowe po dodaniu `mbank`/`pawel-cesar-sanjuan-szklarz`
Nie sprawdzane od nowa w tym lincie — współwystępowanie słów kluczowych
(`mbank`+`szklarz`, `mbank`+`fll`, `szklarz`+`fll`, `mbank`+`future minds`,
`szklarz`+`future minds`) już zweryfikowane bezpośrednio SQL-em przy
kompilacji tych stron w sesji 08-27 (zero trafień merytorycznych — patrz
sekcje „Powiązane" na obu stronach i `sprawy/fll-2025-26.md` „Powiązane").
Nowego retrievalu do tego lintu nie uruchamiano — przeniesienie plików nie
zmienia treści, więc wynik pozostaje ważny.
## Wniosek
Zero krytycznych znalezisk: brak martwych linków, brak przypisów wskazujących
złą kopertę, brak przypisów zdegradowanych. 7/7 stron-konceptów zgodnych z
OKF v0.1 wg `check_okf.py`. `~/kb-wiki-etap1-sesja-2026-08-27/` (repo lokalne
tej sesji) pozostaje nietknięte — usunięcie po weryfikacji operatora, poza
zakresem tego lintu.

View file

@ -1,314 +0,0 @@
#!/usr/bin/env python3
"""Walidator konformancji OKF v0.1 dla kb-wiki.
Przeniesiony z `~/kb-wiki-etap1-sesja-2026-08-27/check_okf.py` przy
rekoncyliacji 2026-08-27 (patrz `_meta/conventions.md` §10 i
`homelab-codex-ws:kb/phases/kb-m5-faza5-wiki.md` §3) rekomendacja tamtej
sesji: "Przenieść check_okf.py tej sesji do prawdziwego repo przy
rekoncyliacji", bo lipcowy proof-of-concept nie zostawił żadnego
skomitowanego walidatora. Rozszerzenia specyficzne dla kb-wiki
(`_meta/conventions.md` §10):
1. `type` musi być z listy zamkniętej TYPES (podmiot|osoba|sprawa|umowa|temat|meta).
2. `sources` niepusty na każdej stronie-koncepcie (inwariant 1, audytowalność).
3. Każdy `envelope_id` z `sources` istnieje w bazie `kb` (SELECT-only,
KB_DSN z env) -- pomijalne flagą --no-db.
4. Każdy `chunks: [...]` id, jeśli podany, istnieje w `document_chunk` i
należy do tego samego `envelope_id`.
Adaptacja przy przeniesieniu (rzeczywisty layout `kb-wiki`, którego lipcowy
proof-of-concept nie miał w zestawie testowym): `README.md` (root) i
`_meta/lint-reports/*.md` istniały w prawdziwym repo od 2026-07-21 bez
frontmattera stron-konceptów (to dokumentacja/raporty, nie skompilowane
encje) wyjęte z wymogu frontmattera, tak samo jak pliki zarezerwowane, ale
bez ograniczenia "tylko root index.md może mieć pola".
To NIE jest scripts/kb/check_okf.py z homelab-codex-ws (ten ma SCOPE=("kb",
"docs/sessions") na sztywno i nie widzi layoutu kb-wiki -- patrz
_meta/conventions.md §10 dla rozjazdu). Ten plik jest osobnym walidatorem
tego repo, nie próbą dopasowania tamtego.
Tylko biblioteka standardowa poza opcjonalnym asyncpg (dociągane tylko gdy
weryfikacja bazy jest włączona). Uruchomienie:
python3 check_okf.py [katalog-repo] [--no-db]
"""
from __future__ import annotations
import asyncio
import os
import sys
from pathlib import Path
RESERVED = {"index.md", "log.md"}
# README.md (root) i INDEX.md (root, nawigacja — OKF-owy index.md nie istnieje
# w tym repo, patrz kb-m5-faza5-wiki.md §3, "do rozstrzygnięcia przez
# operatora") istniały bez frontmattera stron-konceptów od 2026-07-21 —
# dokumentacja/nawigacja, nie skompilowana encja.
NO_FRONTMATTER_REQUIRED = {"README.md", "INDEX.md"}
TYPES = {"podmiot", "osoba", "sprawa", "umowa", "temat", "meta"}
STATUSES = {"active", "archived"}
def split_frontmatter(text: str) -> tuple[str | None, str]:
"""Zwraca (blok frontmattera lub None, reszta dokumentu)."""
if not text.startswith("---\n"):
return None, text
end = text.find("\n---", 3)
if end == -1:
raise ValueError("blok frontmattera otwarty '---', ale nigdy nie zamknięty")
return text[4:end], text[end + 4 :]
def parse_yaml(block: str) -> dict[str, object]:
"""Minimalny parser mapy YAML najwyższego poziomu (skalar/lista inline/lista
blokowa; wystarczające dla frontmatterów tego repo, w tym listy map
`sources:` z zagnieżdżonym `envelope_id`/`chunks`)."""
out: dict[str, object] = {}
key: str | None = None
current_item: dict[str, object] | None = None
for lineno, raw in enumerate(block.splitlines(), start=2):
line = raw.rstrip()
if not line.strip() or line.lstrip().startswith("#"):
continue
indent = len(line) - len(line.lstrip(" "))
stripped = line.strip()
if indent > 0:
if stripped.startswith("- "):
if key is None:
raise ValueError(f"linia {lineno}: element listy bez klucza")
if not isinstance(out.get(key), list):
out[key] = []
rest = stripped[2:]
if ":" in rest and not (rest.startswith('"') or rest.startswith("'")):
# element listy = mapa (np. sources: - envelope_id: ... )
current_item = {}
out[key].append(current_item) # type: ignore[union-attr]
k2, _, v2 = rest.partition(":")
current_item[k2.strip()] = scalar(v2.strip())
else:
current_item = None
out[key].append(scalar(rest)) # type: ignore[union-attr]
continue
# wcięta linia bez "- " -- kolejne pole bieżącego elementu-mapy listy
if current_item is not None and ":" in stripped:
k2, _, v2 = stripped.partition(":")
current_item[k2.strip()] = scalar(v2.strip())
continue
raise ValueError(f"linia {lineno}: nieoczekiwane wcięcie: {line!r}")
current_item = None
if ":" not in line:
raise ValueError(f"linia {lineno}: brak ':' w {line!r}")
key, _, value = line.partition(":")
key = key.strip()
if not key:
raise ValueError(f"linia {lineno}: pusty klucz w {line!r}")
out[key] = scalar(value.strip())
return out
def scalar(value: str) -> object:
value = value.strip()
if not value:
return ""
if value.startswith("[") and value.endswith("]"):
inner = value[1:-1].strip()
return [scalar(v) for v in inner.split(",")] if inner else []
if len(value) >= 2 and value[0] == value[-1] and value[0] in "\"'":
return value[1:-1]
return value
def as_list(value: object) -> list:
if value in ("", None):
return []
if isinstance(value, list):
return value
return [value]
def check_file(path: Path, root: Path) -> tuple[list[str], list[tuple[str, list]]]:
"""Zwraca (błędy, [(envelope_id, chunks), ...] do weryfikacji w bazie)."""
rel = path.relative_to(root).as_posix()
errors: list[str] = []
to_verify: list[tuple[str, list]] = []
text = path.read_text(encoding="utf-8")
try:
block, _ = split_frontmatter(text)
except ValueError as exc:
return [f"{rel}: {exc}"], []
if (rel.count("/") == 0 and path.name in NO_FRONTMATTER_REQUIRED) or (
rel.startswith("_meta/lint-reports/")
):
return [], []
if path.name in RESERVED:
is_root_index = rel == "index.md"
if block is None:
return [], []
if not is_root_index:
return [f"{rel}: plik zarezerwowany nie może mieć frontmattera (OKF §6/§11)"], []
try:
fm = parse_yaml(block)
except ValueError as exc:
return [f"{rel}: niepoprawny YAML we frontmatterze — {exc}"], []
extra = set(fm) - {"okf_version"}
if extra:
errors.append(
f"{rel}: root index.md może mieć wyłącznie okf_version, "
f"znaleziono też: {', '.join(sorted(extra))} (OKF §11)"
)
elif not str(fm.get("okf_version", "")).strip():
errors.append(f"{rel}: puste okf_version (OKF §11)")
return errors, []
if block is None:
return [f"{rel}: brak bloku frontmattera YAML (OKF §9.1)"], []
try:
fm = parse_yaml(block)
except ValueError as exc:
return [f"{rel}: niepoprawny YAML we frontmatterze — {exc}"], []
doc_type = str(fm.get("type", "")).strip()
if not doc_type:
errors.append(f"{rel}: brak niepustego pola `type` (OKF §9.2)")
elif doc_type not in TYPES:
errors.append(f"{rel}: `type` = {doc_type!r} spoza listy ({', '.join(sorted(TYPES))})")
status = str(fm.get("status", "")).strip()
if status and status not in STATUSES:
errors.append(f"{rel}: `status` = {status!r} spoza {sorted(STATUSES)}")
# _meta/conventions.md jest samo type: temat, ale bez sources -- to jest
# dokument o konwencjach, nie strona-koncept skompilowana z retrievalu.
# Reguła "sources niepuste" dotyczy stron w podmioty/osoby/sprawy/umowy/tematy
# poza _meta/.
is_meta = rel.startswith("_meta/")
sources = as_list(fm.get("sources"))
if not is_meta:
if not sources:
errors.append(f"{rel}: `sources` puste — inwariant 1 (audytowalność) wymaga ≥1 wpisu")
for item in sources:
if not isinstance(item, dict) or not str(item.get("envelope_id", "")).strip():
errors.append(f"{rel}: wpis `sources` bez `envelope_id`: {item!r}")
continue
env_id = str(item["envelope_id"])
chunks = item.get("chunks", [])
chunks = as_list(chunks) if not isinstance(chunks, list) else chunks
to_verify.append((env_id, [str(c) for c in chunks if str(c).strip()]))
return errors, to_verify
async def verify_sources_in_db(pairs: list[tuple[str, list]], dsn: str) -> list[str]:
try:
import asyncpg
except ImportError:
return ["[db] moduł `asyncpg` niedostępny — pomiń weryfikację bazy albo zainstaluj asyncpg"]
errors: list[str] = []
conn = await asyncpg.connect(dsn)
try:
env_ids = sorted({env for env, _ in pairs})
rows = await conn.fetch(
"SELECT id FROM envelope WHERE id = ANY($1::text[])", env_ids
)
existing_envs = {r["id"] for r in rows}
for env in env_ids:
if env not in existing_envs:
errors.append(f"[db] envelope_id nie istnieje w bazie: {env!r}")
chunk_ids = sorted({int(c) for _, chunks in pairs for c in chunks if c.isdigit()})
non_numeric = sorted({c for _, chunks in pairs for c in chunks if not c.isdigit()})
for c in non_numeric:
errors.append(f"[db] chunk id nie jest liczbą całkowitą: {c!r}")
if chunk_ids:
crows = await conn.fetch(
"SELECT id, envelope_id FROM document_chunk WHERE id = ANY($1::bigint[])",
chunk_ids,
)
chunk_owner = {r["id"]: r["envelope_id"] for r in crows}
for env, chunks in pairs:
for c in chunks:
if not c.isdigit():
continue
cid = int(c)
if cid not in chunk_owner:
errors.append(f"[db] chunk id nie istnieje w document_chunk: {cid}")
elif chunk_owner[cid] != env:
errors.append(
f"[db] chunk id {cid} należy do envelope {chunk_owner[cid]!r}, "
f"nie do {env!r} podanego w sources"
)
finally:
await conn.close()
return errors
def main() -> int:
args = [a for a in sys.argv[1:] if not a.startswith("--")]
no_db = "--no-db" in sys.argv[1:]
root = Path(args[0] if args else Path(__file__).parent).resolve()
files = sorted(p for p in root.rglob("*.md") if ".git" not in p.parts)
print(f"Repo: {root}")
print(f"Sprawdzono plików .md: {len(files)}")
if not files:
print("BRAK plików .md — nie ma czego walidować.")
return 1
concepts = [p for p in files if p.name not in RESERVED]
reserved = [p for p in files if p.name in RESERVED]
print(f" koncepty: {len(concepts)}, zarezerwowane: {len(reserved)}")
errors: list[str] = []
all_pairs: list[tuple[str, list]] = []
by_type: dict[str, int] = {}
for path in files:
errs, pairs = check_file(path, root)
errors.extend(errs)
all_pairs.extend(pairs)
try:
block, _ = split_frontmatter(path.read_text(encoding="utf-8"))
if block and path.name not in RESERVED:
t = str(parse_yaml(block).get("type", "?"))
by_type[t] = by_type.get(t, 0) + 1
except ValueError:
pass
if by_type:
print(" per type: " + ", ".join(f"{t}={n}" for t, n in sorted(by_type.items())))
n_sources = len(all_pairs)
n_degraded = sum(1 for _, chunks in all_pairs if not chunks)
print(f" przypisów sources: {n_sources} ({n_degraded} envelope-only / bez chunków)")
print()
db_errors: list[str] = []
if all_pairs and not no_db:
dsn = os.environ.get("KB_DSN", "")
if not dsn:
errors.append(
"[db] KB_DSN nie ustawione w env — weryfikacja envelope_id/chunk_id pominięta "
"(ustaw KB_DSN albo uruchom z --no-db żeby nie traktować tego jako błąd)"
)
else:
db_errors = asyncio.run(verify_sources_in_db(all_pairs, dsn))
errors.extend(db_errors)
if errors:
print(f"NIEZGODNE z OKF v0.1 (kb-wiki) — {len(errors)} problem(ów):")
for err in errors:
print(f"{err}")
return 1
print("ZGODNE z OKF v0.1 (kb-wiki): frontmatter parsowalny, type/status poprawne,")
print(f"sources niepuste, wszystkie {n_sources} przypisów zweryfikowane w bazie.")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -1,110 +0,0 @@
---
title: Paweł Cesar Sanjuan Szklarz
type: osoba
status: active
tags: [znajomy, mimuw, cosmose, alias-resolution]
sources:
- envelope_id: "4022216C.2000303@zodiac.mimuw.edu.pl"
chunks: [172924]
- envelope_id: "200603150843.02302.pawel.szklarz@cc.com.pl"
chunks: [111282]
- envelope_id: "CAGReoCTiyNKmHzHFpUSWap+sr93rSJV=MF4eUXNiNUTspsVCZQ@mail.gmail.com"
chunks: [309305]
- envelope_id: "CAFYN7ZhtTxGhzL6GcH8BK-Farrxiuap9yFNn_FND01XM5d9qfQ@mail.gmail.com"
chunks: [304788]
- envelope_id: "AANLkTik8U8mtOC0uTwBrmdDMo=-SjRNCLymQo0qGQNrD@mail.gmail.com"
chunks: [256542]
- envelope_id: "CAL9nWdE+tYafaui1FvYktvT6ck_BuVqNmCCKJxCVLp5RFXVfEw@mail.gmail.com"
chunks: [324806]
- envelope_id: "CAL9nWdFVmc27gJWr=dChxT4HzX_tiHO7UT_cRtrkTCypzSFEhg@mail.gmail.com"
chunks: [324958]
- envelope_id: "CAGReoCRh5XZcvHMOJgX-erD3=Rf9kSb2_8k6RP_AB0R3ySpqzQ@mail.gmail.com"
chunks: [309064]
- envelope_id: "CAGReoCRkvUZ8XEbggiNVmOA1RMa5PKqJSyEN+_C2UBekRy+jbA@mail.gmail.com"
chunks: [309079]
- envelope_id: "CAGReoCRJ8_jnjN=vrXUt6qUz9DcBbkqAERDC3DFSq4dfVhcp+g@mail.gmail.com"
chunks: [309073]
- envelope_id: "CAGReoCQBhErFwXuhBC4JqPr1ieezDPjYBBtR2PjR2pr0Q9wFPA@mail.gmail.com"
chunks: [308938]
- envelope_id: "157ae30e11f.c52d209812015.2749371788195312018@paweld2.eu"
chunks: [95878]
- envelope_id: "583920006.2.1393944079337.JavaMail.tomcat@uxmal"
chunks: [204835]
compiled_by: claude-sonnet-5
compiled_at: 2026-08-27
updated_at: 2026-08-27
---
# Paweł Cesar Sanjuan Szklarz
Znajomy/współpracownik operatora od 2004 do dziś (2024, ostatni potwierdzony
kontakt w indeksowanym korpusie). Test entity-resolution: w korpusie mailowym
występuje pod **co najmniej sześcioma adresami e-mail** w różnych okresach
życia, z niespójną formą wyświetlanego imienia i nazwiska nawet pod tym samym
adresem (`Paweł Cesar Sanjuan Szklarz` / `Pawel Cesar Sanjuan Szklarz` /
`Pawel Szklarz` / `Paweł Sanjuan Szklarz` — bez pełnej konsystencji, prawdopo-
dobnie zależnej od klienta pocztowego nadawcy, nie od zmiany tożsamości).
## Chronologia po adresie/domenie
| Okres | Adres/domena | Kontekst | Dowód |
|---|---|---|---|
| 20042005 | `ps189466@zodiac.mimuw.edu.pl`, `@students.mimuw.edu.pl` | Student MIMUW (Wydział Matematyki, Informatyki i Mechaniki UW) razem z operatorem — wspólne zajęcia ("bazy danych", "MWiUDwNPiS") | [^4022216C.2000303@zodiac.mimuw.edu.pl#172924] |
| 20052006 | `pawel.szklarz@cc.com.pl` | Krąg znajomych/lista `all@cc.com.pl`; obrona pracy (dyplomowej, najpewniej licencjackiej/magisterskiej) 2006-03-15 — domena, nie potwierdzony pracodawca | [^200603150843.02302.pawel.szklarz@cc.com.pl#111282] |
| 20102012 | `pawel.szklarz@outbox.pl` | Współpraca zawodowa: utrzymanie programu „Plan" dla klienta (kontekst: IMGW — Instytut Meteorologii i Gospodarki Wodnej; kontakt serwisowy `support@okit.pl`), commit-y na branch `bugfix` projektu „rsw" | [^CAGReoCTiyNKmHzHFpUSWap+sr93rSJV=MF4eUXNiNUTspsVCZQ@mail.gmail.com#309305] [^CAFYN7ZhtTxGhzL6GcH8BK-Farrxiuap9yFNn_FND01XM5d9qfQ@mail.gmail.com#304788] |
| 20102015 | `pawel.szklarz@caimandesign.eu` | 2 koperty w indeksowanym korpusie — treść techniczna (parsowanie Javy) i osobista (wakacje z dziećmi, 2015); charakter/właściciel domeny **nieustalony** wprost z treści | [^AANLkTik8U8mtOC0uTwBrmdDMo=-SjRNCLymQo0qGQNrD@mail.gmail.com#256542] |
| 20152016 | `pawel@cosmose.co` | Współpracownik w Cosmose (patrz [[mbank]] — bez związku; właściwy sąsiad to encja `cosmose`, poza zakresem Etapu 1): dołączył do zespołu 2015-10-14 na zaproszenie Mirona Mironiuka, praca inżynierska (klaster Kafka, konfiguracja `cosmose-cluster` na Bitbucket) | [^CAL9nWdE+tYafaui1FvYktvT6ck_BuVqNmCCKJxCVLp5RFXVfEw@mail.gmail.com#324806] [^CAL9nWdFVmc27gJWr=dChxT4HzX_tiHO7UT_cRtrkTCypzSFEhg@mail.gmail.com#324958] |
| 20052024 (ciągłe) | `paweld2@gmail.com` | Adres osobisty, jedyny obecny w każdym okresie — nośnik relacji przyjacielskiej niezależnie od pracy/miejsca | (patrz wiersze wyżej i „2024" niżej) |
| 2024 | `paweld2@gmail.com` | Odwiedziny w Warszawie (lipiec 2024); w trakcie przeprowadzki do Hagi (Holandia) z Belgii — "ostateczne lądowanie", zakup mieszkania, przeprowadzka początek sierpnia 2024; nowy numer telefonu z prefiksem +32 (Belgia) w chwili pisania | [^CAGReoCRh5XZcvHMOJgX-erD3=Rf9kSb2_8k6RP_AB0R3ySpqzQ@mail.gmail.com#309064] [^CAGReoCRkvUZ8XEbggiNVmOA1RMa5PKqJSyEN+_C2UBekRy+jbA@mail.gmail.com#309079] [^CAGReoCRJ8_jnjN=vrXUt6qUz9DcBbkqAERDC3DFSq4dfVhcp+g@mail.gmail.com#309073] |
## Rodzina
Ma co najmniej jedno dziecko już w 2015 (wzmianka o wakacjach "z dziećmi",
Hiszpania), i córkę w wieku licealnym we wrześniu 2024 — zainteresowaną
pracą testera oprogramowania, z ukończonym kursem na Coursera, dwujęzyczną
(polski + niderlandzki C2, 5 lat nauki w Belgii) [^CAGReoCQBhErFwXuhBC4JqPr1ieezDPjYBBtR2PjR2pr0Q9wFPA@mail.gmail.com#308938].
**Imię córki celowo pominięte** — dane osobowe osoby trzeciej (małoletniej),
niepotrzebne dla tożsamości strony.
## Niepewne / sprzeczne
- **`cc.com.pl` i `caimandesign.eu` — charakter domeny nieustalony.** Obie
domeny pojawiają się w kontekstach mieszanych (towarzyskim i
zawodowym/technicznym) bez jawnego stwierdzenia "to jest mój pracodawca" w
żadnym przeczytanym chunku. Nie zgaduję pracodawcy tam, gdzie źródło tego
wprost nie mówi — w przeciwieństwie do `outbox.pl` (2010-2012), gdzie
kontekst klienta/projektu jest jawny w treści, i `cosmose.co`
(2015-2016), gdzie zaproszenie "on board" jest jawne.
- **Fałszywe trafienia pod tym samym korzeniem domeny —
`paweld2.eu`.** Adresy `sales@paweld2.eu` (wyświetlana nazwa "Speedy
Gonzalez", ankieta po szkoleniu Docker firmy PMsoft, 2016-10-10)
[^157ae30e11f.c52d209812015.2749371788195312018@paweld2.eu#95878] i
`niezbednik@paweld2.eu` (rejestracja do serwisu "Niezbednik Inteligenta",
2014-03-04) [^583920006.2.1393944079337.JavaMail.tomcat@uxmal#204835]
dzielą korzeń `paweld2` z jego głównym adresem osobistym
(`paweld2@gmail.com`), ale treść obu **nie ma żadnego związku** z osobą —
to zautomatyzowane wiadomości systemowe niepowiązanych usług, które
przypadkiem trafiają pod bardzo podobny człon domeny/nazwy użytkownika.
**Świadomie wykluczone jako dowód tożsamości**, mimo że oba są prawdziwymi
kopertami w bazie (`sources:` wpisane celowo, z prawdziwym `envelope_id` i
`chunk_id` — żeby ten negatywny wynik był tak samo audytowalny jak każdy
pozytywny fakt na tej stronie, nie tylko opisany prozą). To jest dokładnie
ten rodzaj fałszywego dopasowania, którego ma unikać ręczny przegląd
bootstrapu (audyt §4 punkt 2: "pierwsze przebiegi *będą* mylić się").
- **Niespójna forma wyświetlanego imienia pod tym samym adresem.** Pod
`paweld2@gmail.com` występują naprzemiennie `Paweł Cesar Sanjuan Szklarz`,
`Pawel Cesar Sanjuan Szklarz`, `Pawel Szklarz`, `PAwel Szklarz` — różnice
kapitalizacji/skrótu nazwiska, nie różne osoby (potwierdzone treścią: ten
sam ton, te same wątki rozmów). Nie traktowane jako sprzeczność
merytoryczna, tylko jako obserwacja o jakości danych nagłówkowych —
odnotowane, bo to dokładnie ten szum, którego prostą deduplikację po
polu "name" by zawiodła.
## Powiązane
Brak wspólnego dowodu w korpusie z [[fll-2025-26]] (sprawdzone
wprost). Naturalny sąsiad grafu to encja `cosmose` (podmiot, #7 listy
zalążkowej audytu) i `miron-mironiuk` (osoba, #8) — obie poza zakresem
Etapu 1 (tylko 3 strony), dobry kandydat do Etapu 2 właśnie dlatego, że
`pawel-cesar-sanjuan-szklarz` już ma z nimi bezpośrednie dowody w chunkach
2015-10-16/2015-10-19 wyżej.

View file

@ -1,125 +0,0 @@
---
title: mBank
type: podmiot
status: active
tags: [bank, finanse]
sources:
- envelope_id: "000101c5d667$29125510$0e03180a@multibank.pl"
chunks: [39690]
- envelope_id: "000601c63bb8$f742a8c0$0e03180a@multibank.pl"
chunks: [39818]
- envelope_id: "000d01c63c4b$a90ccd00$0e03180a@multibank.pl"
chunks: [40146]
- envelope_id: "CHILKAT-MID-ae002ee3-2ae4-4eda-b6b2-18099c8e0565@mm1"
chunks: [338987]
- envelope_id: "07ea061e0c113a0369.36fdf8b7ee7446a3a17bf6ceb042adf7@mbank.pl"
chunks: [389929]
- envelope_id: "07ea07170529210109.c26ca6ba0c294a0cbc5d6a286b9b93f1@mbank.pl"
chunks: [389932]
- envelope_id: "511ff8c6-1879-4a0a-a219-cee33f1a13b1@mbank.pl"
chunks: [391147]
- envelope_id: "8f5f44db-473e-4ccc-9713-e6c83d68e2b1@mbank.pl"
chunks: [391591]
- envelope_id: "e3f5ec66-9ffe-4a83-81b0-1301c2a807bd@mbank.pl"
chunks: [392767]
- envelope_id: "bb43b2ba-1e28-4c03-8460-8d42f89ef2ad@mbank.pl"
chunks: [392017]
- envelope_id: "07ea0805111d3802fa.fb94d6df606e4de284ebd75d80159ded@mbank.pl"
chunks: [389934]
compiled_by: claude-sonnet-5
compiled_at: 2026-08-27
updated_at: 2026-08-27
---
# mBank
Relacja bankowa **kontakt@mbank.pl → oskar.kapala@gmail.com** obejmująca
1099 kopert w korpusie mailowym, od 2005-12-13 do 2026-08-05 — najdłuższa
ciągła relacja instytucjonalna widoczna w indeksowanym korpusie (retrieval
wg §3 audytu, przed kompilacją tej strony). Poniżej wyłącznie zdarzenia z
bezpośrednią treścią w chunku (nie surowy wolumen).
## Początek relacji (20052006)
Pierwszy zidentyfikowany kontakt: wniosek o kartę kredytową **Visa Electron**
mBanku, złożony 2005-10-21 [^000101c5d667$29125510$0e03180a@multibank.pl#39690].
Drugi wniosek, o kartę **Visa Classic**, złożony 2006-02-27 o 16:56:59, numer
wniosku **C42567757785**
[^000601c63bb8$f742a8c0$0e03180a@multibank.pl#39818]; wstępnie rozpatrzony
pozytywnie następnego dnia (2006-02-28)
[^000d01c63c4b$a90ccd00$0e03180a@multibank.pl#40146]. Od sierpnia 2006 bank
wysyła cykliczne elektroniczne zestawienia operacji (miesięczne wyciągi z
konta i karty kredytowej) — pierwsze znalezione w korpusie: zestawienie za
lipiec 2006, wysłane 2006-08-02
[^CHILKAT-MID-ae002ee3-2ae4-4eda-b6b2-18099c8e0565@mm1#338987].
## Stan bieżący (2026)
Na dzień kompilacji strony aktywne co najmniej: rachunek osobisty z
preferencyjnym oprocentowaniem (wniosek **KOS115501787**, zaakceptowany
2026-06-30) [^07ea061e0c113a0369.36fdf8b7ee7446a3a17bf6ceb042adf7@mbank.pl#389929]
oraz co najmniej jeden kredyt — bank potwierdza to wprost w mailu o zmianie
regulaminu (2026-07-23): *"Otrzymujesz ten e-mail, ponieważ masz przynajmniej
jeden kredyt w mBanku (sprawdziliśmy to na dzień 9 lipca 2026 r.)"*, ze
zmienionym „Regulaminem rachunków dla osób fizycznych i klientów Private
Banking" wchodzącym w życie 26.08.2026
[^07ea07170529210109.c26ca6ba0c294a0cbc5d6a286b9b93f1@mbank.pl#389932].
## Konto dla dziecka (sierpień 2026)
2026-08-05 bank rejestruje **dwa** wnioski o eKonto możliwości dla dziecka
(**HELENA**) — wniosek **RAC144473366**
[^511ff8c6-1879-4a0a-a219-cee33f1a13b1@mbank.pl#391147] i wniosek
**RAC193823242**
[^8f5f44db-473e-4ccc-9713-e6c83d68e2b1@mbank.pl#391591], oba tego samego
dnia. Wniosek RAC193823242 zostaje tego samego dnia wycofany na żądanie
[^e3f5ec66-9ffe-4a83-81b0-1301c2a807bd@mbank.pl#392767]; wniosek RAC144473366
przechodzi do podpisania umowy w placówce (mKiosk, Galeria KEN Center, ul.
Ciszewskiego 15, Warszawa)
[^bb43b2ba-1e28-4c03-8460-8d42f89ef2ad@mbank.pl#392017] i tego samego dnia
zostaje potwierdzony jako aktywny, z kartą w drodze
[^07ea0805111d3802fa.fb94d6df606e4de284ebd75d80159ded@mbank.pl#389934]. Dwa
równoległe wnioski tego samego dnia dla tej samej osoby czytane jako
duplikat rozwiązany przez bank tego samego dnia — nie sprzeczność (patrz
niżej, dlaczego to *nie* trafiło do "Niepewne/sprzeczne").
## Niepewne / sprzeczne
- **Dwa równoległe wnioski o konto dla dziecka 2026-08-05 — czytelnie
rozstrzygnięte, nie umieszczone jako sprzeczność.** Oba dowody
(RAC144473366 aktywny, RAC193823242 wycofany) pochodzą z tego samego dnia
i wprost się uzupełniają (bank potwierdza rezygnację z jednego, aktywację
drugiego) — to jest przypadek "nowy dowód wprost aktualizuje poprzedni",
nie sprzeczność w rozumieniu konwencji (`_meta/conventions.md` §7) — stąd
w treści głównej, nie tutaj. Odnotowane tu wyłącznie jako uzasadnienie tej
klasyfikacji, na wypadek gdyby przyszła kompilacja tej strony chciała ją
zakwestionować.
- **Mail z 2006 (chunki 39818, 40146) ma widoczne uszkodzenia kodowania**
(znaki zastępcze `<60>` w miejscu polskich liter: "Dzi<7A>kujemy", "z<>o<EFBFBD>enie") —
ten sam typ artefaktu co ~12 kopert opisanych w
`kb/incidents/2026-08-26-mail-sync-20-dni-ciszy.md` §6 (poza zakresem
naprawy). Fakty liczbowe (numer wniosku, data, godzina) pozostają czytelne
i niedwuznaczne — cytowane mimo uszkodzenia, bo weryfikowalne wprost z
tego samego, nieuszkodzonego fragmentu tekstu.
- **Fałszywe trafienie przy wyszukiwaniu po nazwie „bank" — odnotowane jako
metodologiczna uwaga, nie fakt o mBanku.** Nadawca "SystemBank"
(`powiadomienia@systembank.pl`, wielokrotnie w latach 20162018) to sklep z
akcesoriami fotograficznymi rozliczający zamówienia z Allegro
[^583ec657.9a80b.11b30.3d60@v281.home.net.pl#204842] — zero związku z
mBankiem poza przypadkową obecnością słowa "Bank" w nazwie firmy. Świadomie
wykluczony z `sources:` tej strony (podany tu tylko jako przykład ryzyka
false-positive przy prostym dopasowaniu substringu — retrieval do tej
strony filtrował po domenie `mbank.pl`/`multibank.pl`, nie po słowie
kluczowym, więc to trafienie nigdy realnie nie weszło do kompilacji; warto
jednak, żeby przyszłe kompilacje encji finansowych pamiętały o tym wzorcu).
## Powiązane
Brak wspólnego dowodu w korpusie z [[fll-2025-26]] ani
[[pawel-cesar-sanjuan-szklarz]] (sprawdzone wprost — patrz notatka
w `fll-2025-26.md` „Powiązane"). Paweł Cesar Sanjuan Szklarz ma **własne**,
niezależne konto w mBanku (numer rachunku widoczny w jego własnej
korespondencji z operatorem, 2013) — to koincydencja skali usługi
(mBank to jeden z największych banków detalicznych w Polsce), nie
rzeczywisty punkt styku tych dwóch stron; nie tworzę na tej podstawie
cross-linku.