Curso / Lição 19  ·  EN
Lição 19 · Motor & método · 6 de 8

O swarm: orchestrator → lead → worker

O swarm L3 é como o Alembic roda muitas unidades de trabalho concorrentemente e sobrevive a crashes. Um orquestrador de 3 camadas com profundidade limitada sobre uma fila de tasks com portão de dependências, com estado filesystem-como-verdade, isolamento por git-worktree, um park T4 duro, e resume seguro a crash. É o pacote que a matriz de fusão diz ter tornado o delegate_tool.py do Hermes um IGNORE — o Alembic já delega, nativamente e melhor. Fonte: packages/swarm/.

Três camadas, um nível de aninhamento

A hierarquia de papéis é orchestrator → lead → worker, e é limitada: MAX_DEPTH = 2. Um nó na profundidade máxima é uma folha e não pode spawnar; subtasks são tasks-folha, então o aninhamento é estruturalmente de um nível. É uma guarda deliberada contra fan-out recursivo descontrolado:

orchestrator (prof. 0) lead (prof. 1) lead (prof. 1) worker (prof. 2) worker (prof. 2) canSpawn = false em MAX_DEPTH (folha) um worker é folha — ele executa, nunca spawna; o aninhamento é de um nível, estruturalmente

A fila — com portão de dependência, pronto significa pronto

Só tasks ready rodam. Uma task se torna ready quando todos os seus dependsOn alcançam sucesso-terminal (done). O orquestrador nunca roda uma task cujos pré-requisitos não tenham de fato tido sucesso — o grafo de dependências é honrado, não torcido. Tipos de worker:

Tipo de workerO que roda
model workeruma chamada de adapter (o run() da lição 14)
command workertaskSpec.command → um subprocesso real; saída 0 → done
background workertaskSpec.background → um filho destacado que sobrevive à morte do pai e re-anexa ao seu report no resume (requer command, proíbe isolate)

Resume seguro a crash — o filesystem é a verdade

O estado é durável no disco: um journal events.jsonl append-only mais um checkpoint.json. O resume reproduz o último checkpoint e todo evento task-state (idempotente, o último-por-id vence). A parte engenhosa é como ele trata um worker que morreu no meio do voo:

// packages/swarm/src/orchestrator.ts:706-755 (condensado) — replayInto
// reproduz checkpoint + todo evento task-state (último-por-id vence, idempotente)
// uma task 'running' órfã (worker morreu) é rebaixada a 'ready' e re-tentada
//   ⇒ execução pelo-menos-uma-vez; enqueue é idempotente então o denominador do
//     monitor cobre toda task mesmo após um crash no meio da declaração

Todo valor que cruza uma fronteira de durabilidade é parseado por Zod na leitura, então uma linha de journal corrompida ou editada à mão é rejeitada na fronteira em vez de confiada em silêncio (filesystem-como-verdade, invariante ③ da lição 16). A unicidade global de id por toda a árvore é validada antes de qualquer coisa ser jornalada, então um id duplicado não pode envenenar um resume.

Isolamento e o park T4 — fail-closed, ambos

Isolamento de worktree fail-closed

isolate: true sem config de worktree é um erro, nunca uma run sem-isolamento em silêncio. Se você pediu isolamento e o ambiente não pode prover, a run para — ela não executa sua task quietamente contra a working tree compartilhada. Um command worker que não pode tocar o checkout principal recebe um git worktree via withWorktree, ou não roda.

Park T4 duro

Tasks irreversíveis / legais / de segurança / T4 são roteadas para o ledger do park e nunca auto-executadas. O park é idempotente entre resumes e estruturalmente protegido — uma task estacionada é imóvel. As razões de park são um conjunto fechado: tier-t4 / irreversible / legal / security / manual. Esta é a expressão em nível de swarm do princípio do portão humano: o motor recusa dar o passo perigoso por conta própria, e você o reabre com alembic approve/reject/propose.

O sinal de reward (computeReward) é um escalar heurístico moldado — estilo PARL, não aprendizado por reforço — e tem portão HITL por requiresApproval. O nome evoca RL; a implementação é uma heurística determinística com um humano no loop.

1. Um lead na profundidade 1 spawna workers. Um desses workers pode spawnar seus próprios sub-workers?
Correto: c. A profundidade é limitada em 2. Um worker é folha; subtasks são tasks-folha, então o aninhamento é estruturalmente de um nível. Isso evita fan-out recursivo descontrolado — o limite de profundidade é um invariante, não uma config.
2. Um processo worker é morto no meio da task e a run é retomada depois. O que acontece com essa task?
Correto: b. replayInto reproduz o checkpoint mais os eventos task-state; uma task deixada running por um worker morto é rebaixada a ready e re-rodada. Combinado com enqueue idempotente, o resume cobre toda task mesmo após um crash no meio da declaração.
3. Um task spec define isolate: true mas nenhuma config de worktree é fornecida. O que o orchestrator faz?
Correto: d. O isolamento de worktree é fail-closed: pedir isolamento que você não pode obter para a run. O motor nunca executa quietamente uma task contra o checkout compartilhado quando você pediu separação explicitamente.

Confusões comuns

"PARL reward significa que ele aprende por reforço." Não — computeReward é um escalar heurístico moldado, explicitamente não RL, e tem portão HITL por requiresApproval. O reward molda a priorização com um humano no loop; não há gradiente, não há treino.
"Background workers rodam de fato assíncronos, liberando o slot." Ainda não — um gap conhecido: background workers ainda bloqueiam o slot da task enquanto fazem polling do report; a melhoria de drain assíncrono é um slice futuro (a costura do dispatcher já a acomoda). O curso declara gaps com honestidade em vez de exagerar.