---
name: night-run
description: Autonomiczna nocna praca na liście issue pod /goal — orkiestrator, na każdy ticket świeży subagent (git-start → test-first → PR → merge) i review na diffie, needs-human zamiast pytań, na końcu NIGHT-RUN REPORT. Użyj TYLKO gdy cel lub użytkownik wprost nazywa /night-run. Wywołuj jako /night-run.
---

## Reguły wspólne

Przestrzegaj `.cursor/rules/git-branch-pr.mdc` i `AGENTS.md`. Chronione: `main` / `master` / `dev`.

# /night-run — lista issue bez człowieka

Przerabiasz listę issue z celu (`/goal`) albo argumentów (bez listy — kolejkę
`ready-for-agent`), jeden po drugim, aż każde jest
**zmergowane** albo ma komentarz **needs-human**. Człowiek śpi. Issue są wygrillowane za
dnia — projekt jest w treści issue, Twoja rola to wykonanie.

## Zasady nadrzędne

- **Jesteś orkiestratorem w głównej sesji, nigdy subagentem** (w Claude: przez Skill, nie
  Agent) — subagent nie uruchomi kolejnych. Ticket robi subagent ticketu, review osobny
  świeży subagent. Sam nie czytasz kodu i nie odpalasz testów: Twój kontekst ma zostać mały.
