Run log¶
Run log pozwala design partnerom mierzyć hipotezę biznesową na ich własnych sesjach. Jest lokalny, domyślnie wyłączony, a Inwards nigdy go nigdzie nie wysyła.
Włączanie¶
Każde z tych ustawień go włącza:
run-log = truew głównym[tool.inwards];INWARDS_RUN_LOG=1w środowisku, w którym działają hooki.
INWARDS_RUN_LOG=0 wyłącza go nawet wtedy, gdy konfiguracja mówi run-log = true. Tak czy inaczej, w projekcie, który nie używa Inwards (bez [tool.inwards] w katalogu głównym i bez stanu sesji), nic nie jest zapisywane, więc ustawienie na poziomie całego użytkownika nie dotyka innych projektów. inwards check --log zapisuje to jedno uruchomienie nawet przy wyłączonym logu; w ten sposób zapisywane są uruchomienia lint-cmd z Aidera (dodaj --log do polecenia, które wypisuje init).
Linie trafiają do .inwards/runs.jsonl, który inwards init wyłącza z gita dla każdego agenta. Plik może czytać tylko ty. inwards check zapisuje log obok konfiguracji, której używa, więc w monorepo sprawdzenie uruchomione wewnątrz pakietu zapisuje do .inwards/ tego pakietu. Po osiągnięciu 5 MiB plik przenosi się do .inwards/runs.1.jsonl, zastępując poprzedni, i zaczyna się nowy plik. Dwa uruchomienia, które rotują log w tej samej chwili, mogą zgubić ten starszy plik; szansa jest mała, a nic więcej nie ginie.
Schemat linii inwards/run@1¶
Każda linia to jeden obiekt JSON:
| Pole | Typ | Znaczenie |
|---|---|---|
v |
number | Wersja schematu, 1. Pola są tylko dodawane; zmiana znaczenia podnosi v |
at |
string | Czas zakończenia uruchomienia w ISO 8601 |
session_id |
string albo null | Identyfikator sesji Claude Code; null dla inwards check |
event |
string | SessionStart, PreToolUse, PostToolUse, Stop albo check |
tool |
string albo null | Narzędzie zdarzenia narzędziowego, np. Edit |
files |
string[] | Pliki sprawdzone w uruchomieniu, ze ścieżkami względnymi wobec projektu. Dla check jego argumenty ścieżek albo "." dla całego projektu |
lines |
object[] | Tylko PostToolUse, dla pliku sprawdzonego przez hook: { "file", "added", "removed" }, liczone z wywołania narzędzia bez linii, które edycja powtarza bez zmian wokół swojej zmiany. Write liczy każdą linię jako dodaną; replace_all liczy jedno wystąpienie |
fingerprints |
string[] | Po jednym na każde zgłoszone naruszenie (zahashowane: kod reguły, moduł i komunikat), tak samo jak w stanie sesji. Dwa identyczne importy w jednym pliku dają ten sam fingerprint dwa razy |
codes |
string[] | Kod reguły każdego fingerprintu, w tej samej kolejności (np. INW001). Linie zapisane, zanim to pole powstało, go nie mają |
severities |
string[] | error albo warning dla każdego fingerprintu, w tej samej kolejności, tak jak ustawiła je [tool.inwards.rules]. Linie zapisane, zanim to pole powstało, go nie mają i są czytane jako błędy |
suppressed |
number | Diagnostyki, które w tym uruchomieniu ukrył komentarz wyciszający i które uruchomienie uznało. Linie zapisane, zanim to pole powstało, go nie mają |
rejected |
string[] | Fingerprinty, których wyciszenia hooki nie uznały, bo dodał je agent (agent-suppressions = "deny"); są też w fingerprints. Linie zapisane, zanim to pole powstało, go nie mają |
exit |
number | Kod wyjścia zwrócony przez Inwards |
durationMs |
number | Czas od startu procesu, łącznie ze startem procesu, z dokładnością do 0,1 ms |
{"v":1,"at":"2026-09-25T20:14:03.512Z","session_id":"7a425a88-...","event":"PostToolUse",
"tool":"Edit","files":["shop/domain/order.py"],
"lines":[{"file":"shop/domain/order.py","added":2,"removed":1}],
"fingerprints":["4c1f0e9a2b7d3e10"],"codes":["INW001"],"severities":["error"],
"suppressed":0,"rejected":[],"exit":2,"durationMs":24.8}
Odczyt¶
inwards stats [DIR] czyta każdy .inwards/runs.1.jsonl i .inwards/runs.jsonl w projekcie: ten w katalogu głównym, do którego piszą hooki, i ten obok każdej konfiguracji, do którego pisze inwards check --log. Scala je w kolejności czasowej i wypisuje trzy liczby, każdą obok jej progu z rozdziału 2. Projekt to DIR, a jeśli go nie podano, to CLAUDE_PROJECT_DIR, potem drzewo robocze gita, w którym jesteś, potem najbliższy katalog powyżej ze stanem hooków w .inwards/state, a na końcu najbardziej zewnętrzny katalog z run logiem, przy czym wyszukiwanie zatrzymuje się poniżej twojego katalogu domowego. --config nie ma tu zastosowania: stats zawsze czyta cały projekt. --format json wypisuje te same liczby jako inwards/stats@1, z werdyktem met dla każdej. Nic nie opuszcza maszyny. Żeby udostępnić sam log, inwards stats --export FILE --redact zapisuje każdą linię logów projektu, w kolejności czasowej, do jednego pliku, z każdą ścieżką i każdym fingerprintem zastąpionym hashem z kluczem z .inwards/export-key; zobacz przewodnik dla design partnerów.
Run log: 2 sessions, 6 hook runs, 1 unreadable line skipped.
Fixed within one retry: 1 of 2 (50%). Target: at least 80%. Not met.
INW001 1 of 1 (100%)
INW011 0 of 1 (0%)
unknown 0 of 0, 1 without a retry
1 more had no later run for their file and weren't reported at Stop.
Violations per 1,000 agent-written lines: 60 (3 in 50 lines). Target: at least 1. Met.
Hook latency: p50 40 ms, p95 200 ms over 6 runs. Target: p50 under 100 ms. Met.
Co jest liczone:
- Naprawione w ramach jednej ponownej próby (fixed within one retry): naruszenie liczy się raz na sesję i plik, przy pierwszej linii
PostToolUse, która je zgłasza, i tylko jeśli jest błędem: ostrzeżenia nie blokują agenta. Jest naprawione, gdy następna liniaPostToolUsedla tego pliku nie zawiera już jego fingerprintu, więc musi zniknąć każda jego kopia. Gdy dla pliku nie ma późniejszego uruchomienia, późniejsza liniaStopz tej samej sesji, która wciąż je zgłasza, liczy się jako nienaprawione, więc agent, który się poddaje, nie wypada ze wskaźnika. Wszystko inne jest wymienione jako bez ponownej próby. Podział na reguły korzysta zcodes; fingerprint, który nigdy nie został zapisany z kodem, liczy się podunknown. - Naruszenia na 1000 linii napisanych przez agenta: fingerprinty błędów po raz pierwszy zgłoszone w liniach
PostToolUsesesji, raz na sesję i plik, tak jak we wskaźniku ponownych prób, podzielone przez sumęaddedw tych liniach..pyi jego.pyidzielą moduł, więc naruszenie powtórzone w pliku zaślepki liczy się dwa razy, raz na plik. - Opóźnienie hooka: p50 i p95 z
durationMsw liniachPostToolUse, które sprawdziły plik, metodą najbliższej rangi. Edycje innych plików (Markdown, pliki spoza projektu) też uruchamiają hook, ale niczego nie sprawdzają, więc są pomijane. - Werdykty porównują z progami dokładne proporcje, a nie zaokrąglone procenty.
- Odrzucone wyciszenia: różne naruszenia w każdej sesji, których wyciszenie w linii dodał agent, a hooki odrzuciły, z pola
rejectedw dowolnej linii. Raport tekstowy pokazuje tę linię tylko wtedy, gdy są jakieś; JSON zawsze marejectedSuppressions. Nie ma progu: liczy, jak często agent próbował najtańszego obejścia (ADR-028).
Hook sprawdza cały plik, więc jego fingerprinty obejmują naruszenia, które były tam, zanim agent go dotknął. Oba wskaźniki pomijają dwa ich rodzaje:
- naruszenia, które linia
checkzgłosiła przed pierwszą edycją w sesji. Uruchominwards check --format json --log, zanim poprosisz agenta o pierwszą zmianę; bez tego naruszenia, które już są w pliku, liczą się na konto agenta przy pierwszej edycji tego pliku; - naruszenia, które miało ostatnie uruchomienie hooka na tym pliku w innej sesji, więc naruszenie pozostałe po wczorajszej sesji, sprzed
/clearalbo od innego agenta pracującego równocześnie nad tym samym plikiem nie jest liczone jako naruszenie tej sesji, dopóki któreś uruchomienie na tym pliku nie przestanie go zgłaszać. Jeśli potem wróci, należy już do tej sesji.
Adopcji hooków, czyli odsetka instalacji z hookiem agenta, nie ma w logu: jeden projekt nie widzi pozostałych. Partnerzy podają ją sami.