Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,18 @@ The reconciler also needs read access to the `SandboxWarmPool` it manages: grant

An optional GitHub push webhook makes pool refresh immediate instead of waiting for the periodic refresh cycle. Configure `GITHUB_WEBHOOK_SECRET` on Studio and point a push webhook at `POST /api/_github/webhook`; without the secret configured, the route answers `503` and pools simply refresh on their own schedule.

<Callout type="note">
Dispatches that only need a checkout — a Claude Code or other harness run against a specific branch, not an interactive dev server — never bind to a tenant warm pool. Binding one would de-warm it for no benefit: the dispatch's branch checkout is classified as a branch change, and a branch change stops the pod's running dev server. These `cloneOnly` claims always fall back to the generic pool (or a cold claim) instead. Separately, if a task asks a sandbox to start a process that's already running (typically `dev`), the daemon now returns the existing task instead of starting a second one — two dev servers on one pod's memory limit was a reliable way to OOM it.
</Callout>

### Admission and capacity

Before making a claim, Studio checks whether the cluster can actually schedule another sandbox pod. `SANDBOX_MAX_CONCURRENT_HOSTED_RUNS` still caps concurrency per pod, but it has no visibility into whether any node has room — on a full node, a burst of runs used to produce a pile of pods stuck `Pending` with `FailedScheduling: Insufficient memory`, each one failing only after the 180s readiness timeout.

The capacity check reads the scheduler's own verdict instead of forecasting: a pod the scheduler could not place stays `Pending` with a `PodScheduled=False`/`Unschedulable` condition, and Studio treats that as "the cluster is full." A run that would add to that pile is **parked** — held, not claimed — until a pod becomes schedulable again, instead of being dispatched straight into a node with no room. This is cluster-wide and complements, not replaces, the per-pod concurrency limit.

The check fails open: if the probe itself errors, Studio treats capacity as available rather than turning a broken health check into a global stop on all sandbox runs.

### Daemon telemetry (OTLP metrics)

The sandbox daemon can export OTLP metrics to an OpenTelemetry collector. This is separate from — and unrelated to — Studio's own monitoring export described in [Monitoring](/en/studio/self-hosting/monitoring): here the metrics come from the sandbox daemon itself (process/runtime health), not from Studio's tool-call logs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,18 @@ Declare cada pool nos dois lados, com o **mesmo** `name`:
Conceda ao Role do runner a permissão `sandboxwarmpools: get` (o chart faz isso automaticamente a partir da versão 0.12.0 do `sandbox-env`) — o reconciler lê o `status.selector` de cada pool para encontrar seus pods, e uma permissão faltante bloqueia silenciosamente toda tentativa de aquecimento.
</Callout>

<Callout type="note">
Dispatches que só precisam de um checkout — uma execução do Claude Code ou de outro harness contra um branch específico, sem dev server interativo — nunca se vinculam a um tenant warm pool. Vincular um seria destrutivo sem benefício: o checkout de branch do dispatch é classificado como troca de branch, e uma troca de branch para o dev server em execução no pod. Esses claims `cloneOnly` sempre caem para o pool genérico (ou um claim frio). Separadamente, se uma task pede para um sandbox iniciar um processo que já está em execução (normalmente `dev`), o daemon agora retorna a task existente em vez de iniciar uma segunda — dois dev servers no limite de memória de um único pod era uma forma confiável de matar o pod por OOM.
</Callout>

### Admissão e capacidade

Antes de fazer um claim, o Studio verifica se o cluster consegue de fato agendar outro pod de sandbox. `SANDBOX_MAX_CONCURRENT_HOSTED_RUNS` continua limitando a concorrência por pod, mas não tem visibilidade sobre se algum node tem espaço — em um node cheio, uma explosão de runs costumava gerar uma pilha de pods parados em `Pending` com `FailedScheduling: Insufficient memory`, cada um falhando somente após o timeout de 180s de readiness.

A verificação de capacidade lê o próprio veredito do scheduler em vez de prever: um pod que o scheduler não conseguiu posicionar fica `Pending` com uma condição `PodScheduled=False`/`Unschedulable`, e o Studio trata isso como "o cluster está cheio". Um run que aumentaria essa pilha é **estacionado** — mantido em espera, sem claim — até que um pod volte a ser agendável, em vez de ser despachado direto para um node sem espaço. Isso é validado no nível do cluster e complementa, sem substituir, o limite de concorrência por pod.

A verificação falha aberta: se a própria sondagem der erro, o Studio trata a capacidade como disponível em vez de transformar uma verificação de saúde quebrada em uma parada global de todos os runs de sandbox.

### Telemetria do daemon (métricas OTLP)

O daemon do sandbox pode exportar métricas OTLP para um collector OpenTelemetry. Isso é separado — e não relacionado — à exportação de monitoramento do próprio Studio descrita em [Monitoring](/pt-br/studio/self-hosting/monitoring): aqui as métricas vêm do próprio daemon do sandbox (saúde do processo/runtime), não dos logs de chamadas de ferramentas do Studio.
Expand Down