Compare commits

..

8 commits

Author SHA1 Message Date
oskar a6d35ba034 docs(kb): session log 2026-08-27 — faza5 Etap 1 + rekoncyliacja kb-wiki 2026-08-27 20:17:21 +02:00
oskar 308ae6a9f4 docs(kb): faza5-wiki §6 — rozliczenie rekoncyliacji kb-wiki (2026-08-27)
Operator zdecydował: opcja 1 z §4 (scalić). Wykonane w klonie roboczym
~/kb-wiki (repo prawdziwe, master, poza tym worktree) — cztery commity
lokalne, świadomie nie pushnięte, czekają na review operatora:

1. sprawy/fll-2025-26.md — scalone przed tą sesją (commit c4127b9 w
   kb-wiki, już pushnięty), ta sesja tylko zweryfikowała ponownie.
2. podmioty/mbank.md i osoby/pawel-cesar-sanjuan-szklarz.md przeniesione z
   ~/kb-wiki-etap1-sesja-2026-08-27/ bez kolizji, linki [[...]] przepisane
   na rzeczywistą konwencję repo (bez prefiksu katalogu).
3. check_okf.py (walidator sesyjny) przeniesiony do kb-wiki, zaadaptowany
   do layoutu repo (type=meta, wyjątek README/INDEX/lint-reports).
