Jak utrzymać w zdrowiu kod pisany przez agentów
Utrzymanie w zdrowiu kodu pisanego przez agentów polega na obserwowaniu trendu, a nie tylko na bramkowaniu każdego pull requesta: na cotygodniowym mierzeniu przeróbek linii, odsetka revertów, złożoności ponad budżet, duplikacji i hotspotów zmian, na obniżaniu autonomii pętli, gdy sygnał przekroczy próg, oraz na planowych pętlach refaktoryzacji, które każdą zmianę udowadniają liczbami przed i po.
Ta strona jest dla tech leadów i deweloperów, którzy prowadzą pętle agentów, a CTO znajdzie tu widok miesięczny. Twoje funkcje dopasowania są zielone, każdy pull request przechodzi testy, a zespół scala więcej niż kiedykolwiek. Mimo to moduł zamówień wymaga dziś trzech sesji agenta zamiast jednej, w każdym sprincie wracają te same pliki, a jedna trzecia kodu z zeszłego miesiąca została już przepisana. Nie spowodował tego żaden pojedynczy pull request, więc nie wyłapie tego żadna bramka na poziomie PR. Potrzebujesz sygnału w czasie i reguły, co się dzieje, gdy ten sygnał się psuje.
Co daje pętla zdrowia kodu
Dział zatytułowany „Co daje pętla zdrowia kodu”- Pięć sygnałów zdrowia z dokładnymi definicjami, liczonych z gita i dwóch narzędzi open source, bez dashboardu dostawcy.
- Skrypt na 120 linii i cotygodniowy job GitHub Actions, które tworzą raport, porównują go z bazą odniesienia i otwierają zgłoszenie, gdy status zmieni się na czerwony.
- Tabelę progów, która mówi, który sygnał obniża autonomię pętli, o ile i kto to podpisuje.
- Planową pętlę refaktoryzacji dla Claude Code, Codeksa i Cursora z zabezpieczeniami, które nie pozwalają jej ruszać wyroczni.
- Trzy prompty do skopiowania: nienadzorowany przebieg refaktoryzacji, cotygodniowy przegląd (triage) i konsolidację duplikatów.
Dlaczego kod pisany przez agentów niszczeje, choć każdy pull request przechodzi?
Dział zatytułowany „Dlaczego kod pisany przez agentów niszczeje, choć każdy pull request przechodzi?”Bramki na poziomie PR sprawdzają, że zmiana nie łamie reguły. Nie widzą, że reguły przechodzą, a kod coraz trudniej zmieniać. Funkcje dopasowania architektury zatrzymują nowe naruszenia warstw i budżetów. Ta strona zajmuje się dryfem, który te bramki przepuszczają: przeróbkami, koncentracją złożoności w kilku plikach i kopiami, z których każda mieści się w budżecie duplikacji.
Trzy niezależne źródła opisują ten sam wzorzec:
- Faros AI („AI Engineering Report 2026: The Acceleration Whiplash”, kwiecień 2026, telemetria 22 000 deweloperów z własnej platformy) raportuje wzrost przepustowości (ukończone epiki na dewelopera +66,2%, odsetek scalonych pull requestów na dewelopera +16,2%) i spadek jakości (błędy na dewelopera +54%, incydenty na pull request +242,7%, churn kodu +861%). Baza klientów jest samowybrana.
- DORA (Google Cloud, raport 2025, 23 września 2025): „we observe a positive relationship between AI adoption on both software delivery throughput and product performance”. Oraz: „However, AI adoption does continue to have a negative relationship with software delivery stability”.
- SlopCodeBench (Orlanski i in., arXiv, v2 7 maja 2026) kazał agentom wielokrotnie rozbudowywać własne rozwiązania. Najlepszy przeszedł 14,8% ze 196 punktów kontrolnych, a autorzy mierzą degradację jako „structural erosion (concentrated complexity) and verbosity (redundant code)”.
Badanie GitClear z czerwca 2026 na 623 milionach zmian, znane nam tylko z wtórnego wyciągu wyszukiwarki, wskazuje ten sam kierunek: kod przenoszony (refaktoryzowany) spadł z 13% do 3,8% zmienianych linii względem 2023 roku. GitClear przedstawia to jako korelację, nie przyczynę. Praktyczny wniosek jest we wszystkich czterech ten sam: generowanie skaluje się samo, a refaktoryzacja nie dzieje się, dopóki ktoś jej nie zaplanuje.
Które sygnały zdrowia śledzić?
Dział zatytułowany „Które sygnały zdrowia śledzić?”Pięć sygnałów pokrywa opisane wyżej problemy. Każdy to trend czytany względem bazy odniesienia tego samego repozytorium, nigdy względem innego zespołu.
| Sygnał | Definicja | Co wyłapuje | Źródło |
|---|---|---|---|
| Przeróbki linii w 14 dni | Z linii dodanych 14–28 dni temu: odsetek, którego dziś już nie ma (bez białych znaków i przesunięć w obrębie pliku) | Kod napisany, scalony i przepisany w ciągu kilku tygodni: specyfikacja była zła albo projekt nie wytrzymał | git log --numstat i git blame -w -M |
| Odsetek revertów | Commity z tematem zaczynającym się od Revert " ÷ wszystkie commity bez merge’ów w 30 dni, względem poprzednich 30 dni | Zmiany na tyle złe, że trzeba je było cofnąć | git log |
| Złożoność ponad budżet | Odsetek linii kodu w funkcjach o złożoności cyklomatycznej powyżej budżetu (domyślnie 10) | Złożoność koncentrującą się w coraz mniejszej liczbie coraz większych funkcji | lizard 1.24.0 |
| Duplikacja | Zduplikowane linie jako procent wszystkich linii | Funkcje pomocnicze kopiowane zamiast używane ponownie, poniżej bramki klonów na poziomie PR | jscpd 5.3.2 |
| Hotspoty | Commity z 90 dni × suma złożoności, per plik, top 10 | Pliki, w których erozja kosztuje najwięcej, bo agenci dotykają ich co tydzień | git log plus lizard |
Pierwsze dwa mierzą proces, kolejne dwa kod, a hotspoty mówią pętli refaktoryzacji, gdzie wydać wysiłek. Złożoność ważona częstotliwością zmian pochodzi z analizy hotspotów Adama Tornhilla (Your Code as a Crime Scene): złożony kod, którego nikt nie dotyka, jest tani, a złożony kod, którego dotykają wszyscy, pochłania czas.
Odsetek revertów to tutaj przybliżenie z gita. Definicje dla całej organizacji, w tym wskaźnik poprawek w ciągu 14 dni liczony z powiązań pull requestów, są na stronie o metrykach, które przetrwają agentów. Używaj ich na dashboardzie CTO, a liczb z tej strony wewnątrz jednego repozytorium.
Mierz sygnały co tydzień
Dział zatytułowany „Mierz sygnały co tydzień”-
Dodaj skrypt. Zapisz go jako
scripts/codebase_health.py. Potrzebuje Pythona 3, gita z pełną historią i narzędzia lizard; jscpd jest opcjonalny i gdy go brak, raport pokazujenull. Funkcje liczące złożoność i duplikację uruchomiliśmy na kodzie TypeScript 26 września 2026 z lizard 1.24.0 i jscpd 5.3.2.#!/usr/bin/env python3"""scripts/codebase_health.py: weekly health signals for an agent-written codebase.Run from the repository root with full history (fetch-depth: 0 in CI).Needs git and lizard (pip install lizard==1.24.0); jscpd is optional.Prints one JSON report to stdout. Compares against health/baseline.json if present."""import argparse, csv, io, json, os, re, subprocess, tempfilefrom collections import Counter, defaultdictSHA = re.compile(r"^[0-9a-f]{40,64} ")WORSE_IF_HIGHER = ["revert_rate_30d", "rework_14d", "over_budget_nloc_share", "duplication_pct"]# A zero baseline would never alert, so compare against at least this absolute floor.FLOOR = {"revert_rate_30d": 0.01, "rework_14d": 0.02, "over_budget_nloc_share": 0.01, "duplication_pct": 0.5}def git(*args):return subprocess.run(["git", *args], check=True, capture_output=True, text=True).stdoutdef revert_rate(src, since, until):subjects = git("log", "--no-merges", f"--since={since} days ago", f"--until={until} days ago","--format=%s", "--", src).splitlines()reverts = sum(1 for s in subjects if s.startswith('Revert "'))return round(reverts / len(subjects), 4) if subjects else Nonedef rework(src, as_of):"""Share of lines added 14-28 days before `as_of` that are gone at `as_of`."""rev = "HEAD" if as_of == 0 else git("rev-list", "-1", f"--before={as_of} days ago", "HEAD").strip()if not rev:return Nonelog = git("log", rev, "--no-merges", "--no-renames", f"--since={as_of + 28} days ago",f"--until={as_of + 14} days ago", "--numstat", "--format=@%H", "--", src)cohort, added = set(), defaultdict(int)for line in log.splitlines():if line.startswith("@"):cohort.add(line[1:])elif line.strip():plus, _minus, path = line.split("\t", 2)if plus != "-": # "-" marks a binary fileadded[path] += int(plus)total = sum(added.values())if not total:return Nonesurvived = 0for path, count in added.items():if subprocess.run(["git", "cat-file", "-e", f"{rev}:{path}"], capture_output=True).returncode:continue # deleted (or renamed) since: nothing survived at this pathblame = git("blame", "-w", "-M", "--line-porcelain", rev, "--", path)alive = sum(1 for l in blame.splitlines() if SHA.match(l) and l.split(" ", 1)[0] in cohort)survived += min(alive, count)return round(1 - survived / total, 4)def complexity(src, budget):out = subprocess.run(["lizard", "--csv", src], capture_output=True, text=True).stdoutper_file, funcs, over, nloc_all, nloc_over, worst = Counter(), 0, 0, 0, 0, 0for row in csv.reader(io.StringIO(out)):nloc, ccn, path = int(row[0]), int(row[1]), os.path.normpath(row[6])funcs, nloc_all, worst = funcs + 1, nloc_all + nloc, max(worst, ccn)per_file[path] += ccnif ccn > budget:over, nloc_over = over + 1, nloc_over + nlocshare = round(nloc_over / nloc_all, 4) if nloc_all else Nonereturn {"functions": funcs, "over_budget": over, "over_budget_nloc_share": share, "max_ccn": worst}, per_filedef duplication(src):with tempfile.TemporaryDirectory() as out:run = subprocess.run(["npx", "--no-install", "jscpd", src, "--reporters", "json","--output", out, "--silent"], capture_output=True, text=True)report = os.path.join(out, "jscpd-report.json")if run.returncode or not os.path.exists(report):return Nonewith open(report) as f:return round(json.load(f)["statistics"]["total"]["percentage"], 2)def main():ap = argparse.ArgumentParser()ap.add_argument("--src", default="src")ap.add_argument("--ccn-budget", type=int, default=10)ap.add_argument("--baseline", default="health/baseline.json")a = ap.parse_args()stats, per_file = complexity(a.src, a.ccn_budget)touches = Counter(os.path.normpath(p) for p in git("log", "--no-merges", "--since=90 days ago", "--format=", "--name-only", "--", a.src).splitlines() if p.strip())hotspots = sorted(({"file": f, "commits_90d": n, "ccn_sum": per_file[f], "score": n * per_file[f]}for f, n in touches.items() if per_file.get(f)), key=lambda h: -h["score"])[:10]trend = [rework(a.src, d) for d in (28, 14, 0)] # oldest firstreport = {"revert_rate_30d": revert_rate(a.src, 30, 0),"revert_rate_prev_30d": revert_rate(a.src, 60, 30),"rework_14d": trend[-1],"rework_trend": trend,**stats,"duplication_pct": duplication(a.src),"hotspots": hotspots,}signals, status = {}, "green"if os.path.exists(a.baseline):with open(a.baseline) as f:base = json.load(f)for key in WORSE_IF_HIGHER:now, then = report.get(key), base.get(key)if now is None or then is None:continueratio = now / max(then, FLOOR[key])signals[key] = "red" if ratio > 1.5 else "amber" if ratio > 1.25 else "green"states = list(signals.values())if "red" in states or states.count("amber") >= 2:status = "red"elif "amber" in states:status = "amber"report.update(signals=signals, status=status if signals else "no-baseline")print(json.dumps(report, indent=2))if __name__ == "__main__":main() -
Uruchom go raz lokalnie z katalogu głównego repozytorium i zanim przeczytasz cokolwiek innego, spójrz na listę hotspotów. Jeśli top 10 zaskakuje zespół, skrypt patrzy na zły katalog albo historia jest płytka.
Okno terminala # Terminal, katalog główny repozytoriumpython3 -m pip install lizard==1.24.0python3 scripts/codebase_health.py --src src > health.json -
Kalibruj przez cztery tygodnie, potem zacommituj bazę odniesienia. Zachowuj każdy cotygodniowy raport, weź medianę każdego sygnału i zacommituj ją jako
health/baseline.json, podając w opisie pull requesta daty czterech raportów. Kataloghealth/i skrypt wpisz doCODEOWNERSz tech leadem jako właścicielem. Progi porównują się z tym plikiem, więc baza, którą każdy może edytować, to próg, który każdy może wyłączyć. -
Zaplanuj job. Nie uruchamia żadnego agenta i nie potrzebuje klucza do modelu. Kończy się błędem, gdy analiza niczego nie objęła, zapisuje raport w podsumowaniu joba, wysyła go jako artefakt i otwiera zgłoszenie przy statusie czerwonym.
.github/workflows/codebase-health.yml name: codebase-healthon:schedule:- cron: '23 5 * * 1' # Mondays 05:23 UTCworkflow_dispatch:permissions:contents: readissues: writejobs:health:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v7with:fetch-depth: 0 # rework and hotspots need the historypersist-credentials: false- uses: actions/setup-node@v7with:node-version: 22- run: npm ci # provides jscpd, pinned to 5.3.2 in devDependencies- uses: actions/setup-python@v7with:python-version: '3.13'- run: python -m pip install lizard==1.24.0- run: python scripts/codebase_health.py --src src > health.json- name: Fail if the analysis was blindrun: jq -e '.functions > 0 and .rework_14d != null' health.json- uses: actions/upload-artifact@v7with:name: codebase-healthpath: health.json- name: Summary, and an issue on redenv:GH_TOKEN: ${{ github.token }}run: |status=$(jq -r .status health.json){ echo "## Codebase health: $status"; echo '```json'; cat health.json; echo '```'; } >> "$GITHUB_STEP_SUMMARY"if [ "$status" = "red" ]; thengh issue create --title "Codebase health is red ($(date +%F))" \--body "Signals: $(jq -c .signals health.json). Apply the red actions from loops.yaml. Report: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"fi
Job pobiera tylko gałąź domyślną, nie trzyma żadnego sekretu poza tokenem, który może pisać zgłoszenia, i nie uruchamia kodu z pull requesta, więc można go bezpiecznie planować.
Które progi powinny obniżać autonomię pętli?
Dział zatytułowany „Które progi powinny obniżać autonomię pętli?”Sygnał ma sens tylko wtedy, gdy jego przekroczenie zmienia to, co wolno agentom. Skrypt oznacza sygnał jako bursztynowy powyżej 1,25 wartości bazowej i czerwony powyżej 1,5; dwa bursztynowe liczą się jak czerwony. To nasze sugerowane wartości startowe, a nie wyniki badań: dostrój je po czterech tygodniach kalibracji i zmieniaj wyłącznie pull requestem zatwierdzonym przez CODEOWNERS. Każdy stosunek liczymy względem bazy albo małego progu bezwzględnego, zależnie od tego, która wartość jest większa (1 punkt procentowy dla odsetka revertów i udziału złożoności, 2 dla przeróbek, 0,5 dla duplikacji), bo baza równa zeru, typowa dla revertów w czystym repozytorium, inaczej nigdy nie wywołałaby alertu.
| Status | Wyzwalacz | Co zmienia się dla pętli | Kto podpisuje |
|---|---|---|---|
| Zielony | Żaden sygnał powyżej 1,25× bazy | Pętle zachowują poziom; pętla refaktoryzacji pracuje na największym hotspocie | Nikt; raport trafia do archiwum |
| Bursztynowy | Jeden sygnał powyżej 1,25× | Awanse zostają wstrzymane. Zmiany w pięciu największych hotspotach tygodnia wymagają człowieka czytającego diff, nawet w pętlach L4. Pętla refaktoryzacji celuje w sygnał, który zmienił kolor | Tech lead, na cotygodniowym przeglądzie |
| Czerwony | Dowolny sygnał powyżej 1,5× albo dwa bursztynowe | Każda pętla, której zakres obejmuje hotspot z pierwszej piątki, schodzi o jeden szczebel na drabinie autonomii: L5 traci auto-merge, L4 wraca do ludzi czytających każdy diff, jak na L3. Nienadzorowane pętle funkcjonalne w tym zakresie stają; pracuje tam tylko pętla refaktoryzacji | Tech lead degraduje; CTO widzi to w trendzie miesięcznym |
| Powrót | Dwa kolejne zielone tygodnie | Pętla może wrócić o jeden poziom po zwykłym audycie awansu | Tech lead, z próbką z audytu |
Zapisz regułę w rejestrze pętli, żeby czerwony tydzień był sprawdzeniem w tabeli, a nie dyskusją:
# loops.yaml (excerpt): see the one-map page for the full register- loop: checkout-feature-work owner: checkout-team level: L4 health_scope: [src/checkout/, src/payments/] on_amber: freeze promotion; humans read diffs touching this week's top-5 hotspots on_red: demote to L3 until two consecutive green weeks, then re-audit last_health_status: green (2026-09-21)Degradacja dotyczy zakresu, nie zespołu. Pętla podbijania zależności, która nigdy nie dotyka src/checkout/, pracuje dalej na swoim poziomie, a pętla funkcjonalna modułu zamówień schodzi o szczebel.
Uruchom planową pętlę refaktoryzacji
Dział zatytułowany „Uruchom planową pętlę refaktoryzacji”Lekarstwem na erozję jest refaktoryzacja według harmonogramu, wykonywana przez agenta pod ostrzejszymi regułami niż praca nad funkcjami. Pięć reguł pozwala puszczać ją bez nadzoru i realizuje zasadę ochrony wyroczni: agent nie może edytować kontroli, które go oceniają.
- Jeden cel na przebieg: największy hotspot z
health.json, chyba że figuruje whealth/skip.txt. - Tylko zmiany, które nie zmieniają zachowania. Żadnych zmian w testach, fixture’ach, regułach funkcji dopasowania, bazach odniesienia,
health/,scripts/codebase_health.py, plikach CI, konfiguracji agentów (.claude/,.codex/,.cursor/), lockfile’ach anipackage.json. Testy muszą przejść bez zmian. Jeśli cel ma słabe testy, zadanie uruchomione przez człowieka najpierw pisze testy charakteryzacyjne. - Limit rozmiaru: 400 zmienionych linii. Większa refaktoryzacja to zaplanowane zadanie, a nie przebieg pętli.
- Zmierzona poprawa. Opis pull requesta podaje złożoność i liczbę klonów celu przed i po, z tych samych narzędzi, których używa job zdrowia. Bez mierzalnej poprawy nie ma pull requesta.
- Zwykłe bramki, merge przez człowieka. Pull requesty refaktoryzacji przechodzą te same wymagane kontrole i zatwierdzenie właściciela kodu. Samą pętlę zacznij w
loops.yamlod L3 i awansuj ją na podstawie dowodów, jak każdą inną.
Prompt przebiegu (pierwsza wskazówka niżej) zapisz jako .github/prompts/refactor-loop.md pod CODEOWNERS, żeby wszystkie trzy narzędzia wykonywały te same instrukcje.
Routines (rutyny) uruchamiają zapisany prompt na repozytorium w chmurze zarządzanej przez Anthropic albo w środowisku self-hosted Twojej organizacji (Team i Enterprise): według harmonogramu, po wywołaniu API albo po zdarzeniu z GitHuba. To funkcja w wersji zapoznawczej (research preview), dostępna w planach Pro, Max, Team i Enterprise, z minimalnym odstępem jednej godziny (sprawdzone w dokumentacji Routines i w Claude Code 2.1.283 26 września 2026). Utwórz rutynę z sesji:
/schedule weekly on Tuesday at 6:41, in acme/shop: follow .github/prompts/refactor-loop.md exactly and open a pull request only if it reports a measured winClaude przed zapisaniem pyta o repozytorium, środowisko i prompt. Zanim zaczniesz na tym polegać, pamiętaj o czterech rzeczach:
- Rutyna działa jako pełna sesja w chmurze bez wyboru trybu uprawnień, a commity i pull requesty mają twoją tożsamość na GitHubie. Usuń każdy konektor, którego nie potrzebuje, bo może wywoływać jego narzędzia bez pytania.
- Pushuje na gałęzie z prefiksem
claude/. Zatrzymują ją twoje reguły gałęzi, wymagane kontrole iCODEOWNERS; reguły deny z twoich lokalnych ustawień nie są granicą dla przebiegu w chmurze. - Zainstaluj lizard w skrypcie konfiguracyjnym środowiska (w środowisku self-hosted na samym runnerze) i zostaw dostęp do sieci na domyślnym poziomie Trusted, chyba że przebieg potrzebuje więcej.
- Zielony status na liście przebiegów rutyny oznacza tylko, że sesja wystartowała i zakończyła się bez błędu infrastruktury, a nie że refaktoryzacja się udała. Jedynym sygnałem sukcesu jest pull request z liczbami przed i po, więc tydzień bez pull requesta i bez wyjaśnienia w transkrypcie wymaga sprawdzenia.
Po stronie ludzi wbudowany skill /simplify przegląda kod pod kątem ponownego użycia istniejących funkcji pomocniczych, uproszczeń, wydajności i poziomu abstrakcji i nanosi poprawki. Skieruj go na największy hotspot podczas cotygodniowego przeglądu. Na głębszą, comiesięczną sesję nad najgorszym modułem użyj skilla improve-codebase-architecture Matta Pococka: przegląda ostatnio zmieniany kod, a potem przepytuje cię z jednej refaktoryzacji, zanim cokolwiek zmieni:
claude plugin install mattpocock-skillsopenai/codex-action@v1 uruchamia codex exec w GitHub Actions pod profilem uprawnień. Job agenta dostaje token tylko do odczytu i nie może pushować. Drugi job, który nigdy nie uruchamia kodu agenta, sprawdza patch pod kątem chronionych ścieżek i otwiera pull request w wersji roboczej (draft).
name: refactor-loopon: schedule: - cron: '41 6 * * 2' # Tuesdays 06:41 UTC workflow_dispatch:
permissions: contents: read
jobs: propose: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: fetch-depth: 0 persist-credentials: false - uses: actions/setup-node@v7 with: node-version: 22 - run: npm ci # before the agent, so the sandboxed run needs no network access - uses: actions/setup-python@v7 with: python-version: '3.13' - run: python -m pip install lizard==1.24.0 && python scripts/codebase_health.py --src src > health.json - uses: openai/codex-action@v1 with: openai-api-key: ${{ secrets.OPENAI_API_KEY }} permission-profile: ':workspace' prompt-file: .github/prompts/refactor-loop.md output-file: /tmp/pr-body.md - run: git add -A -- . ':!health.json' && git diff --cached --binary > /tmp/refactor.patch - uses: actions/upload-artifact@v7 with: name: refactor path: | /tmp/refactor.patch /tmp/pr-body.md
open-pr: needs: propose runs-on: ubuntu-latest permissions: contents: write pull-requests: write steps: - uses: actions/checkout@v7 - uses: actions/download-artifact@v8 with: name: refactor path: /tmp/refactor - name: Refuse oracle edits, then open a draft pull request env: GH_TOKEN: ${{ github.token }} run: | test -s /tmp/refactor/refactor.patch || { echo "No change proposed."; exit 0; } if git apply --numstat /tmp/refactor/refactor.patch | cut -f3 \ | grep -E '(^|/)(\.github|\.claude|\.codex|\.cursor|health|tests?|__tests__|fixtures)/|\.(test|spec)\.|(^|/)(package(-lock)?\.json|pnpm-lock\.yaml|yarn\.lock|AGENTS\.md|CLAUDE\.md|eslint\.config\.[cm]?[jt]s|\.jscpd\.json|\.dependency-cruiser[^/]*|scripts/codebase_health\.py)$'; then echo "The patch touches protected paths; refusing it." >&2; exit 1 fi branch="refactor-loop/${GITHUB_RUN_ID}" git switch -c "$branch" git apply --index /tmp/refactor/refactor.patch git -c core.hooksPath=/dev/null -c user.name="refactor-loop" \ -c user.email="refactor-loop@users.noreply.github.com" commit -m "refactor: weekly health loop" git push origin "$branch" gh pr create --draft --head "$branch" --title "refactor: weekly health loop" --body-file /tmp/refactor/pr-body.mdWzorzec chronionych ścieżek obejmuje konfigurację funkcji dopasowania dla TypeScriptu z funkcji dopasowania architektury: eslint.config.*, .jscpd.json, konfigurację .dependency-cruiser i jej bazę znanych naruszeń. Dopisz każdy inny plik, który w twoim repozytorium trzyma regułę, budżet albo bazę odniesienia, na przykład pyproject.toml, config/fitness-checkstyle.xml czy ArchitectureTest.java. Inaczej agent może poluzować budżet i nadal przejść bramkę.
Drugi job nakłada patch jako dane i na czas swojego jedynego commita wyłącza hooki gita, więc nic, co napisał agent, nie wykonuje się, gdy obecny jest token z prawem zapisu. Pull request otwarty przez GITHUB_TOKEN nie uruchamia twoich workflowów pull_request, więc jego wymagane kontrole wiszą: niech recenzent zamknie go i otworzy ponownie albo otwieraj go tokenem instalacji GitHub App.
Aplikacja Codex ma też Automations do planowanych zadań w tle (sprawdzone 28 sierpnia 2026; 26 września 2026 nie dało się ponownie odczytać tej strony). Używaj ich do osobistego cotygodniowego przebiegu, a workflowu powyżej, gdy pętla należy do zespołu. Skille z zakładki Claude Code:
npx skills add mattpocock/skills --skill improve-codebase-architecture codebase-design grilling -a codexCursor Automations „run cloud agents in the background, either on a schedule or in response to events from GitHub, GitLab, Slack, webhooks, Linear, and more”, a Scheduled to jeden z wyzwalaczy (cursor.com/docs/cloud-agent/automations, sprawdzone 28 sierpnia 2026; 26 września 2026 nie dało się ponownie sprawdzić cursor.com). Utwórz automatyzację z cotygodniowym harmonogramem, wskaż repozytorium i wklej polecenie wykonania .github/prompts/refactor-loop.md.
Cloud Agents działają we własnych maszynach wirtualnych, więc hook albo ustawienie użytkownika z twojego laptopa ich nie wiąże. Z przebiegiem podróżuje tylko to, co jest zacommitowane w repozytorium, a nawet to jest radą dla agenta, a nie bramką: bramką są reguły gałęzi, wymagane kontrole i CODEOWNERS. Środowisko w chmurze potrzebuje zainstalowanego lizard dla skryptu zdrowia. Bugbot może zrecenzować pull request refaktoryzacji, ale jest recenzentem, a nie bramką.
Skille z zakładki Claude Code:
npx skills add mattpocock/skills --skill improve-codebase-architecture codebase-design grilling -a cursorPrompty do skopiowania: zdrowie kodu
Dział zatytułowany „Prompty do skopiowania: zdrowie kodu”Prompty wywołują npm run fitness, pojedynczy skrypt zdefiniowany na stronie funkcji dopasowania architektury. Jeśli twoja komenda bramki nazywa się inaczej, podstaw ją.
Skąd wiesz, że sama pętla zdrowia działa?
Dział zatytułowany „Skąd wiesz, że sama pętla zdrowia działa?”Raport zdrowia, który pokazuje zero, jest gorszy niż brak raportu, bo zielony status daje zaufanie, na które nie zasłużył. Krok jq -e w workflowie przerywa job, gdy lizard nie przeanalizował żadnej funkcji albo git nie zwrócił kohorty przeróbek. Tak wygląda płytki klon, zły --src albo brak narzędzia. Dwa tygodnie bez commitów w --src też dają pustą kohortę przeróbek. W repozytorium o małym ruchu sprawdzaj tylko .functions > 0, a pustą wartość przeróbek traktuj w przeglądzie jako „brak danych”, żeby czerwony job nadal oznaczał ślepą analizę. Po każdej zmianie w toolchainie porównaj listę hotspotów z intuicją zespołu.
Pull request refaktoryzacji weryfikujesz bez czytania każdej linii:
- Zachowanie: istniejące testy przechodzą bez zmian, a sprawdzenie chronionych ścieżek (albo reguły gałęzi i
CODEOWNERS) dowodzi, że żaden test się nie zmienił. Ten dowód jest tak mocny, jak zestaw testów. Zanim pętla dotknie hotspotu, sprawdź siłę wyroczni dla tego pliku i dopisz go dohealth/skip.txt, dopóki nie będzie wystarczająca. - Kształt: bramki z funkcji dopasowania architektury przechodzą bez edycji reguł i baz odniesienia.
- Wartość: liczby przed i po z opisu odtwarzają się, gdy recenzent uruchomi lizard albo jscpd na gałęzi. Refaktoryzacja, która przenosi złożoność zamiast ją usuwać, pojawi się w przyszłym tygodniu jako nowy hotspot.
- Uwaga recenzenta: recenzent czyta uwagi o ryzyku i sprawdza wyrywkowo zmienione miejsca wywołań, tak jak w code review PR-a agenta. Liczby przed i po należą do pakietu dowodów pull requesta.
| Kto | Odpowiada za | Podpisuje |
|---|---|---|
| Tech lead | Bazę odniesienia, progi, health/skip.txt i prompt refaktoryzacji | Degradacje, awanse i każdą zmianę bazy |
| Deweloper z dyżuru | Cotygodniowy przegląd i pull requesty refaktoryzacji | Merge pull requestów refaktoryzacji jako właściciel kodu |
| CTO | Miesięczny trend w repozytoriach | Politykę progów i listę repozytoriów, które muszą uruchamiać pętlę |
CTO wystarczą trzy liczby na repozytorium i miesiąc: tygodnie w każdym statusie, pętle zdegradowane i przywrócone oraz odsetek scalonych pull requestów refaktoryzacji. Repozytorium zawsze zielone i bez scalonych refaktoryzacji jest albo zdrowe, albo mierzy zły katalog; lista hotspotów powie ci, który to przypadek.
Co się psuje w pętlach zdrowia kodu?
Dział zatytułowany „Co się psuje w pętlach zdrowia kodu?”Agent oszukuje wskaźnik złożoności. Dzieli jedną złożoną funkcję na pięć pomocniczych po siedem parametrów, CCN spada, a projekt się pogarsza. Rozwiązanie: trzymaj budżety max-params i długości funkcji w funkcjach dopasowania i czytaj przeróbki obok złożoności: podział, który nie pomógł, zostanie przepisany w ciągu kilku tygodni.
Refaktoryzacja podnosi sygnał przeróbek. Refaktoryzacja celowo usuwa świeże linie, więc intensywny tydzień refaktoryzacji może podbić przeróbki do bursztynowego. Rozwiązanie: na przeglądzie czytaj przeróbki obok pull requestów refaktoryzacji z danego tygodnia i nie degraduj pętli za wzrost spowodowany przez pętlę refaktoryzacji.
Baza odniesienia pełznie w górę. Ktoś ustawia nową bazę w złym miesiącu i erozja staje się nową normą. Rozwiązanie: baza zmienia się tylko pull requestem zatwierdzonym przez CODEOWNERS, z czterema raportami źródłowymi, i nigdy przy statusie czerwonym.
Czerwony staje się szumem. Cotygodniowe zgłoszenie, którego nikt nie zamyka, uczy zespół je ignorować. Rozwiązanie: czerwony zmienia przez loops.yaml to, co wolno pętlom, więc ignorowanie ma koszt; pozwól na jedno otwarte zgłoszenie zdrowia naraz i zamykaj je w pierwszym zielonym tygodniu.
Odsetek revertów liczy źle. Liczy commity, więc workflow z merge commitami i wieloma commitami na pull request go rozcieńcza, a zespoły, które naprawiają do przodu, w ogóle nie revertują. Rozwiązanie: na dashboardzie używaj wskaźnika poprawek opartego na pull requestach ze strony o metrykach, a tego przybliżenia do trendów w jednym repozytorium.
Pętla refaktoryzacji wciąż wybiera ten sam plik. Jej poprawka nie obniżyła wyniku dość mocno, więc plik zostaje na szczycie. Rozwiązanie: po dwóch przebiegach na tym samym celu bez scalonego pull requesta dopisz go do health/skip.txt i przekaż do sesji prowadzonej przez człowieka z improve-codebase-architecture albo do decyzji architektonicznej.
Incydent agenta zaczyna się w hotspocie. Czerwony sygnał bywa wczesnym ostrzeżeniem przed incydentem, który przychodzi później. Rozwiązanie: oznacz incydent hotspotem i pętlą w procesie obsługi incydentów agentów i sklasyfikuj go według taksonomii błędów.
Dokąd dalej po zdrowiu kodu
Dział zatytułowany „Dokąd dalej po zdrowiu kodu”Kolejna strona tej sekcji zamienia błędy, na które wskazują te sygnały, w taksonomię do oznaczania incydentów.