Uma run do Alembic não "termina" — ela atravessa cancelas: Scope → Council → Proof → Validator → Publish. Cada unit.proof[] vira comando real que precisa sair 0; trabalho arriscado estaciona em T4 até um humano decidir; e o plano inteiro é determinístico por construção — a VM recusa Date.now() e Math.random() antes de executar uma linha. É a materialização do Proof Gate do loop-engineering: nenhum progresso sem prova.
Cada gate tem dono e arquivo: ① Scope (packages/forge/src/scope.ts:37, loadScope) copia GOAL.md, plan module e contrato para o run-dir — a run carrega o próprio escopo; ② Council (packages/mission/src/council-gate.ts:79, runCouncilGate) é o GO/NO_GO opcional de pré-voo; ③ Proof (packages/coda/src/proof.ts:26, runProofGate) exige exit 0 de cada prova; ④ Validator (packages/coda/src/validator.ts:105, runValidatorGate) escrutina por milestone com quem NÃO construiu; ⑤ Publish (packages/coda/src/publish.ts:63, runPublishGate) gera e publica o curso visual-teach.
Pense como… as eclusas de um canal: o barco (a run) só sobe de nível quando a câmara anterior FECHOU e encheu por completo — não existe "meio-eclusa". Onde quebra: eclusa atrasa todo barco igualmente; aqui o Council e o Validator podem ser dispensados por configuração (skipScrutiny/skipUserTesting) quando o risco não justifica.
| # | Gate | Função · quem falha fechado | Fonte |
|---|---|---|---|
| ① | Scope | Materializa GOAL.md + plan + contrato no run-dir | forge/src/scope.ts:37 |
| ② | Council | GO/NO_GO opcional de pré-voo | mission/src/council-gate.ts:79 |
| ③ | Proof | Cada unit.proof[] com exit 0 — senão err | coda/src/proof.ts:26 |
| ④ | Validator | Escrutínio independente por milestone | coda/src/validator.ts:105 |
| ⑤ | Publish | Gera e publica o curso visual-teach | coda/src/publish.ts:63 |
No plan module, cada string de unit.proof[] vira uma task bash -c que depende da unit. O gate (runProofGate) então: lê os estados do journal de eventos append-only — e não do checkpoint, porque "the gate reads task states from the append-only event journal so it survives checkpoint overwrites" (proof.ts:22–24); filtra as tasks com metadata.kind === 'proof' (proof.ts:43–49); agrega por unit e persiste units/<id>/proof-results.jsonl (proof.ts:75–88); e se QUALQUER prova falhou, retorna err com o resumo — a run falha fechada (proof.ts:90–96).
Nem tudo deve rodar sozinho. No swarm, classifyPark (packages/swarm/src/park.ts:38) inspeciona cada task antes da execução; o que cair na regra — tier T4 ou marcador legal/segurança (park.ts:17) — é estacionado e journalado, nunca descartado (orchestrator.ts:245–260). O resgate é um ciclo de comandos com trilha de auditoria: alembic propose <run-id> reabre os parkeados como proposta; alembic approve --task-id registra a decisão em approvals.jsonl no run-dir; alembic reject registra em rejections.jsonl. A decisão humana também é um registro append-only.
Determinismo paga dividendos aqui: a chave de cache de h.agent()/h.swarm() é sha256(JSON estável de {prompt, opts}) (computeCacheKey, packages/vm/src/cache.ts:20–21), guardada em <runDir>/workflows/<wf-id>/cache.json (cache.ts:23–24). Mesmo prompt + mesmas opts = mesmo resultado sem nova chamada; --no-cache pula leitura e escrita. E como TUDO relevante vive em events.jsonl + cache, alembic replay <run-id> re-executa/retoma a run a partir do disco; --resume valida goal/plan/contrato contra o meta.json antes de reutilizar o run-dir.
Antes de importar seu alembic.plan.ts, runPlan (packages/vm/src/run-plan.ts:53–65) lê o FONTE e roda o cheque de determinismo: para .ts, um sweep por regex (checkDeterminismTs, run-plan.ts:16–28) proíbe Date.now(), new Date() e Math.random(); para JS, o checkDeterminism de @alembic/mission parseia com Acorn. Reprovou → a run morre com "plan module … is non-deterministic" antes de qualquer efeito.
.ts é regex sobre o texto — pega Date.now() até dentro de comentário ou string. O doc assume o trade-off: "a false positive is safer than allowing non-determinism into a resumable plan" (run-plan.ts:12–14). Precisa de tempo ou aleatório? Receba um clock/seed injetado pelos hooks — nunca do ambiente.O flag --coordinated de alembic run liga um irmão do Validator: runCoordinatedValidatorGate (packages/coda/src/coordinated-validator.ts:476). Primeiro assessRiskTier (:167) classifica a unit em trivial | lite | full (riskTierSchema, :53); o tier escolhe as lentes (LENSES_BY_TIER, :140) que rodam sobre o mesmo artefato, além dos cheques determinísticos (runDeterministicChecks, :242); os achados deduplicados (dedupeFindings, :195) viram nota PERFECT | VERIFIED | PARTIAL | FAILED (:61) e daí um veredito pass | needs-review | fail (:57), gravado em units/<id>/coordinated-verdict.json. O ponto de arquitetura: é aditivo — nunca muda a decisão do council e nunca falha a run (offline por default; sem adapter, roda só o piso determinístico). Observabilidade extra, zero risco novo.
alembic forge "<prompt>" roda o front-end de 7 passos registrados em packages/forge/src/front-end/index.ts:22–28: grill (afia escopo + critérios de aceite em SCOPE.md) → research (contexto/restrições) → prototype (decisão registrada de prototipar ou não) → prd → issues → goal (GOAL.md com done-when) → review. O resultado materializa um diretório de escopo com GOAL.md + alembic.plan.ts + contrato — exatamente o trio que o Scope Gate copia para dentro da run e que alembic run --goal GOAL.md --plan alembic.plan.ts --yes executa.
unit.proof[] sai com exit 1. O que o Proof Gate faz?proof.ts:90–96 — qualquer prova sem status done entra em failed e o gate retorna err('Proof Gate failed: unit=… command=…'). Antes disso ele SEMPRE persiste units/<id>/proof-results.jsonl (a evidência fica, mesmo na falha). Não há retry no gate: prova é determinística ou não é prova.alembic.plan.ts tem Date.now() — mas só dentro de um comentário. O que o runPlan faz?.ts o cheque é checkDeterminismTs — regex sobre o texto-fonte (run-plan.ts:16–28), que "may flag occurrences inside comments/strings, but a false positive is safer" (run-plan.ts:12–14). O caminho Acorn/AST existe, mas para módulos JS via checkDeterminism de @alembic/mission. Apague o comentário e siga.O ciclo completo de uma run com gates (o --coordinated é aditivo e offline):
# rodar um escopo Forge (Scope → … → Publish) alembic run --goal GOAL.md --plan alembic.plan.ts --yes alembic run --goal GOAL.md --plan alembic.plan.ts --coordinated --yes # o ciclo T4: reabrir, aprovar, rejeitar (tudo vira journal) alembic propose <run-id> alembic approve <run-id> --task-id <unit-id> alembic reject <run-id> --task-id <unit-id> # retomar/replayar do disco (events.jsonl + cache SHA-256) alembic replay <run-id> alembic run --goal GOAL.md --plan alembic.plan.ts --resume <run-id> --no-cache --yes # ver a recusa de não-determinismo na fonte sed -n '16,28p' packages/vm/src/run-plan.ts # evidências de uma run cat <runDir>/units/<unit-id>/proof-results.jsonl cat <runDir>/units/<unit-id>/coordinated-verdict.json
Regra de leitura: prova persiste MESMO quando falha — o run-dir é o dossiê completo, não só o troféu.
proof.ts:22–24).approvals.jsonl).sha256({prompt, opts}) — determinismo compra replay de graça (cache.ts:20–21).Date.now/new Date()/Math.random ANTES de executar (run-plan.ts:16–28).--coordinated observa e registra; nunca veta — aditivo por construção.