Pokud na macOS pracuješ s Claude Code a ještě nemáš zapnutý sandbox, zapni si ho. Je to jediná smysluplná obrana, jak izolovat agenta od zbytku stroje — filesystem, síť, syscally — a riziko vážného průšvihu klesne řádově. Praktický návod, jak to nakonfigurovat, je třeba v článku Sandboxing Claude Code on macOS. Tenhle text předpokládá, že už ten krok máš za sebou — a popisuje jeden konkrétní side-effect, na který jsem narazil.
Symptom
Začalo to nenápadně — všiml jsem si, že Claude Code mi začal nezvykle dlouho trvat i jednoduché úkoly. Drobné refaktory, na které byl dřív zvyklý odpovědět během chvíle, najednou zabraly desítky minut. Když jsem se podíval pořádně, co se uvnitř session vlastně děje, ukázalo se, že agent opakovaně spouští dotnet build a každý jednotlivý běh mu trvá pět minut. Lokálně, ve vedlejším terminálu, ten samý build doběhne za sekundu.
Výstup z těch dlouhých běhů vypadal pokaždé identicky:
Determining projects to restore...
Build FAILED.
0 Warning(s)
0 Error(s)
Time Elapsed 00:05:00.81
Pět minut, FAILED, a přitom nula chyb a nula varování. Build failed s 0 errors? Co to vůbec je? Buď build selhal a má nám sdělit proč, nebo neselhal — ale tyhle dvě věci dohromady jsou nesmysl.
Diagnostika
Nechal jsem to vyřešit samotného agenta — zajímalo mě, jakou cestou se k příčině dostane on. Co následuje, je jeho úvaha nahlas.
První rozumný tip byl NuGet restore — síť uvnitř sandboxu je omezená a v logu svítilo „Determining projects to restore“. Zkusil tedy --no-restore. Stejný výsledek. Tak možná zápisová práva do ~/.dotnet nebo ~/.nuget cache? Ne. Přidal --no-dependencies -v:m, nic. Pro jistotu pak pustil ještě jeden běh úplně mimo sandbox přes dangerouslyDisableSandbox: true — a build doběhl za 1,02 sekundy. Tím bylo jasné, že problém je 100 % sandbox.
Když běžná podezření zradila, sáhl po -v:diag — to MSBuild zaplaví výstup úplně vším, co se uvnitř děje. Hned na prvních pár desítkách řádků se objevilo:
(eval):1: nice(5) failed: operation not permitted
Příčina
Claude Code na macOS pouští každý Bash tool call přes Apple Seatbelt sandbox, který blokuje syscall setpriority() (běžně volaný přes nice). MSBuild defaultně forkuje worker nody (-maxcpucount), každý worker si při startu chce snížit svou prioritu přes nice(), sandbox volání odmítne — a worker se zacyklí v silent retry/respawn smyčce. Žádná chyba na stdout, žádný non-zero exit, žádné varování. Jen čekání, až dotnet sám zabije proces vlastním 5-minutovým watchdogem.
Klíčové na tomhle failure mode: MSBuild považuje nice failure za non-fatal, a worker fork stack se snaží vzpamatovat. Sandbox tedy nezablokuje build — jen mu odebere mechanismus terminace.
Řešení
Sandbox nenabízí granular syscall allow (Seatbelt na macOS to neumí filtrovat per syscall pro běžné uživatelské profily). Co Claude Code umí, je vyloučit konkrétní commands ze sandboxování:
{ "sandbox": { "excludedCommands": ["dotnet *"] }}
V ~/.claude/settings.json. Pozor na syntax — excludedCommands používá Claude Code permission-rule syntax, tedy prefix s wildcardem. Plain ["dotnet"] se s ničím nematchne a problém přetrvá. Po změně settings je potřeba session restart.
Výsledek: dotnet build v sandboxu 0,62 s, žádný nice retry-loop.
