Skip to content

refactor: dekompozycja trzech monstrów compile i capture (faza 3a)#68

Merged
mpasternak merged 1 commit into
mainfrom
cleanup/faza-3a-compile-capture
Jul 22, 2026
Merged

refactor: dekompozycja trzech monstrów compile i capture (faza 3a)#68
mpasternak merged 1 commit into
mainfrom
cleanup/faza-3a-compile-capture

Conversation

@mpasternak

Copy link
Copy Markdown
Member
funkcja CC
_compile_step 36 → 4
run_compile 39 → 4
capture_pages 46 → 6

CC w całym repo: 6 → 3. Zostały wyłącznie trzy funkcje w recorder/render/ — własność równoległego strumienia 3b.

Reguła, która zdecydowała o powodzeniu

Najpierw obiekt stanu, potem ekstrakcja faz. Odwrotna kolejność daje funkcje o ośmiu parametrach zwracające krotki — czyli complexity przeniesioną do sygnatur zamiast usuniętą.

capture_pages — niezmiennik przestał być komentarzem

Dotąd prev_cursor/prev_shape były lokalnymi zmiennymi pętli aktualizowanymi po zbudowaniu strony, a jedyną ochroną był komentarz. Przesunięcie jednej linii sprawiłoby, że strzałka zaczyna się na bieżącym celu zamiast poprzedniego — zły obrazek w PDF-ie, który nadal się generuje.

Teraz _CursorTrail ma prywatne pola i metodę advance(), która czyta wychodzącą parę i przyjmuje przychodzącą jako jedno wyrażenie, wołaną z pozycji argumentu przy budowaniu strony. Nie ma już granicy instrukcji, w którą dałoby się wsunąć linię. reset() czyści obie połowy razem — dziś navigate i scroll robią to dwiema kolejnymi liniami, które mogą się rozjechać.

Dowód: zamiana kolejności odczytu i zapisu w advance()5 testów czerwonych. reset() czyszczący tylko kursor → 2 czerwone.

Znaleziona i załatana luka w pokryciu

Mutacja przesuwająca odczyt url_before za wykonanie akcji przeżyła. Powód: każdy scenariusz w test_compile.py klika przycisk niezmieniający URL-a, więc expect: navigation nigdy nie było zamrażane w teście jednostkowym. Dopisany test klikający <a href=about:blank>; po nim mutant pada.

capture.py celowo NIE jest pakietem

Wszystkie 45 miejsc podmiany dotyczy trzech nazw (reuse_failure ×39, pause_for_inspection ×4, annotations_for ×2). Pakiet wymusiłby przecelowanie wszystkich 45 na capture.run.

Zamiast tego podział na moduły siostrzane, z szwami i ich konsumentami zostawionymi w capture.pyzero zmian w testach. Ponieważ to tworzy realną możliwość cichej awarii, doszedł bespoke strażnik (test_capture_seams.py, 128 linii) odkrywający listę szwów skanem: SeamGuard guarduje pakiety i uznałby każdą z tych podmian za patch w fasadę.

Szczegół, który mógł wywalić wszystko po cichu

_PageWatch ma eq=False i to jest load-bearing: pyee kluczuje tablicę handlerów po haszu, a domyślna dataclass ustawia __hash__ = None — związany listener nie dałby się zarejestrować.

Mutant, który przeżył i jest równoważny, nie luką

Usunięcie skipped_branch = None nie zostało złapane. To nie dziura w testach: FlatStep.branch to indeks wpisu when:, więc stary identyfikator nigdy nie trafi w późniejszy krok. Martwy kod już przed refaktorem — do backlogu.

Testy

1527 unit (+4) + 51 integration
ruff check, preview blank-lines, ruff format --check — czysto
C901 zbiory: trzy usunięte, nic nie dodane · bez # noqa, bez zmiany progu

compile/run.py ma 573 linie — najciaśniejszy plik. CompileSession nie może przejść do state.py, bo zabrałby ze sobą szew write_compiled.

🤖 Generated with Claude Code