4. Decyzje (d)-(f) audytu 08-26 (fallback chunk-id, sekcja "Niepewne/
   sprzeczne" + polityka aktualizacji, inwariant 7) scalone do
   _meta/conventions.md bez duplikowania tego, co repo już miało.

Stan końcowy: 7 stron w repo, 74/74 sources + 150/150 przypisów inline
zweryfikowanych wprost w bazie (KB_DSN, SELECT-only), zero wiszących
linków. Pełny raport: _meta/lint-reports/2026-08-27-rekoncyliacja.md w
kb-wiki. ~/kb-wiki-etap1-sesja-2026-08-27/ pozostawione nietknięte na
żądanie operatora.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7AnxL6pbgySpEgcCwAfww
2026-08-27 19:57:39 +02:00
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
oskar 2df4f1dbc3 fix(kb): check_okf.py flags control bytes (NUL etc.) outside \t\n\r
The validator only ever parsed frontmatter fields — it never scanned body
content, so a file with literal NUL bytes pasted in from a psql SELECT
(kb/audits/wiki-kompilat-recon-2026-08-26.md) passed silently. Adds a
whole-file scan for control bytes other than tab/newline/CR, one error per
offending line. Covered by scripts/kb/tests/test_check_okf.py.
2026-08-27 15:53:35 +02:00
oskar e20845ae1b docs(kb): dopisz inwariant 7 (izolacja retrievalu kompilacji od source=wiki) do faza3 §8.1
Decyzja (f) z kb/audits/wiki-kompilat-recon-2026-08-26.md §10, zatwierdzona
przez operatora w całości 2026-08-27. Kompilacja strony wiki nigdy nie czyta
source='wiki' jako dowodu (exclude_sources=('wiki',)); tylko /search
(warstwa użytkownika, po syntezie odpowiedzi) widzi wiki w kaskadzie —
mitygacja self-citation/citogenesis przy źródle retrievalu.

Dopisane jako rozszerzenie po inwariantach 1-6, które pozostają nietknięte
(szkic operatora 2026-07-15 nadal "NIE podlega zmianie").
2026-08-27 15:53:35 +02:00
oskar d8ff94ca1b docs(kb): oznacz decyzje (a)-(h) wiki-kompilat-recon jako zatwierdzone, usuń wklejone bajty NUL
Operator zatwierdził pakiet (a)-(h) z §10 w całości bez zmian 2026-08-27.
Plik zawierał 4 dosłowne bajty NUL (offsety ~37383/37891/41544/47333) —
wklejki z SELECT-ów po korpusie mailowym — które sprawiały, że narzędzia
tekstowe (grep) traktowały plik jako binarny; usunięte, treść w tych
miejscach pozostaje czytelna (puste `` obok `�` — oba demonstrują literalnie
bajt, którego dotyczą).
2026-08-27 15:53:35 +02:00
oskar 17e7cb0aea docs: session 2026-08-27 13:49 2026-08-27 15:49:48 +02:00
oskar a97cec0819 merge: task/drobne-fixy (resolve-requests 775, prune out of health-monitor, events doc, redeploy action_id) 2026-08-27 15:45:17 +02:00
7 changed files with 508 additions and 7 deletions

View file

@ -0,0 +1,59 @@
---
okf: "0.1"
type: session-log
visibility: private
status: active
updated: 2026-08-27
links:
- ../../kb/phases/kb-m5-faza5-wiki.md
- ../../kb/phases/kb-m5-faza3.md
- ../../kb/audits/wiki-kompilat-recon-2026-08-26.md
---
# Sesja 2026-08-27 — Faza 5: decyzje + Etap 1 + rekonсyliacja (ZAMKNIĘTY)
## Timeline
- **Sanity** — automat mailowy zdrowy po nocy (last_success świeży, gmail
max(ts) bieżący).
- **Formalizacja decyzji** (2df4f1d i wcześniejsze) — pakiet (a)-(h) audytu
`wiki-kompilat-recon-2026-08-26.md` ZATWIERDZONY w całości; inwariant 7
dopisany do faza3 §8.1 (izolacja retrievalu kompilacji od `source='wiki'`);
`check_okf.py` łapie bajty kontrolne (nowy check #12 + testy) — 4 NUL-e
usunięte z samego audytu.
- **Etap 1** (`task/wiki-etap1`, autonomiczny run CC ~25 min) — bootstrap +
3 strony proof (`sprawy/fll-2025-26`, `podmioty/mbank`,
`osoby/pawel-cesar-sanjuan-szklarz`). 40 sources + 47 przypisów
zweryfikowanych byte-for-byte, zero degradacji. Alias-resolution: 6 er
życia Pawła spójnie + 2 fałszywe pozytywy pod `paweld2.eu` wykryte i
udokumentowane jako wynik negatywny (kluczowy test mechanizmu PASS).
- **ODKRYCIE** — kb-wiki istniało na Forgejo od 2026-07-21 (5 stron: `pzu`,
`warta`, `ubezpieczenie-auto`, `fll-2025-26`, `wspólnota`) — proof
wykonany w fazie 3 (wątek "Kontynuacja wątku o KB", decyzja D7),
nieodnotowany w `kb/` ani w audycie. Recon 08-26 błędnie stwierdził
"remote nie istnieje" — sprawdził repo/pilota/bazę, nie odpytał Forgejo
bezpośrednio.
- **LEKCJA 1**: fakty wykonania muszą lądować w repo (lekcja 6 narty27 w
praktyce — zgubiliśmy całe repo na 5 tygodni).
- **LEKCJA 2**: recon zasobów zewnętrznych odpytuje źródło wprost, nie
wnioskuje z planów.
- **Rekonсyliacja** — porównanie dwóch niezależnych kompilacji `fll-2025-26`
(lipiec paperless-only vs sierpień paperless+gmail): komplementarne, zero
sprzeczności w faktach wspólnych; konwergencja metodologiczna (obie sesje
ten sam chunk 277/278, obie odmówiły potwierdzenia nieczytelnego OCR).
Scalenie: 21 envelope, 38 par sources, 45 przypisów re-zweryfikowanych;
naprawiony wadliwy lipcowy przypis; konwencja "Brak danych w KB"
sformalizowana. Potem mbank+paweł przeniesione do kanonicznego repo,
konwencje (d)-(f) scalone, walidator w repo. Stan końcowy kb-wiki@Forgejo
`a540a99`: 7 stron, lint 74/74 sources + 150/150 inline zielono.
- **kb-wiki remote** przepięty HTTPS→SSH (port 222).
- **Kandydat Etapu 2** wykryty SQL-em: `podmioty/future-minds` (organizator
FLL, 8+ dokumentów).
- **Follow-upy bez zmian**: rotacja sekretów (kb-postgres + `TG_TOKEN`
NADAL WISI), fix UID SEARCH, PDF-y, charset, R1/R2 z incydentu 26.08.
## Stan Fazy 5
- Etap 1 ✓ (2026-08-27). Next: Etap 2 — skala do 17+1 encji, lint w kodzie,
skan charset/NUL, decyzja o integracji `source='wiki'` w retrievalu
(inwariant 7 obowiązuje).

View file

@ -0,0 +1,23 @@
## Session 13:49
### Commits
a97cec0 merge: task/drobne-fixy (resolve-requests 775, prune out of health-monitor, events doc, redeploy action_id)
1dca438 fix(supervisor): apply started_at suffix to redeploy action_id too
40d78ce docs: fix events layout drift in CLAUDE.md
9a86843 fix(monitor): remove unfiltered docker container prune from health-monitor.sh
89f75c3 fix(observer): make world/resolve-requests/ group-writable
### Files changed
CLAUDE.md | 2 +-
scripts/monitor/health-monitor.sh | 110 +++------------------
scripts/observer/observer.py | 12 +++
services/control-plane/src/supervisor.py | 40 ++++----
.../control-plane/tests/test_incident_lifecycle.py | 17 ++++
.../tests/test_supervisor_action_id_uniqueness.py | 50 +++++++++-
6 files changed, 109 insertions(+), 122 deletions(-)
### Deploys
- control-plane → VPS: tagged 4/4 images `:rollback-pre-drobnefixy`, `git pull` (03441a1→a97cec0) + `docker compose up -d --build --force-recreate` (direct, no deploy-local.sh, per 26.08 precedent). Result: 4/4 healthy, zero error/traceback in logs since restart, sha256 of observer.py and supervisor.py match HEAD, `world/resolve-requests/` mode 775 confirmed, incidents.json stable (md5 unchanged) over 3 observer cycles. 30s permission test: flag `test-perms-123` dropped via plain `ssh` as `oskar` (no docker exec) was picked up and deleted in ~5s with `WARNING - Resolve-request flag for unknown incident test-perms-123 — removing flag` — 775 confirmed working in practice.
### Narrative
> _user-provided summary_

View file

@ -3,7 +3,7 @@ okf: "0.1"
type: audit type: audit
visibility: private visibility: private
status: active status: active
updated: 2026-08-26 updated: 2026-08-27
as_of: 2026-08-26 as_of: 2026-08-26
links: links:
- ../phases/kb-m5-faza3.md - ../phases/kb-m5-faza3.md
@ -663,7 +663,7 @@ wiki-kompilat:
- **NUL w `entities` jsonb jest poważniejszy strukturalnie** — jeśli sesja - **NUL w `entities` jsonb jest poważniejszy strukturalnie** — jeśli sesja
kompilująca odpytuje `entities` bezpośrednio (nie tylko `document_chunk.text`), kompilująca odpytuje `entities` bezpośrednio (nie tylko `document_chunk.text`),
NUL bajt w jsonb może wywrócić parser po stronie klienta (Pythonowy 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 `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 **nie zostało zweryfikowane w tym reconie** (nie testowałem odczytu takiego
wiersza) — flaguję jako `[do weryfikacji]`, nie jako potwierdzony problem. wiersza) — flaguję jako `[do weryfikacji]`, nie jako potwierdzony problem.
@ -671,7 +671,7 @@ wiki-kompilat:
(charset/NUL) — skala nie uzasadnia opóźnienia PoC. Warto natomiast, żeby (charset/NUL) — skala nie uzasadnia opóźnienia PoC. Warto natomiast, żeby
lint (inwariant 3) przy pierwszym uruchomieniu zrobił **jednorazowy skan** lint (inwariant 3) przy pierwszym uruchomieniu zrobił **jednorazowy skan**
`document_chunk`/`envelope.entities` pod kątem znaków zastępczych `document_chunk`/`envelope.entities` pod kątem znaków zastępczych
(`<60>`) i `` w źródłach, które faktycznie zasiliły PoC (17 stron (`<60>`) i `` w źródłach, które faktycznie zasiliły PoC (17 stron
§3) — tanie zabezpieczenie, żeby nie odkryć problemu dopiero przy spot-checku §3) — tanie zabezpieczenie, żeby nie odkryć problemu dopiero przy spot-checku
tygodnie później. tygodnie później.
@ -692,6 +692,10 @@ 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**) ### 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 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: (17 encji) — pierwszy etap ma zweryfikować *mechanizm*, nie pokryć korpus:
@ -721,7 +725,7 @@ Zależy od Etapu 1 (mechanizm zweryfikowany). Zakres:
semantyczna to zadanie dla CC, nie dla skryptu; skrypt robi tylko semantyczna to zadanie dla CC, nie dla skryptu; skrypt robi tylko
pre-filtr kandydatów). pre-filtr kandydatów).
2. Rozszerzenie o pozostałe encje z §3 (do 17), plus jednorazowy skan pod 2. Rozszerzenie o pozostałe encje z §3 (do 17), plus jednorazowy skan pod
kątem `<60>`/`` (§8 „follow-upy fazy mailowej") na źródłach, które kątem `<60>`/`` (§8 „follow-upy fazy mailowej") na źródłach, które
faktycznie zasiliły te strony. faktycznie zasiliły te strony.
3. **DoD etapu**: `lint.py` uruchamiany ręcznie (nie timer — zgodnie z §4, 3. **DoD etapu**: `lint.py` uruchamiany ręcznie (nie timer — zgodnie z §4,
automatyzacja poza zakresem), raport w `_meta/lint-reports/`, zero automatyzacja poza zakresem), raport w `_meta/lint-reports/`, zero
@ -743,6 +747,11 @@ Zależy od Etapu 1 (mechanizm zweryfikowany). Zakres:
## 10. Punkt 9 — Decyzje operatora ## 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. 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 ### (a) Wersja OKF dla `kb-wiki`: pin v0.1 (jak pilot) vs start od razu na v0.2
@ -822,7 +831,7 @@ mini-śledztwa kontekstu firmy.
### (h) Follow-upy fazy mailowej (charset/NUL) — nie blokują, jednorazowy skan przy Etapie 2 ### (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; **Rekomendacja:** (§8) nie opóźniać PoC na R1R3 z incydentu 20-dni-ciszy;
dopisać jednorazowy skan `<60>`/`` do zakresu Etapu 2 (lint), nie jako dopisać jednorazowy skan `<60>`/`` do zakresu Etapu 2 (lint), nie jako
osobny, wcześniejszy krok. osobny, wcześniejszy krok.
--- ---
@ -843,4 +852,5 @@ zweryfikować mechanizm entity-resolution na trudnym przypadku — aliasach —
zanim zainwestuje się w skalę), z jawnym odłożeniem pipeline'u eskalacji i zanim zainwestuje się w skalę), z jawnym odłożeniem pipeline'u eskalacji i
integracji retrievalu produkcyjnego do kolejnych etapów. integracji retrievalu produkcyjnego do kolejnych etapów.
Do decyzji operatora: (a)-(h) w §10. Decyzje: (a)-(h) w §10 — **ZATWIERDZONE przez operatora w całości 2026-08-27,
bez zmian.**

View file

@ -3,7 +3,7 @@ okf: "0.1"
type: phase type: phase
visibility: private visibility: private
status: active status: active
updated: 2026-07-17 updated: 2026-08-27
links: [] links: []
--- ---
@ -628,6 +628,17 @@ Adapter i embed są już idempotentne — nowość to wyłącznie orkiestracja i
> półprodukt kompilacji) i PO filtrze śmieciowych chunków. Pełna wiki po fazie mailowej > półprodukt kompilacji) i PO filtrze śmieciowych chunków. Pełna wiki po fazie mailowej
> (przyrostówka) — wcześniej kompilat byłby fotografią przeszłości. > (przyrostówka) — wcześniej kompilat byłby fotografią przeszłości.
**Inwariant 7 (dodany 2026-08-27 — rozszerzenie, nie zmiana inwariantów 16
powyżej, które pozostają NIE podlega zmianie):** decyzja (f),
`kb/audits/wiki-kompilat-recon-2026-08-26.md` §10, zatwierdzona przez operatora
w całości 2026-08-27. Kompilacja strony wiki **nigdy** nie czyta `source='wiki'`
jako dowodu (`exclude_sources=('wiki',)` w `cascade_retrieve`/`hybrid_retrieve`) —
retrieval na potrzeby kompilacji zawsze wyklucza wiki, czyta wyłącznie warstwę
dowodową (mail, paperless). Tylko `/search` (warstwa użytkownika, po zbudowaniu
syntezy odpowiedzi — inwariant 5, faza 5) widzi wiki w kaskadzie. Mitygacja
self-citation/citogenesis przy źródle retrievalu, nie tylko przez lint (inwariant
3) po fakcie — patrz audyt §7 dla pełnego rozumowania.
### 8.2 Rozwinięcie wykonawcze (szczegóły, decyzje architektoniczne bez zmian) ### 8.2 Rozwinięcie wykonawcze (szczegóły, decyzje architektoniczne bez zmian)
**Repozytorium** (rozstrzygnięcie punktu 6 szkicu): osobne repo `kb-wiki` — decyzja 7, **Repozytorium** (rozstrzygnięcie punktu 6 szkicu): osobne repo `kb-wiki` — decyzja 7,

View file

@ -0,0 +1,322 @@
---
okf: "0.1"
type: phase
visibility: private
status: active
updated: 2026-08-27
links:
- ../audits/wiki-kompilat-recon-2026-08-26.md
- kb-m5-faza3.md
- kb-m5-faza-mailowa.md
---
# Moduł 5, faza 5 — wiki-kompilat (Etap 1: bootstrap + proof, i **odkrycie rozjazdu**)
> **Status: Etap 1 wykonany 2026-08-27 — ale w cieniu ważniejszego znaleziska: proof-of-concept
> wiki-kompilatu (repo `kb-wiki`, 5 stron) już istniał od **2026-07-21**, na Forgejo
> (`https://forgejo.kapala.org/oskar/kb-wiki`), i `kb/audits/wiki-kompilat-recon-2026-08-26.md`
> tego nie wykrył. Ta sesja (2026-08-27) zbudowała **niezależny, równoległy** lokalny bootstrap
> (`~/kb-wiki-etap1-sesja-2026-08-27/`, 3 strony) nie wiedząc o repo z lipca — dowiedziała się
> dopiero przy Części C tego zadania, kiedy próba udokumentowania "remote nie istnieje" (cytat z
> polecenia sesji, zgodny z audytem) skłoniła do sprawdzenia Forgejo wprost. **Nic nie zostało
> wypchnięte do prawdziwego zdalnego repo** — ta sesja tylko czytała je (`git clone`, pull-only).
>
> **Rekoncyliacja domknięta 2026-08-27 (operator zdecydował: opcja 1 z §4 —
> scalić).** `kb-wiki` ma teraz **7 stron**: 5 lipcowych + `fll-2025-26`
> scalone (dwie niezależne kompilacje tej samej encji) + `mbank`/
> `pawel-cesar-sanjuan-szklarz` przeniesione bez kolizji. Cztery commity na
> `master` **lokalnie, nie pushnięte** — czekają na review operatora przed
> pushem do Forgejo. Pełne rozliczenie: §6.
---
## 0. Jak doszło do odkrycia (żeby przyszła sesja nie powtórzyła tej samej luki)
`kb/audits/wiki-kompilat-recon-2026-08-26.md` (2026-08-26, decyzje a-h zatwierdzone
2026-08-27) stwierdza wprost w nagłówku: *„Nic nie zostało zaimplementowane,
zdeployowane ani skommitowane poza tym dokumentem."* Dowody audytu: worktree
`homelab-codex-ws` cięty z mastera, pilot `~/narty-2027/saalbach-kb` (lokalny
odczyt), żywa baza `kb` na PIHA (`ssh piha 'docker exec kb-postgres psql...'`),
`systemctl status` na PIHA. **Forgejo nie było w tym zestawie dowodów** — audyt nie
odpytał API/repo Forgejo, mimo że `CLAUDE.md` i `inventory/topology.yaml` jasno
wskazują `git_provider: forgejo` jako miejsce, gdzie `kb-wiki` miało w ogóle
powstać (decyzja D7 faza3 §8.2: *„osobne repo kb-wiki"*, bez wskazania konkretnego
hosta w chwili pisania szkicu — Forgejo to naturalna, ale niejawna implikacja).
Zlecenie tej sesji (2026-08-27, Etap 1) powtórzyło za audytem: *„REMOTE NIE
ISTNIEJE — nie konfiguruj, nie pushuj; operator założy repo na Forgejo później."*
To zdanie okazało się **faktycznie nieprawdziwe** — sprawdzone dopiero przy pisaniu
tego dokumentu (Część C zlecenia), zapytaniem GET do `http://100.108.208.3:3000/
api/v1/repos/search?q=kb-wiki` (read-only, bez uwierzytelnienia, `private: false`
więc odpowiedź jawna) i następnie `git clone` (pull-only, potwierdzone uprawnieniami
API: `"push": false, "pull": true`) do katalogu tymczasowego poza tym repo.
**Wniosek na przyszłość:** *„repo nie istnieje"* w planie/audycie nie jest
wystarczające bez sprawdzenia bezpośrednio w systemie, który je hostuje
(`inventory/topology.yaml` → `git_provider: forgejo` → Forgejo API/`git ls-remote`),
nie tylko w obrębie `homelab-codex-ws` i lokalnych klonów znanych z wcześniejszych
sesji. Ten sam błąd metodologiczny mógłby się powtórzyć przy dowolnym innym „osobnym
repo" planowanym w dokumentacji, ale nigdy nie zweryfikowanym wprost u hosta.
---
## 1. Co faktycznie istnieje w `oskar/kb-wiki` (Forgejo, od 2026-07-21)
Stan wg `git clone https://forgejo.kapala.org/oskar/kb-wiki.git` (read-only,
2026-08-27), 3 commity na `master`:
```
156925c struktura: katalogi, konwencje, README
42c7731 compile: pzu, warta, ubezpieczenie-auto-ga431ja-2026, fll-2025-26, wspolnota-mieszkaniowa-targowa-2a ← kaskada retrievalu kb-postgres@PIHA
c52f867 lint: pierwszy raport ręczny 2026-07-21
```
Struktura: `README.md`, `INDEX.md` (wielka litera — inaczej niż plik zarezerwowany
OKF `index.md`, patrz §3 niżej), `_meta/conventions.md`, `_meta/lint-reports/
lint-report-2026-07-21.md`, 5 stron-encji:
| Strona | Typ | Skrót |
|---|---|---|
| `podmioty/pzu.md` | podmiot | PZU — OWU Auto/OC, karty produktu, oferta T1531938417 |
| `podmioty/warta.md` | podmiot | WARTA — OWU AC Standard/Komfort/Moje Auto |
| `sprawy/ubezpieczenie-auto-ga431ja-2026.md` | sprawa | Porównanie ofert PZU/WARTA/InterRisk, Mazda 6 (GA431JA), 06.2026 |
| `sprawy/fll-2025-26.md` | sprawa | FLL Challenge UNEARTHED, drużyna #1100, Warszawa II |
| `sprawy/wspolnota-mieszkaniowa-targowa-2a.md` | sprawa | Wspólnota Mieszkaniowa Targowa 2A (Pruszków) — zebranie roczne 2026 |
`osoby/`, `umowy/`, `tematy/` istnieją jako katalogi puste (`.gitkeep`) — **zero
stron typu `osoba`** w lipcowym proof-of-concept. To jest jedyna luka pokrycia
typów, którą Etap 1 tej sesji (§2) faktycznie domyka, nie duplikuje.
Jakość: wysoka, metodologicznie zgodna z tym, co audyt 08-26 dopiero rekomendował
(sekcja „Niepewne/sprzeczne" jako wzorzec — tam nazwana wprost inaczej, ale ta sama
funkcja; degradacja przy uszkodzonym OCR zamiast ślepego zaufania streszczeniu;
84/86 wpisów `sources[].chunks` i 73/73 przypisów inline zweryfikowanych wprost w
bazie, udokumentowane w `_meta/lint-reports/lint-report-2026-07-21.md`). Pełna
treść: `_meta/conventions.md` tamtego repo (różni się od konwencji tej sesji w
kilku miejscach nieistotnych merytorycznie — casing `INDEX.md`/`index.md`, typ
`meta` vs `temat` dla `conventions.md` — patrz §3).
**`kb-m5-faza4.md` (linia 17, `docs/sessions/2026-07-21.md`) już to dokumentowały
poprawnie** — ten fakt był w repo `homelab-codex-ws` cały czas, czytelny `git log`/
`grep -r kb-wiki`. Audyt 08-26 po prostu tego nie sprawdził (§0 wyżej).
---
## 2. Co ta sesja (2026-08-27) faktycznie zrobiła — Etap 1 wg zlecenia, **lokalnie, bez wiedzy o §1**
Zbudowany od zera, niezależnie: `~/kb-wiki-etap1-sesja-2026-08-27/` (przemianowany
z `~/kb-wiki/` po odkryciu §1 — żeby nie kolidować ścieżką z przyszłym `git clone`
prawdziwego repo). Git lokalny, **6 commitów, nigdy nie pushowany do żadnego
remote** (żaden remote nie był skonfigurowany — zgodnie z literą pierwotnego
zlecenia, które okazało się oparte na błędnym założeniu z §0).
3 strony proof, celowo zróżnicowane typy (jak żądał audyt §9 Etap 1):
| Strona | Typ | Pokrycie względem §1 |
|---|---|---|
| `sprawy/fll-2025-26.md` | sprawa | **Duplikat tej samej encji** co lipcowe repo — skompilowany niezależnie, z częściowo innym zestawem chunków (uzupełniające, nie identyczne `sources`). Wysoka zgodność wniosków — patrz §4. |
| `podmioty/mbank.md` | podmiot | **Nowe pokrycie** — nie istnieje w lipcowym repo (tam tylko PZU/WARTA, ubezpieczyciele) |
| `osoby/pawel-cesar-sanjuan-szklarz.md` | osoba | **Domyka lukę** — lipcowe repo ma katalog `osoby/` pusty; to pierwsza strona typu `osoba` w całym projekcie, jedyny dotąd test entity-resolution na aliasach |
Pełna treść stron, `_meta/conventions.md` tej sesji (napisane niezależnie od
lipcowego — patrz różnice §3), `check_okf.py` (walidator, **którego lipcowe repo
nie ma jako skomitowanego pliku** — tamta sesja weryfikowała `sources`/przypisy
ad hoc, bez zostawienia narzędzia), i `_meta/lint-reports/2026-08-27.md` — wszystko
w `~/kb-wiki-etap1-sesja-2026-08-27/`, 6 commitów (`git log --oneline`):
struktura+konwencje, walidator, 3× `compile:`, 1× `lint:`.
DoD audytu §9 Etap 1 (tabela, 4 punkty) — spełnione **lokalnie**, w tym repo
równoległym: repo istnieje z konwencjami, walidator działa (zielono, 40 par
`sources` + 47 inline przypisów zweryfikowanych wprost w `KB_DSN`), 3 strony
proof spełniają inwarianty 1-2, raport lintu ręcznego zapisany.
**Weryfikacja lint:**
```
$ python3 check_okf.py # w ~/kb-wiki-etap1-sesja-2026-08-27/
ZGODNE z OKF v0.1 (kb-wiki): [...] wszystkie 40 przypisów zweryfikowane w bazie.
```
**`scripts/kb/check_okf.py` z `homelab-codex-ws` uruchomiony na tym samym repo —
zgodnie z rozjazdem przewidzianym w zleceniu:**
```
$ python3 scripts/kb/check_okf.py ~/kb-wiki-etap1-sesja-2026-08-27
Zakres: kb, docs/sessions
Sprawdzono plików .md: 0
BRAK plików w zakresie — nie ma czego walidować.
```
`SCOPE = ("kb", "docs/sessions")` w tamtym skrypcie jest wpisany na sztywno
względem `root``kb-wiki` (żadna z dwóch kopii, lipcowa ani ta) nie ma
podkatalogu `kb/` ani `docs/sessions/`, więc walidator zawsze zwróci "brak
plików" dla tego layoutu. Nie hackowany — to jest realny, ustrukturalny rozjazd
(nie błąd w `kb-wiki`), odnotowany tu jako follow-up: albo `scripts/kb/
check_okf.py` dostaje parametr `--scope`, albo `kb-wiki` (dowolna wersja) trzyma
własny walidator na stałe (rekomendacja tej sesji — już częściowo zrobione, patrz
`check_okf.py` w repo tej sesji).
---
## 3. Różnice konwencji: lipiec vs ta sesja (nieistotne merytorycznie, warto ujednolicić)
| Aspekt | Lipiec (`oskar/kb-wiki`, prawdziwe repo) | Ta sesja (lokalne) | Rekomendacja |
|---|---|---|---|
| Plik nawigacyjny root | `INDEX.md` (duża litera), osobny od OKF-owego `index.md` (nie istnieje w ogóle w lipcowym repo — brak pliku `index.md`) | `index.md` (mała litera, OKF §11 reserved-file, pełni tę samą rolę) | Do rozstrzygnięcia przez operatora — lipcowe repo **nie ma pliku `index.md`** w ogóle, więc formalnie nie przechodzi `check_okf.py` tej sesji (`brak bloku frontmattera` nie wystąpi, bo pliku nie ma, ale reguła "root index.md wymagany" by to złapała, gdyby uruchomić walidator tej sesji na lipcowym repo — nie testowane, bo nie nasze repo do modyfikacji) |
| `type` dla `_meta/conventions.md` | `meta` (dodatkowa wartość w słowniku typów) | `temat` (nadużycie istniejącego typu, bo `TYPES` tej sesji nie miało `meta`) | Lipcowe podejście czystsze — warto przyjąć `meta` jako 6. typ przy rekoncyliacji |
| Frontmatter `sources` obowiązkowe niepuste | Nie wymuszone narzędziem (ręczna dyscyplina) | Wymuszone w `check_okf.py` (poza `_meta/`) | Zachować regułę tej sesji przy rekoncyliacji — tańsze niż poleganie na dyscyplinie |
| Walidator jako plik w repo | Brak (weryfikacja ad hoc, nieskomitowana) | `check_okf.py`, skomitowany, wielokrotnego użytku | Przenieść `check_okf.py` tej sesji do prawdziwego repo przy rekoncyliacji |
| Remote skonfigurowany | Tak (Forgejo, `master`) | Nie (lokalny, celowo) | — |
Żadna z różnic nie jest błędem — obie konwencje są spójne z faza3 §8.2 i audytem
08-26 w rzeczach, które się nakładają (format `[^envelope_id#chunk_id]`, fallback,
sekcja niepewności, CC/API-only, izolacja retrievalu kompilacji od `source='wiki'`).
---
## 4. Rekoncyliacja — do operatora, nie rozstrzygane przez tę sesję
To jest decyzja o realnych, już opublikowanych danych osobowych (finanse,
ubezpieczenia, dziecko na turnieju) w prawdziwym repo — powyżej progu, przy którym
ta sesja podejmuje decyzje sama. Opcje, bez rekomendacji wiążącej:
1. **Scalić nowe strony tej sesji do prawdziwego repo.** `podmioty/mbank.md` i
`osoby/pawel-cesar-sanjuan-szklarz.md` nie kolidują z niczym w lipcowym repo —
czysty dodatek. `sprawy/fll-2025-26.md` **koliduje** (dwie różne kompilacje tej
samej encji) — wymaga porównania treść-po-treści przez operatora albo kolejną
sesję CC, nie automatycznego scalenia.
2. **Porównanie dwóch niezależnych kompilacji `fll-2025-26` jako test jakości
kompilatora.** Obie wersje (lipcowa i ta) zgadzają się merytorycznie w
kluczowych punktach: wynik 195 pkt jako jedyny czytelny wprost z chunka
(`paperless:119#277`/`#278` — obie sesje trafiły w ten sam chunk niezależnie),
pozostałe dwa wyniki (165/230) tylko ze streszczenia, nominacja do Nagrody
Sędziów niepotwierdzona wprost w chunku, imiona z formularzy zgód celowo
pominięte, nazwisko trenera nieczytelne OCR. Rozbieżność: ta sesja znalazła i
zacytowała sprzeczność liczebności drużyny (10 vs 8 osób, z korespondencji
gmail — lipcowa wersja jej nie ma, bo nie sięgnęła po te same koperty gmail).
Lipcowa wersja znalazła niejednoznaczność sezonu SUBMERGED vs UNEARTHED
(`paperless:6`) i konkretne imiona dzieci z nazw plików (metadane, nie treść
dokumentu) — ta sesja pominęła `paperless:6` całkowicie i nie sięgnęła po
`entities[type=filename]`. **Dwie niezależne kompilacje tej samej encji nie są
sprzeczne, są komplementarne** — dobry sygnał, że metoda jest powtarzalna, zły
sygnał, że jedna kompilacja pomija realne dowody, które druga znalazła.
3. **Zostawić oba repo osobno, jawnie zarchiwizować to jako dwa niezależne
przebiegi tej samej fazy.** Najbezpieczniejsze, ale traci szansę na scalenie
uzupełniających się dowodów z punktu 2.
Krok operatora, niezależnie od wyboru z (1)-(3): **`~/kb-wiki-etap1-sesja-2026-08-27/`
istnieje lokalnie, nigdy nie pushowany** — do usunięcia/scalenia/zachowania wg
decyzji, nie automatycznie.
---
## 5. Poza zakresem tej notatki (Etap 2, jeśli operator potwierdzi kontynuację)
Zależnie od wyniku rekoncyliacji z §4:
- Skala do pełnej listy 17 encji z audytu §3 (już częściowo pokryta lipcowym repo
inną listą — PZU/WARTA/wspólnota/FLL nie pokrywają się z 17-pozycyjną listą
audytu poza `fll-2025-26`; wymaga ponownego review, które z 17 są już zrobione
pod innymi nazwami).
- Lint automatyczny w kodzie (`kb-wiki/lint.py`, inwariant 3 pełny) — żadna z
dwóch kopii tego nie ma, obie robiły lint ręcznie.
- Jednorazowy skan `<60>`/`\x00` na źródłach, które faktycznie zasiliły PoC (decyzja
h) — nie wykonany w żadnej z dwóch kompilacji; oba proof-of-concept **napotkały**
uszkodzony OCR organicznie (paperless:119/120 w obu wersjach fll-2025-26,
mbank.md ta sesja) bez systematycznego skanu.
- Integracja `source='wiki'` w `packages/kb-retrieval` (inwariant 5+7) — poza
zakresem obu przebiegów.
- Rozjazd `scripts/kb/check_okf.py` (SCOPE sztywny) — patrz §2, nie naprawiony
celowo (instrukcja zlecenia: nie hackować).
---
## 6. Rekoncyliacja domknięta (2026-08-27, decyzja operatora: opcja 1 z §4)
Operator zdecydował scalić — bez rozstrzygania punktu 3 z §4 (dwa osobne
repo) i bez samego jedynie archiwizowania punktu 2 (test jakości
kompilatora, wykonany przy okazji scalenia `fll-2025-26`, patrz niżej).
Wykonano w klonie roboczym `~/kb-wiki` (repo prawdziwe, `master`), **cztery
commity, lokalnie, świadomie nie pushnięte** — review operatora przed
pushem do Forgejo jest krokiem następnym, nie tej sesji.
### 6.1 `fll-2025-26` — scalenie dwóch niezależnych kompilacji
Wykonane **przed** tą sesją reconciliation (`kb-wiki` commit `c4127b9`,
autor: operator lub wcześniejsza sesja — poza zakresem tego zadania, ta
sesja zastała je już na `master` i pushnięte: *"Push zrobiony"*, cytat
zlecenia). Wynik zgodny z analizą §4 punkt 2: zero sprzecznych twierdzeń,
czysto komplementarne pokrycie (lipiec: 14 kopert paperless; sierpień
dodał 6 kopert gmail + `paperless:183`) — 21 kopert finalnie (15 paperless +
6 gmail), 38/38 par `sources` + 45/45 przypisów inline zweryfikowanych w
bazie przy tamtym scaleniu. Ta sesja **nie modyfikowała** tej strony —
tylko zweryfikowała ją ponownie w ramach pełnego lintu repo (§6.4).
### 6.2 `mbank` i `pawel-cesar-sanjuan-szklarz` — przeniesione bez kolizji
Zgodnie z §4 punkt 1 ("nie kolidują z niczym w lipcowym repo — czysty
dodatek"): skopiowane z `~/kb-wiki-etap1-sesja-2026-08-27/` do `~/kb-wiki`,
linki `[[...]]` przepisane ze składni sesyjnej (`[[../katalog/plik]]`) na
rzeczywistą konwencję repo (`[[plik]]`, bez katalogu). Treść merytoryczna
nietknięta. `INDEX.md` dopisany o oba wpisy. `sprawy/fll-2025-26.md`
przestało być sierotą w grafie `[[...]]` (link przychodzący z obu nowych
stron, sekcja „Powiązane" — decyzja sesyjna sprzed przeniesienia, nie tej
rekoncyliacji: „sprawdzone wprost, zero wspólnego dowodu", link mimo braku
dowodu).
### 6.3 Konwencje: `check_okf.py` przeniesiony, decyzje (d)-(f) scalone
`check_okf.py` (walidator sesyjny) przeniesiony do `~/kb-wiki` — lipcowy
proof-of-concept nie zostawił żadnego skomitowanego narzędzia (§3 tabela,
rekomendacja "Przenieść `check_okf.py` tej sesji do prawdziwego repo przy
rekoncyliacji" — wykonana dosłownie). Zaadaptowany do rzeczywistego layoutu
tego repo: `TYPES` rozszerzone o `meta` (repo miało 6. typ od lipca, sesyjny
walidator znał tylko 5), `README.md`/`INDEX.md`/`_meta/lint-reports/*.md`
wyjęte z wymogu frontmattera stron-konceptów (istniały bez niego od
2026-07-21 — dokumentacja/raporty, nie skompilowane encje).
Decyzje (d)-(f) audytu 08-26, wypracowane w `_meta/conventions.md` sesji
(§5/§7/§8 tamtego pliku), scalone do `_meta/conventions.md` prawdziwego repo
**bez duplikowania** tego, co repo już miało z lipca/wcześniejszej sesji
(6 typów wliczając `meta`, sekcja „Brak danych w KB" — obie już scalone,
commity `156925c`/`3bd2441`, przed tym zadaniem):
- **(d) fallback envelope-only** — `[^envelope_id]` bez `#chunk_id`, gdy
dowód nie ma konkretnego chunka albo chunk zniknął po re-chunkingu.
- **(e) sekcja „Niepewne / sprzeczne"** jako nazwana sekcja końca strony
(już używana de facto na żywych stronach, teraz sformalizowana pisemnie) +
polityka aktualizacji „dopisz, nie nadpisuj" przy nowym sprzecznym dowodzie.
- **(f) inwariant 7** — izolacja retrievalu kompilacji od `source='wiki'`
(mitygacja self-citation/citogenesis), CC/API-only jako wykonawca.
Różnica konwencji z §3 tabeli dotycząca casingu pliku nawigacyjnego
(`INDEX.md` vs OKF-owy `index.md`) **pozostaje nierozstrzygnięta** — zgodnie
z §3, do operatora, poza zakresem tej rekoncyliacji. `check_okf.py`
pragmatycznie traktuje oba pliki root (`README.md`, `INDEX.md`) jako
wyjęte z wymogu frontmattera, bez przesądzania, który plik "wygrywa".
### 6.4 Lint na całości — stan końcowy: 7 stron, zero błędów
`python3 check_okf.py` w `~/kb-wiki`: **ZGODNE**, 74/74 par `sources`
(wszystkie 6 stron-konceptów poza `_meta/`) zweryfikowanych w `KB_DSN`
(SELECT-only). Osobny, pełny przebieg (skrypt jednorazowy, nieskomitowany —
`check_okf.py` nie parsuje treści Markdown) po **wszystkich** przypisach
inline `[^envelope_id#chunk_id]` na **wszystkich** 7 stronach: **150/150**
zweryfikowanych, 0 zdegradowanych do fallback. Zero wiszących linków
`[[...]]`, zero błędów przypisania chunk→envelope. Pełny raport:
`_meta/lint-reports/2026-08-27-rekoncyliacja.md` w `kb-wiki`.
| Metryka | Wynik |
|---|---|
| Stron w repo (po rekoncyliacji) | 7 (5 lipiec + `fll-2025-26` scalone + 2 przeniesione) |
| `sources:` (agregat frontmatter) zweryfikowane w bazie | 74/74 |
| Przypisy inline zweryfikowane w bazie | 150/150 |
| Wiszące linki `[[...]]` | 0 |
| Przypisy zdegradowane (fallback envelope-only) | 0 |
| Commity rekoncyliacji na `kb-wiki:master` | 4, lokalne, **nie pushnięte** |
### 6.5 Co zostało poza zakresem tej rekoncyliacji
- `~/kb-wiki-etap1-sesja-2026-08-27/` — pozostawione nietknięte na żądanie
operatora ("skasuję sam po weryfikacji"), nie usuwane przez tę sesję.
- Push `kb-wiki:master` do Forgejo — czeka na review operatora.
- Wszystko z §5 (skala do 17 encji, lint automatyczny w kodzie, skan
`<60>`/`\x00` na źródłach, integracja `source='wiki'` w retrievalu, rozjazd
`scripts/kb/check_okf.py`) — nadal otwarte, nie ruszone tą rekoncyliacją.

View file

@ -18,6 +18,8 @@ reguły tego repo:
10. Wpisy `contradicts` wyglądające jak ścieżka .md też muszą istnieć; 10. Wpisy `contradicts` wyglądające jak ścieżka .md też muszą istnieć;
pozostałe wpisy to wolny tekst. pozostałe wpisy to wolny tekst.
11. `stub` o ile obecne musi być boolem. 11. `stub` o ile obecne musi być boolem.
12. Brak bajtów kontrolnych poza \t \n \r w całym pliku (łapie wklejki NUL/inne
binarne śmieci wklejone z zewnętrznych źródeł, np. wyników SELECT-ów).
Zakres domyślny: kb/ oraz docs/sessions/, z wyłączeniem README-wskaźników Zakres domyślny: kb/ oraz docs/sessions/, z wyłączeniem README-wskaźników
(POINTER_GLOBS) te nawigacją do kb-doca, nie dokumentami KB, i celowo nie (POINTER_GLOBS) te nawigacją do kb-doca, nie dokumentami KB, i celowo nie
@ -71,6 +73,7 @@ POINTER_GLOBS = (
EXCLUDE_DIRS = ("build",) EXCLUDE_DIRS = ("build",)
DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$") DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$")
CONTROL_CHAR_RE = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]")
def is_pointer(rel: str) -> bool: def is_pointer(rel: str) -> bool:
@ -232,6 +235,17 @@ def check_file(path: Path, root: Path) -> list[str]:
f"{rel}: `contradicts` wskazuje na nieistniejący plik: {item}" f"{rel}: `contradicts` wskazuje na nieistniejący plik: {item}"
) )
seen_lines: set[int] = set()
for m in CONTROL_CHAR_RE.finditer(text):
line_no = text.count("\n", 0, m.start()) + 1
if line_no in seen_lines:
continue
seen_lines.add(line_no)
errors.append(
f"{rel}: bajt kontrolny {ord(m.group()):#04x} w linii {line_no} "
"(dozwolone tylko \\t \\n \\r)"
)
return errors return errors

View file

@ -0,0 +1,62 @@
"""Tests for check_okf.py — the OKF v0.1 frontmatter validator.
Covers the control-character check added 2026-08-27: a file that otherwise has
valid frontmatter must still fail if its body contains a NUL byte or other
control byte outside \\t \\n \\r (the class of bug found in
kb/audits/wiki-kompilat-recon-2026-08-26.md raw bytes pasted in from a psql
SELECT over the mail corpus).
"""
from __future__ import annotations
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import check_okf # noqa: E402
VALID_FRONTMATTER = """---
okf: "0.1"
type: decision
visibility: private
status: active
updated: 2026-08-27
links: []
---
"""
def _write(tmp_path: Path, body: str) -> Path:
p = tmp_path / "doc.md"
p.write_text(VALID_FRONTMATTER + body, encoding="utf-8")
return p
def test_clean_file_has_no_control_char_errors(tmp_path):
path = _write(tmp_path, "# Title\n\nZwykła treść z \t tabulatorem i \r\n końcem linii.\n")
errors = check_okf.check_file(path, tmp_path)
assert errors == []
def test_nul_byte_in_body_is_flagged(tmp_path):
path = _write(tmp_path, "# Title\n\nZawiera bajt NUL: \x00 w tym miejscu.\n")
errors = check_okf.check_file(path, tmp_path)
assert any("bajt kontrolny" in e and "0x0" in e for e in errors)
def test_control_char_error_reports_correct_line(tmp_path):
body = "linia 1\nlinia 2\nzepsuta \x00 linia 3\nlinia 4\n"
path = _write(tmp_path, body)
errors = check_okf.check_file(path, tmp_path)
control_errors = [e for e in errors if "bajt kontrolny" in e]
assert len(control_errors) == 1
# Frontmatter occupies 7 lines before the body starts.
frontmatter_lines = VALID_FRONTMATTER.count("\n")
expected_line = frontmatter_lines + 3
assert f"w linii {expected_line}" in control_errors[0]
def test_multiple_control_chars_same_line_reported_once(tmp_path):
path = _write(tmp_path, "para \x00 z dwoma \x00 bajtami na tej samej linii\n")
errors = check_okf.check_file(path, tmp_path)
control_errors = [e for e in errors if "bajt kontrolny" in e]
assert len(control_errors) == 1