.factPlik .fact to niezmienny dziennik zdarzeń (Append-only Ledger). Jest trzecim filarem systemu SOTER obok .model (Ontologia) i .view (Soczewka).
Pełna filozofia Ledgera: → CONTEXT_BOOTSTRAP_FILE.md §§ V–VII
corrects:.recorded_at w momencie zapisu.Źródło (Sensor / UI)
↓
Obserwator ← nadaje recorded_at, kolejka FIFO
↓
Ledger ← Append-only (nienaruszalny)
↓
Weryfikator ← zderzenie faktów z .model (arytmetyka MES)
↓
Reconciler ← generuje Fakt Dorozumiany jeśli wykryje lukę
↓
Obserwator ← pętla zamknięta (fakt wraca na wejście)
factfact <ID>
occurred_at: <ISO8601>
recorded_at: <ISO8601> # (opcjonalne — nadawane przez Obserwatora)
trigger: <UUID-Akcji>
consume:
<UUID-Obiektu>: { amount: <liczba>, cost_basis: <wartość> <waluta> }
produce:
<UUID-Obiektu>: { cost_basis: <wartość> <waluta> }
| Atrybut | Wymagane | Opis |
|---|---|---|
occurred_at |
✅ | Czas deklarowany przez źródło (subiektywny) |
recorded_at |
⬜ | Czas obiektywnego zapisu w Ledgerze (nadawany przez Obserwatora) |
trigger |
✅ | UUID Akcji z .model, która wyzwoliła zdarzenie |
consume |
⬜ | Zużyte zasoby (Object) z ilościami i wartościami cost_basis |
produce |
⬜ | Wytworzone zasoby z wartościami cost_basis / market_value |
corrects |
⬜ | ID wcześniejszego błędnego faktu (korekta immutable) |
type |
⬜ | logical_compensation — fakt generowany przez Reconcilera |
| Wymiar | Atrybut | Charakter |
|---|---|---|
| Czas fenomenu | occurred_at |
Subiektywny — deklarowany przez źródło |
| Czas zapisu | recorded_at |
Obiektywny — nadawany przez system |
| Tarcie | recorded_at - occurred_at |
Kluczowa metryka opóźnienia informacyjnego |
Błędów nie usuwa się. Tworzysz nowy fakt z corrects::
fact f-002
occurred_at: 2026-03-08T09:05:00
corrects: f-001
trigger: u-<UUID-akcji>
consume:
u-<UUID-obiektu>: { amount: 10, cost_basis: 2000 PLN }
produce:
u-<UUID-obiektu>: { cost_basis: 1800 PLN }
Logika korekty: f-001 pozostaje w Ledgerze dla celów audytowych. Wszelkie obliczenia stanu ignorują f-001 — biorą pod uwagę tylko f-002.
Gdy Weryfikator wykryje lukę arytmetyczną (np. 200 PLN brakujące w bilansie), Reconciler automatycznie generuje fakt kompensacyjny:
fact f-003
type: logical_compensation
recorded_at: 2026-03-08T09:05:01 # Milisekundę po f-002
trigger: f-002
produce:
u-kosz-na-bugi: { cost_basis: 200 PLN }
Fakt ten jest wstrzykiwany z powrotem na wejście Obserwatora — pętla cybernetyczna zamknięta.
.fact| Błąd | Kod | Warunek |
|---|---|---|
Brak occurred_at |
E-F01 | Każdy fakt musi mieć czas fenomenu |
Brak trigger |
E-F02 | Każdy fakt musi wskazywać na Akcję z .model |
trigger wskazuje na nieistniejący UUID |
E-F03 | Odwołanie do niezdefiniowanej Akcji |
| Luka bilansowa bez kompensacji | W-F10 | consume.cost_basis ≠ produce.cost_basis (brak faktu Reconcilera) |
corrects wskazuje na nieistniejący ID |
E-F04 | Korekta widma |
soter v1
# --- FAKTY ---
# 1. Adam raportuje 10h — błąd w kwocie
fact f-001
occurred_at: 2026-03-08T09:00:00
trigger: u-implementacja-funkcji
consume:
u-roboczogodzina: { amount: 10, cost_basis: 1500 PLN } # BŁĄD: powinno być 2000
produce:
u-funkcja-kodu: { cost_basis: 2000 PLN }
# 2. Adam koryguje raport
fact f-002
occurred_at: 2026-03-08T09:05:00
corrects: f-001
trigger: u-implementacja-funkcji
consume:
u-roboczogodzina: { amount: 10, cost_basis: 2000 PLN }
produce:
u-funkcja-kodu: { cost_basis: 1800 PLN } # 200 PLN "ucieka"
# 3. RECONCILER: domyka bilans
fact f-003
type: logical_compensation
recorded_at: 2026-03-08T09:05:01
trigger: f-002
produce:
u-kosz-na-bugi: { cost_basis: 200 PLN }