A disciplina que dirigiu TODA a fusão — learn → analyze → execute UMA → verify → decide, com Proof Gate e done-when mensurável — não mora só na cabeça do operador: ela está internalizada no monorepo como um pacote com duas metades. skill/ embarca a doutrina inteira em markdown; src/ implementa um runtime mínimo tipado: escopo com doneWhen obrigatório, log append-only e um gate que só libera com prova.
packages/loop-engineering/src/index.ts quase não tem lógica — e isso é a tese. Ele exporta CAMINHOS: skillDir (index.ts:9) aponta para a árvore skill/ embarcada, e dali saem loopEngineeringSkillPath (o SKILL.md, index.ts:14), os dois scripts do Publish Gate (publish-course-gist.js em index.ts:19 e publish-course-pages.js) e os módulos da doutrina: forge/, fusion/, council/, visual-teach/, ultragoal/, brightdata-cli/, computer-use-cli/, harness-brief/, além de reference.md, USE-CASES.md, grill.md e setup.sh. O package.json reexporta tudo como "./skill/*" — qualquer agente instala o pacote e recebe a disciplina INTEIRA, legível, junto com o código.
Duas honestidades de mapa: (1) o Course Gate NÃO mora mais aqui — index.ts:42 reexporta generateCourse de @alembic/coda ("moved… re-export for compatibility"); (2) este pacote depende só de @alembic/coda, @alembic/contracts e @alembic/tui, e o package.json não declara script test — os 8 testes de runtime.test.ts rodam pelo glob do workspace raiz (guarde isso: é uma das gotchas da lição 29).
Pense como… um manual de voo encadernado junto com o checklist plastificado da cabine: o manual (skill/) explica o porquê; o checklist (src/) só deixa decolar se cada item for riscado. Onde quebra: checklist de avião não escreve diário — aqui cada passo vira linha permanente no LOOP-LOG.md.
A primeira regra da disciplina — "capture o done-when ANTES de executar" — é código: scopeSchema (runtime.ts:25–52) exige goal string não-vazia e doneWhen array não-vazio; sem eles, err. readScope (runtime.ts:57–75) aceita duas formas: um .md com bloco cercado ```json sob o heading ## Scope (o formato que writeScope materializa), ou um .json puro. Um markdown SEM o bloco JSON é recusado com err('no JSON scope block found…') — prosa bonita não é escopo.
const scopeSchema = (raw: unknown): Result<LoopScope, Error> => tryCatch(() => { if (raw === null || typeof raw !== 'object') { throw new Error('scope must be an object'); } // … const goal = obj.goal; if (typeof goal !== 'string' || goal.length === 0) { throw new Error('scope.goal is required and must be a non-empty string'); } const doneWhen = obj.doneWhen; if (!Array.isArray(doneWhen) || doneWhen.length === 0) { throw new Error('scope.doneWhen is required and must be a non-empty array'); } // … context/constraints/editableSurface/agents opcionais });
evaluateScopeGate(scope, proofs) (runtime.ts:131–138) cruza cada item do doneWhen com a lista de provas e devolve { passed, missing }. Só passa o item cuja prova é literalmente true; o resto aparece NOMEADO em missing. "Deve funcionar" não tem representação neste tipo.runLoopEngineering (runtime.ts:178–238) é o control plane: lê o escopo, e para CADA LoopUnit grava uma entrada execute no log, chama o executor INJETADO, verifica com o verifier (ou o próprio executor como fallback — verified = succeeded && verify(unit)), empilha a prova, grava a entrada verify (converged ou iterate) e segue. No fim, evaluateScopeGate decide a entrada final improve: converged ("scope gate cleared") ou blocked com os itens faltantes por nome. O log é escrito com flag: 'a' (appendLoopLog, runtime.ts:116–123) — append-only: a história dos passos não se reescreve.
Repare na honestidade do vocabulário: o tipo LoopLogEntry.phase (runtime.ts:101) admite learn | analyze | execute | verify | improve — mas o runtime só GRAVA execute, verify e improve. As fases learn/analyze são do agente que dirige (ele pode registrá-las); o pacote não finge pensar por você. E o executor ser injetado é a separação builder/validador da casa: quem executa a unidade e quem a verifica podem ser processos diferentes.
learn e analyze existem no tipo para o AGENTE registrar as suas fases; o pacote só grava o que consegue provar sozinho — execute, verify e improve..md caprichado descreve o objetivo em prosa, sem bloco ```json sob ## Scope. O que readScope faz?readScope (runtime.ts:57–75) só reconhece o bloco cercado ```json (ou um arquivo JSON puro) e falha fechado sem ele. E mesmo com JSON, scopeSchema ainda exige goal não-vazio e doneWhen não-vazio — o done-when mensurável é pré-condição, não enfeite.verifier injetado devolve false. Qual é o efeito no relatório final?verified = succeeded && verify(unit) — a prova empilhada é false, unitsPassed não incrementa, e evaluateScopeGate lista o item em missing, produzindo a entrada final improve · blocked: <item>. Não há retry escondido: iterar é decisão do agente que dirige, com o log como memória.Verifique a lição inteira com leitura pura ($0):
# as duas metades: doutrina embarcada + runtime sed -n '1,45p' packages/loop-engineering/src/index.ts ls packages/loop-engineering/skill/ | head -20 # o contrato do escopo e o Proof Gate sed -n '25,75p' packages/loop-engineering/src/runtime.ts sed -n '131,138p' packages/loop-engineering/src/runtime.ts # o loop por unidade e a entrada final improve sed -n '178,241p' packages/loop-engineering/src/runtime.ts # o append-only literal (flag 'a') e a doutrina exportada como pacote grep -n "flag: 'a'" packages/loop-engineering/src/runtime.ts grep -n '"./skill/' packages/loop-engineering/package.json | head -5 # os 8 testes reais (e onde eles rodam: glob do raiz, não script local) grep -n "it(" packages/loop-engineering/src/runtime.test.ts grep -n '"test"' packages/loop-engineering/package.json # (vazio — ponte p/ lição 29)
skill/ (doutrina legível, exportada como ./skill/*) e src/ (runtime que a cobra).goal + doneWhen[] obrigatórios em scopeSchema — sem eles, err.evaluateScopeGate: item sem prova true sai NOMEADO em missing; o gate final é converged ou blocked.flag:'a'): a história dos passos é evidência, não rascunho.executor injetado. Próxima lição: a superfície onde TUDO isso vira comando de terminal — apps/cli, seus 33 comandos e a doutrina dry-run.