https://claude.ai/code/session_0134rSCftVxsMVL8sngceT7A

  _compile_step   36 → 4
  run_compile     39 → 4
  capture_pages   46 → 6

CC w całym repo: 6 → 3. Zostały wyłącznie trzy funkcje w recorder/render/,
własność równoległego strumienia 3b.

REGUŁA: najpierw obiekt stanu, potem ekstrakcja faz. Odwrotna kolejność daje
funkcje o ośmiu parametrach zwracające krotki, czyli complexity przeniesioną
do sygnatur zamiast usuniętą.

_compile_step: _Resolution (klaster, który stare ciało wiązało w dwóch
miejscach i czytało w czterech) + _StepContext z metodą message() zamiast
domknięcia. Drabina akcji zastąpiona tablicą dyspozycji z jednolitym
protokołem JEDNOARGUMENTOWYM. Sygnatura publiczna bajtowo identyczna.

run_compile: _PageWatch + CompileSession + _StepRun (per krok).
CompileSession MUSI zostać w run.py — checkpoint() jest szwem write_compiled,
a __globals__ metody to moduł, w którym ją zdefiniowano.
_PageWatch ma eq=False i to jest load-bearing: pyee kluczuje tablicę
handlerów po haszu, a domyślna dataclass ustawia __hash__ = None, więc
związany listener nie dałby się zarejestrować.

capture_pages: NIEZMIENNIK prev_shape przestał być komentarzem.
_CursorTrail ma prywatne pola i metodę advance(), która czyta wychodzącą parę
i przyjmuje przychodzącą JAKO JEDNO WYRAŻENIE, zwracając słownik rozpakowywany
w liście argumentów annotations_for(). Nie ma już granicy instrukcji, w którą
dałoby się wsunąć linię. reset() czyści obie połowy razem — dziś navigate
i scroll robią to dwiema kolejnymi liniami, które mogą się rozjechać.

ZNALEZIONA I ZAŁATANA LUKA W POKRYCIU. Mutacja przesuwająca odczyt url_before
za wykonanie akcji PRZEŻYŁA: każdy scenariusz w test_compile.py klika przycisk
niezmieniający URL-a, więc `expect: navigation` nigdy nie było zamrażane
w teście jednostkowym. Dopisany test klikający <a href=about:blank>; po nim
mutant pada.

capture.py CELOWO NIE JEST PAKIETEM. Wszystkie 45 miejsc podmiany dotyczy
trzech nazw (reuse_failure ×39, pause_for_inspection ×4, annotations_for ×2).
Pakiet wymusiłby przecelowanie wszystkich 45 na capture.run. Zamiast tego
podział na moduły siostrzane z szwami i ich konsumentami zostawionymi
w capture.py — zero zmian w testach. Bespoke strażnik (test_capture_seams.py)
odkrywa listę szwów skanem, bo SeamGuard guarduje pakiety i uznałby każdą
z tych podmian za patch w fasadę.

DOWODY: mutacja zamiany kolejności w _CursorTrail.advance → 5 testów czerwonych.
reset() czyszczący tylko kursor → 2 czerwone. Bezwarunkowy checkpoint →
test reużycia cache czerwony (co przy okazji dowodzi, że patch na
compile_module.run.write_compiled nadal dociera). _PageWatch.observe jako
no-op → 3 testy popupów czerwone.

Mutant, który przeżył i jest RÓWNOWAŻNY, nie luką: usunięcie
`skipped_branch = None`. FlatStep.branch to indeks wpisu when:, więc stary
identyfikator nigdy nie trafi w późniejszy krok. To był martwy kod już przed
refaktorem — do backlogu, nie do naprawy tutaj.

run.py ma 573 linie — najciaśniejszy z moich. CompileSession nie może przejść
do state.py, bo zabrałby ze sobą szew; przyszły wzrost wymaga najpierw
wyniesienia _StepRun.

Suite: 1527 unit (+4) + 51 integration. ruff, preview blank-lines i format
czysto. C901 zbiory: trzy usunięte, nic nie dodane.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0134rSCftVxsMVL8sngceT7A
@mpasternak
mpasternak merged commit 6e3b4b1 into main Jul 22, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant