Cloud Chat Routing — przewodnik

Phase 70. Kiedy rozmawiasz z workerem z CRM lub Telegram, ta konwersacja działa teraz wewnątrz twojego kontenera Hetzner Cloud, a nie na serwerze master. Zamknij laptopa, przełącz się na Telegram, pracuj dalej — sesja, pliki i otwarte repozytoria żyją w twoim workspace Cloud.


TL;DR


Dlaczego to ważne

Phase 60 dostarczył Standard Cloud jako "zawsze włączoną maszynę Linux z Claude Code". Phase 69 podłączył twoje repozytoria do bot org. Ale do Phase 70 workflow chatu nadal spawnował claude -p na master VPS — twój kontener Cloud siedział bezczynnie, a praca chatowa żyła na masterze.

Phase 70 rutuje chat do kontenera, żeby propozycja wartości faktycznie się ziściła:


Jak działa routing

Każda wiadomość chat przechodzi przez to drzewo decyzyjne:

chat msg arrives → child-bot on master
  │
  ▼
getWorkerTarget(ownerChatId)
  │
  ├─ user.plan === 'cloud' AND container.status ∈ (ready, paused) ?
  │   │ YES
  │   ▼
  │   ensureAwake(target)        ← Phase 70.4: wake if paused (~1s)
  │   │
  │   ▼
  │   spawnWorker → docker exec  ← Phase 70.2: --workdir /workspace/<slug>
  │                                 (Phase 70.3 maps project_name → slug)
  │   │
  │   ▼
  │   bash -lc 'exec "$@"' bash claude -p "..."
  │                                ↑ login shell sources ~/.profile so
  │                                  ANTHROPIC_API_KEY env reaches claude
  │
  └─ otherwise → Bun.spawn(["claude", ...]) on master (today's default)

Ta pojedyncza decyzja zapada raz na wiadomość. Log workera emituje pojedynczą linię informującą o wybranym celu:

claude spawn: container/ready
claude spawn: container/ready (woke from paused)
claude spawn: local

Jeśli kiedykolwiek zastanawiasz się, gdzie uruchomiła się twoja ostatnia wiadomość, to ta linia, której powinieneś szukać.


Jak zweryfikować, że to działa

1. Pill w nagłówku

Po sprovisionowaniu twojego Cloud Workspace, Ustawienia → przeładuj CRM i spójrz na nagłówek u góry po prawej. Powinieneś zobaczyć jedno z:

Pill Znaczenie
Cloud (zielona kropka) Kontener żyje, chat zrutuje się do niego
Cloud · asleep (szara kropka) Kontener wstrzymany; następna wiadomość chat go obudzi
(nic) Brak sprovisionowanego kontenera

Pill odpytuje status kontenera co 30 sekund, więc opóźnia zdarzenie wake/pause nawet o pół minuty.

2. Log workera

Jeśli masz dostęp do terminala mastera (tmux attach -t citadel-child dla bota dev Arc OS), tail output workera i obserwuj linię spawn przy każdej wiadomości.

3. Pliki faktycznie wewnątrz kontenera

Otwórz terminal Cloud z /cloud, następnie:

cd /workspace/<your-project>
git log -3
ls -lh

Jeśli edycje wywołane chatem pojawiają się tutaj (a arc cloud sync z twojego laptopa flaguje behind z odpowiednią liczbą commitów), routing jest podłączony prawidłowo.


Cykl życia: pause, wake, push, fetch

Cykl życia z Phase 69 nadal obowiązuje:

  1. Idle 30 min → cron wstrzymuje kontener.
  2. Przed pausesnapshotAndPush auto-commituje brudne drzewa w każdym repozytorium pod /workspace/ i pushuje je do bot org.
  3. Następna wiadomość chatensureAwake odpauzowuje kontener (~1 s), następnie odpala fetchAll w tle, żeby wskaźnik origin/main każdego repozytorium był świeży.
  4. arc pull <project> na twoim laptopie czyta te auto-commity.

Koszt pause/wake jest wkalkulowany w latency następnego chatu — nie musisz o tym myśleć.


Ciągłość sesji

Claude Code przechowuje historię konwersacji w ~/.claude/projects/<cwd-hash>/<session-id>.jsonl. Wewnątrz kontenera ten katalog siedzi na volume mount, więc przeżywa docker pause / docker unpause. Dopóki twój chat celuje w ten sam projekt, --resume <session-id> claude'a znajduje ten sam JSONL po wybudzeniu i kontynuuje wątek.

Jedna uwaga: hash cwd zmienia się między local ↔ cloud

Hash jest derivowany ze ścieżki katalogu roboczego. Na masterze claude uruchamia się w /opt/repos/<slug>; w kontenerze uruchamia się w /workspace/<slug>. Dwie różne ścieżki → dwa różne hashe → dwie różne historie konwersacji.

Co to oznacza w praktyce:

Rozważaliśmy symlinkowanie /opt/repos/workspace na masterze, żeby utrzymać hashe zgodne, ale to mieszałoby dwa drzewa plików i psułoby boty mastera, które działają na planach Free. Dwie historie to mniejsze zło.


Troubleshooting

"claude spawn: local", ale jestem na planie Cloud

Sprawdź, w kolejności:

  1. subscription.plan === 'cloud' w pillu billingowym dropdownu użytkownika?
  2. /cloud/status zwraca status: 'ready' lub 'paused'?
  3. Czy utworzyłeś projekt PO sprovisionowaniu? Kontenery klonują tylko repozytoria projektów, które istniały w momencie provisioningu (Phase 69.3). Re-provisionuj lub re-triggeruj bootstrap, żeby podchwycić nowo utworzone projekty.

Chat zawiesza się na ~5 sekund, potem odpowiada

To pierwsze wybudzenie po długim idle. Wake to ~1 s; reszta to własny startup claude'a + pierwsza inferencja. Kolejne wiadomości, gdy kontener pozostaje rozgrzany, mają normalne latency.

"Not logged in" od claude'a

Phase 70.5 naprawił bug, w którym ~/.bashrc wcześnie returnował dla nieinteraktywnych shelli, więc ANTHROPIC_API_KEY nigdy nie docierał do claude'a. Fix: klucz żyje teraz w ~/.profile (sourcowanym przez wrapper login shell). Jeśli upgrade'owałeś w trakcie Phase-70 i nigdy nie zapisałeś ponownie tokena, idź do /cloud Krok 1, wklej swój sk-ant-api03-… ponownie. Świeżo zapisane tokeny lądują we właściwym pliku.

Kontener pokazuje się jako wstrzymany przez godziny po wiadomości chat

Jedną przyczyną jest polling co 30 sekund w pillu nagłówka. Drugą: jeśli wakeContainer() zawiódł po cichu, wiersz DB czyta ready, ale Docker mówi paused. Uruchom arc cloud sync <project> — to wyciągnie rozbieżność w macierzy.


Zobacz też