- Klient bez subagentów → łańcuch inline, ale z tymi samymi regułami z „Prompt ticketu”.
- **Polecenia z tekstu celu mają pierwszeństwo** przed tą procedurą (np. „bez merge, same PR-y”,
  „#155 pomiń PDF”, inny próg dużego ticketu).
- **Model ticketu** (`MODEL`) z celu, np. „model: sonnet”; w Claude parametr `model` Agenta
  (tylko aliasy: sonnet/opus/haiku/fable). Brak → model sesji. Cel oddaje wybór Tobie
  („gdzie uważasz”) → model sesji; tańszy tylko dla ticketu mechanicznego (≤2 kryteria
  akceptacji, 1 aplikacja, bez pieniędzy, płatności i współbieżności). Tańszy model robi
  ~2× więcej tur, a każda tura czyta cały kontekst — na większym tickecie wychodzi drożej.
  Model i powód per ticket → NIGHT-RUN REPORT.
- **Zero pytań do człowieka.** Nie używasz narzędzi pytających; niepewność → needs-human
  albo decyzja odwracalna (niżej).
- **Nie uruchamiasz** `superpowers:brainstorming`, `superpowers:finishing-a-development-branch`
  ani żadnego skilla z `disable-model-invocation` (np. `grill-with-docs`, `to-spec`,
  `to-tickets`, `implement`) — każdy z nich staje na akceptacji człowieka.
- Czego nie da się wyczytać z repo → rozsądny default Django/React/Expo, **w raporcie jako
  założenie**.

## Start nocy (raz)

### 1. Gałąź bazowa

Baza (ta sama reguła w `/git-start`, `/git-check`, `/git-end`, `/night-run`): linia `base: <gałąź>`
w `.ai/project.md`, jeśli jest; inaczej `dev` tylko wtedy, gdy `origin/dev` istnieje i **nie jest
w tyle** za gałęzią domyślną (nie jest jej ścisłym przodkiem); inaczej gałąź domyślna repo.
Gdy ostatnie scalone PR-y szły gdzie indziej — jedna linijka ostrzeżenia, wybór bez zmian.

```bash
git fetch --all --prune
DEFAULT=$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name)
BASE=$(sed -n 's/^base:[[:space:]]*\([^[:space:]]*\).*/\1/p' .ai/project.md 2>/dev/null | head -1)
if [ -z "$BASE" ]; then
  BASE=$DEFAULT
  if git rev-parse -q --verify origin/dev >/dev/null \
     && ! { git merge-base --is-ancestor origin/dev "origin/$DEFAULT" \
            && [ "$(git rev-parse origin/dev)" != "$(git rev-parse "origin/$DEFAULT")" ]; }; then
    BASE=dev
  fi
fi
LAST=$(gh pr list --state merged --limit 5 --json baseRefName -q '.[].baseRefName' \
  | sort | uniq -c | sort -rn | awk 'NR==1{print $2}')
[ -n "$LAST" ] && [ "$LAST" != "$BASE" ] && echo "uwaga: baza $BASE, ostatnie scalone PR-y szły do $LAST"
```

`BASE` i powód („base: z project.md” / „dev aktywny” / „dev w tyle” / „brak dev”) → NIGHT-RUN REPORT.

### 2. Bramki jakości

Źródła, bez duplikatów: (1) kroki `run:` z `.github/workflows/*.yml` na `pull_request`/`push`
(bez deploy, publikacji, sekretów); (2) overlay `.ai/project.md` (MCP `get_overlay`) —
kontrole/testy/komendy i `codegen:`.

Brak obu → default: `ruff check`, `pytest <pliki testów dotknięte ticketem>`,
`python manage.py makemigrations --check --dry-run`, `tsc --noEmit` w każdym pakiecie
frontendu, a przy `codegen: orval` (domyślnie) regeneracja klienta bez różnicy w `git diff`.
Zapisz jako założenie. Lokalnie idą tylko te **szybkie**; pełny zestaw testów — tylko CI.

### 3. Baza zielona

Ostatni przebieg CI na bazie:
`gh run list --branch $BASE --limit 1 --json conclusion,headSha`. Czerwony → **halt**
(przypadek C). Brak CI → bramki szybkie na `origin/$BASE` odpala krótki subagent.

### 4. Kolejka

Nazwy etykiet stanu: prawa kolumna `docs/agents/triage-labels.md`, jeśli projekt ma ten plik.
W komendach niżej stoją nazwy domyślne — wstawiaj nazwy z `triage-labels.md` projektu, nie dosłownie.

- Cel ani argumenty nie podają listy →
  `gh issue list --state open --label ready-for-agent --json number,title --limit 50`.
  Pusta lista → NIGHT-RUN REPORT „brak ready-for-agent” i koniec.
- Issue z listy ręcznej z etykietą `ready-for-human` / `needs-info` / `needs-triage` /
  `wontfix` → pomijasz, w raporcie `skipped: <etykieta>`. Bez etykiety stanu → bierzesz
  (zgodność wstecz).
- Etykiety designu (każdy klient, także z `ready-for-agent` — zabezpieczenie przed źle
  przełączonym stanem): `design: layout` bez `docs/design/<N>.md` na bazie
  (`git cat-file -e origin/$BASE:docs/design/<N>.md` pada) → `skipped: design spec missing`;
  `design: tokens` → `skipped: design: tokens`.

### 5. Kolejność

Dla każdego issue z listy:
`gh api repos/<owner>/<repo>/issues/<N>/dependencies/blocked_by --jq '.[].number'`.
Kolejność topologiczna; przy remisie kolejność z celu. Blokujący spoza listy i otwarty →
ticket dostaje needs-human „zablokowany przez #X spoza listy”.

### 6. Plik kontekstu nocy

`CTX=/tmp/night-run-<repo>-<data>/context.md` — raz na noc, poniżej ~150 linii, sekcje:

- **Nagłówek:** BASE, bramki szybkie, „pełny zestaw testów: tylko CI”.
- **Mapa aplikacji:** katalog → co w nim jest, 1 linia.
- **Konwencje:** same reguły z AGENTS.md, BUGBOT.md, `.ai/project.md`.
- **Pułapki toolchainu:** z pamięci projektu (CLAUDE.md, notatki agenta) i overlay — kit nie
  zna specyfiki projektu; brak źródła → „brak danych”.
- **Przekazanie:** `### #N` + modele, serwisy, endpointy, migracje z hand-backu. Dopisujesz
  **Ty** po każdym MERGED.
- **Stan (`## Stan` na końcu CTX):** ticket, id subagenta, krok (`ticket|review|resume|ci`),
  pauzy (od–do), wznowienia per ticket. Nadpisujesz **Ty** przy każdej zmianie kroku; tik
  czyta ją jako pierwszą.

### 7. Heartbeat (tylko Claude Code)

`CronCreate`, recurring `13,33,53 * * * *`, prompt z wpisaną na sztywno ścieżką CTX:
`night-run heartbeat: przeczytaj sekcję „Heartbeat” w /night-run i „## Stan” w <CTX>,
działaj wg niej.` Bez `CronCreate` u klienta → ten krok pomiń (limit API zostaje haltem C).
Każde zakończenie nocy (wszystko MERGED, halt, raport) zaczyna się od `CronDelete`.

## Łańcuch na ticket — dokładnie ten

1. **Subagent ticketu** (świeży, `model: MODEL`): `N`, `BASE`, `CTX` + „Prompt ticketu”
   dosłownie. Wraca z hand-backiem i niewypchniętym branchem. Zapisz jego id, nadpisz
   `## Stan` w `CTX` (ticket, id, krok `ticket`).
2. **Subagent review** (zawsze nowy): „Review `git diff origin/$BASE...HEAD` na `<branch>`:
   `/review-bugbot` + `/review-backend` i/lub `/review-frontend` wg diffa. Czytaj tylko diff
   i dotknięte pliki, zakresami. Zwróć `Severity | Location | Finding | Fix` +
   potwierdzony/niepewny. Nie edytuj.” Zapisz jego id, nadpisz `## Stan` (krok `review`).
3. **Wznów subagenta ticketu** (w Claude `SendMessage` na jego id, nie nowy start) z
   findingami: potwierdzone → poprawka + test; niepewne → opis PR. Review z kroku 2 **jest**
   potwierdzeniem review dla `/git-end`. Przed wznowieniem nadpisz `## Stan` (krok `resume`),
   przed CI — krok `ci`. Dalej kroki 6–8 promptu.
4. Sprawdź dowód sam: `gh pr view <PR> --json state` → `MERGED`.
5. Dopisz „Przekazanie” do `CTX`, policz koszt ticketu (sekcja „Pomiar”),
   `git checkout $BASE && git pull` przed następnym ticketem.

Hand-back needs-human albo halt od subagenta → sekcja „Gdy ticket napotka problem”.

## Prompt ticketu

Przekazujesz go subagentowi bez skracania.

```markdown
Ticket #<N>, baza <BASE>. Kontekst projektu: <CTX> — przeczytaj go najpierw, zamiast
AGENTS.md, BUGBOT.md i ADR-ów. ADR czytasz tylko wtedy, gdy issue go wskazuje.
Specyfikacja = body issue + komentarz z briefem agenta (AGENT-BRIEF od `/triage`), jeśli jest:
`gh issue view <N> --comments`.
Frontend: po kroku 1 plik `docs/design/<N>.md` jest w drzewie (z <BASE> albo z istniejącego
brancha issue) → layout wyłącznie według `docs/design/<N>.md`; rozbieżność = hand-back
needs-human. Brak speca → layout domyślny z bundla (`stack:frontend:ui-structure`).

Kroki:
1. `/git-start #<N>`. Branch musi wychodzić z <BASE>; inaczej
   `gh issue develop <N> --name <typ>/<N>-<slug> --base <BASE> --checkout`.
2. Duży ticket (>3 kryteria akceptacji albo >1 aplikacja/pakiet): plan 5–10 kroków.
3. Test-first: test czerwony → kod → zielony.
4. Lokalnie tylko testy dotknięte ticketem + bramki szybkie z <CTX>. Pełny zestaw: CI.
5. `/git-commit`. Nie pushujesz. Oddajesz hand-back.
--- po wznowieniu ---
6. Poprawki z review, `/git-end` — review już zrobione, bramka review potwierdzona, nie
   czekaj. Opis PR: testy, założenia jako Q z odpowiedzią, niepewne findingi.
   CI: `sleep 20; gh pr checks <PR> --watch` wzorcem „Praca w tle” (`sleep`, bo checki
   rejestrują się z opóźnieniem). `EXIT` ≠ 0 i w logu `no checks reported` → to nie błąd:
   powtórz raz po minucie; nadal brak → brak CI na tę bazę, merge po bramkach szybkich,
   zapis w `Final report`. Inne `EXIT` ≠ 0 → napraw, maks. 2 próby.
7. `gh pr merge <PR> --merge`.
8. Jeden raport: komentarz na PR `## Final report (night-run) — #<N>`. Na issue tylko link
   do niego, potem `gh issue close <N>` (`Closes` nie zamyka przy merge na niedomyślną).

Reguły:
- Plik > ~150 linii czytasz zakresami: `codegraph explore` (gdy jest `.codegraph/`) albo
  `grep -n` + `sed -n X,Yp`, wiele odczytów w jednej komendzie. Nigdy całego pliku: ani
  `cat` / `xargs cat`, ani Read bez `offset`/`limit`.
- Bez skilli procesowych (writing-plans, tdd, verification, review-*). Wolno `/git-*`.
- Praca w tle (CI, dłuższy `pytest`): od razu w tle (w Claude `run_in_background`), jeden
  wzorzec, bez pipe (`| tee` gubi exit code): `<cmd> > <log> 2>&1; echo "EXIT=$?" >> <log>`.
  Koniec = powiadomienie o zakończeniu zadania albo linia `EXIT=` w logu; wynik = jej
  wartość. Nie czekasz na tekst narzędzia (`passed`, `All checks…`) ani na stan procesu
  (`ps`, `pgrep`, `kill -0`) — taka pętla wisi bez końca albo staje na prośbie o zgodę.
  Nie oddajesz wyniku, dopóki praca w tle trwa; przed hand-backiem żadne Twoje zadanie
  w tle nie działa, zbędne kończysz.
- Zero pytań do człowieka. Needs-human albo halt → wypchnij branch, hand-back z Q1/Q2
  (komentarz na issue daje orkiestrator).

Hand-back (bez streszczania pracy): stan (do review / MERGED / needs-human / halt); branch,
PR, link `Final report`; testy unit/x, integration-db/y, integration/z, e2e/w (z komend);
dodane modele, serwisy, endpointy, migracje; założenia.
```

## Testy — piramida po zależności, nie po warstwie

| Poziom | Test | 
| --- | --- |
| unit | bez bazy (logika czysta, mapowania) |
| integration-db | `django_db` — ORM, modele, endpointy, webhooki |
| integration | żywe usługi (Postgres, Redis…) — marker `integration` |
| e2e | Playwright (lub inny runner e2e w repo) |

- Zewnętrzne API płatności i podobne zawsze atrapą.
- **Nowe e2e** tylko gdy issue tego wymaga **i** runner e2e jest w repo. Inaczej „test
  przepływu na API”, tak nazwany w raporcie.
- Liczby **z komend**: `pytest <pliki> -m <marker> --co -q`. Brak markerów → per plik
  (`pytest <plik> --co -q`) + „brak markerów”. Trafiają do opisu PR i `Final report`.

## Zakazy

Nie skipujesz, nie usuwasz i nie osłabiasz testów. Nie `--no-verify`. Na `BASE` i inne
chronione gałęzie tylko merge PR po zielonym CI, nigdy push.

## Gdy ticket napotka problem

### Wyzwalacze

**needs-human** (przypadek A lub B): kryterium akceptacji niejasne albo sprzeczne
z ADR/`CONTEXT.md`; decyzja produktowa/prawna, której nie ma w issue ani docs; CI czerwone
po 2 próbach naprawy; merge zablokowany przez ochronę gałęzi (wymagane review itp.) — PR
zostaje otwarty.

**halt** (przypadek C): czerwona gałąź bazowa, awaria CI/infrastruktury, brak sekretów.
U klientów bez `CronCreate` także limit API; w Claude Code limit API to przypadek D, nie C.

**Limity:** 2 pauzy na noc (tylko 429); 2 wznowienia na ticket (inne przyczyny) → needs-human.

### Zasięg — zanim cokolwiek zrobisz

Które tickety z listy od tego zależą: jawnie (blocked-by) i niejawnie (ticket zmienia
model/pole/serwis/endpoint, z którego korzysta inny — treść issue + „Przekazanie” w `CTX`).

### Przypadki

- **A — tylko ten ticket:** raport na issue, bez merge, następny ticket.
- **B — część listy:** raport na issue + na każdym zależnym `needs-human: czeka na
  odpowiedzi z #N`; robisz tylko tickety spoza zasięgu.
- **C — wszystko:** `CronDelete`, potem `night-run halted: <powód>` na pierwszym issue,
  NIGHT-RUN REPORT, koniec.
- **D — pauza limitu (tylko Claude Code, 429 / „session limit”):** zapisz pauzę do `## Stan`
  (od–do, godzina resetu z komunikatu jeśli była), koniec tury — nie halt. Przy pierwszym
  udanym tiku heartbeat: teraz < koniec nocy (domyślnie 08:00 lokalnie albo `koniec:` z celu)
  **i** pauzy ≤ 2 → łańcuch od kroku z `## Stan`; inaczej halt C.

Przy A i B, obok komentarza, przełącz stan ticketu (tylko gdy etykieta jest w repo; inaczej
sam komentarz; `--remove-label` tylko, gdy issue ma `ready-for-agent`):
`gh issue edit <N> --remove-label ready-for-agent --add-label ready-for-human`
(`needs-info` zamiast `ready-for-human`, gdy stoi na brakujących danych od zgłaszającego,
nie na decyzji). Merge nie rusza etykiet — zamknięte issue wypada z kolejki.

### Decyzja odwracalna — nie blokuj

Niejasność odwracalna (nazwa, kształt odpowiedzi nowego endpointu, wartość domyślna),
która nie dotyka pieniędzy, danych osobowych ani migracji usuwającej dane → najbezpieczniejsze
założenie, jako Q z przyjętą odpowiedzią w opisie PR. Blokuje tylko to, czego nie da się cofnąć.

### Format raportu needs-human (komentarz na issue)

```markdown
needs-human: <jedno zdanie, co stoi>

Q1. <pytanie>
- Opcje: a) … b) …
- Moja rekomendacja: … bo …
- Konsekwencja wyboru: …

Q2. …

Sprawdzone: <pliki, ADR, testy, komendy z wynikiem>
Wpływ: blokuje #X, #Y · nie blokuje #Z
Stan kodu: branch <nazwa> wypchnięty / porzucony
```

## Heartbeat (tik co 20 min, tylko Claude Code)

Najpierw zaległe powiadomienia, potem status subagenta z `## Stan` (`ListAgents`):

- działa → jedna linia „ok”, koniec tiku;
- failed albo zakończony bez hand-backu → `SendMessage` na jego id; gdy się nie da → nowy
  subagent ticketu „kontynuuj z brancha `<branch>`: sprawdź `git log` / PR, dokończ od kroku X”
  (fallback obowiązkowy — `SendMessage` po 429 może nie zadziałać);
- działa, ale bez zmian w gicie i PR od > 90 min (`git log -1 --format=%ct` na branchu +
  ostatni check) → `TaskStop`, potem wznowienie jak wyżej;
- nic nie działa, a noc nieskończona (także przerwana tura orkiestratora) → łańcuch od kroku
  z `## Stan` (przypadek D: pauzy ≤ 2 i przed końcem nocy → wznów, inaczej halt C).

## Pomiar kosztu ticketu (Claude Code)

Po każdym tickecie liczysz jego subagentów (ticket + review) — `python3`, bez `jq`.
Wiersz: agent | model | tury | input | cache_creation | cache_read | output | maks. kontekst | czas.

```bash
python3 -c '
import json, sys, datetime as dt
K = ("input_tokens", "cache_creation_input_tokens", "cache_read_input_tokens", "output_tokens")
for p in sys.argv[1:]:
    use, ts, mod = {}, [], set()
    for line in filter(str.strip, open(p)):
        d = json.loads(line); m = d.get("message") or {}
        if d.get("timestamp"): ts.append(dt.datetime.fromisoformat(d["timestamp"].replace("Z", "+00:00")))
        if d.get("type") == "assistant" and m.get("usage") and m.get("model") != "<synthetic>":
            u = use.setdefault(m.get("id"), {}); mod.add(m.get("model") or "?")
            for k in K: u[k] = max(u.get(k, 0), m["usage"].get(k, 0))
    tot = [sum(u.get(k, 0) for u in use.values()) for k in K]
    ctx = max((sum(u.get(k, 0) for k in K[:3]) for u in use.values()), default=0)
    mins = (max(ts) - min(ts)).total_seconds() / 60 if ts else 0
    row = [p.rsplit("/", 1)[-1], ",".join(sorted(mod)), len(use), *tot, ctx, f"{mins:.0f} min"]
    print("| " + " | ".join(map(str, row)) + " |")
' ~/.claude/projects/*/*/subagents/agent-{<id1>,<id2>}.jsonl
```

Tura = unikalne `message.id` (odpowiedź zajmuje kilka linii, `output_tokens` rośnie — bierz
maks.). Czas = wall-clock z oczekiwaniem na review. Inny klient → „brak transkryptu”.

## NIGHT-RUN REPORT — ostatnia wiadomość

Zaczynasz od `CronDelete`. Warunek `/goal` sprawdza transkrypt, więc raport i dowody muszą
paść w rozmowie.

```markdown
## NIGHT-RUN REPORT
Baza: <BASE> — <powód>
Bramki szybkie: <lista> (źródło: CI / overlay / założenie) · pełny zestaw: CI
Model ticketu: <MODEL> albo per ticket: #N <model> — <powód>
Pauzy limitu: <liczba> (przestój <od>–<do>, reset: <godzina z komunikatu / brak>)
Wznowienia per ticket: <#N: liczba>

| Ticket | Stan | Dowód | Koszt (tury / cache_read / output / kontekst / czas) |
| --- | --- | --- | --- |
| #N | MERGED | `gh pr view <PR> --json state` → MERGED; <link Final report> | 58 / 6.1M / 41k / 164k / 20 min |
| #M | needs-human → ready-for-human | <link do komentarza> | … |
| #J | skipped: <etykieta> | etykieta stanu z listy ręcznej | — |
| #K | halted | <link do komentarza night-run halted> | … |

Założenia: <co przyjęto bez danych z repo>
Pomiar per agent: <wiersze ze snippetu>

### Nowe ekrany
| PR | Route |
| --- | --- |
| #<PR> | <plik dodany w routes-web / routes-mobile> |
```

**Nowe ekrany:** pliki dodane w katalogach `routes-web` / `routes-mobile` (`.ai/project.md
## Ścieżki`) przez PR-y zmergowane tej nocy — tylko pliki nowe:
`gh api repos/<owner>/<repo>/pulls/<PR>/files --jq '.[] | select(.status=="added") | .filename'`
przeciw tym ścieżkom. Lista do decyzji człowieka; issue nie zakładasz. Brak kluczy
`routes-*` → „brak routes-* w ## Ścieżki”.

**Follow-up „zaprojektuj layout”** (sesję designu robi człowiek w Claude Code — Claude Design,
`/design-sync`; przebieg w `docs/agents/design-handoff.md`). Zmergowany ticket **bez**
`design: *` dodał plik w `routes-web` / `routes-mobile` → po merge'u zakładasz issue:

- tytuł: `refactor: design layout for <ekran>` (`refactor` ↔ `type: refactor` ↔ branch
  `refactor/N-…`);
- body: link do PR, route, komponenty sekcji z diffa, odnośnik do formatu speca w bundlu
  (`stack:frontend:ui-structure`), linia „Sesja designu: Claude Code”;
- etykiety (tylko istniejące w `gh label list --limit 200`): `design: layout`,
  `ready-for-human`, `type: refactor`, `scope: frontend:web` albo `scope: frontend:mobile`.

Brak klucza `routes-*`, brak etykiety `design: layout` albo `--dry-run` → tylko wiersz
w „Nowe ekrany”. Do tabeli „Nowe ekrany” dopisz kolumnę `Follow-up` z numerem issue.

Każdy ticket z listy ma w tabeli dokładnie jeden wiersz. Brak dowodu = ticket nie jest skończony.
