Lição 11 · Fusão v3 · Forge: do prompt cru ao escopo executável ← índice
Alembic × Hermes — O Curso de Fusão v3 · Execução e planejamento

Forge: do prompt cru ao escopo executável

O @alembic/forge é a ponte entre uma frase vaga e uma run verificável. Um front-end de 7 passos (grill → research → prototype → prd → issues → goal → review) materializa um diretório de escopo com GOAL.md, validation-contract.md e alembic.plan.ts — e o Scope Gate (loadScope) copia exatamente esse trio para DENTRO do run-dir, para que toda run carregue o próprio escopo consigo. Nesta lição: os 7 passos no código real, a materialização do run-dir e o caminho pelo qual um escopo vira run.

1

Os 7 passos, registrados no código

O pipeline canônico não é convenção de doc — é um array. DEFAULT_FORGE_STEPS (packages/forge/src/front-end/index.ts:21–29) registra os 7 passos na ordem, e runForgeFrontEnd (index.ts:42–55) os executa sequencialmente, parando no primeiro erro ("the first failure short-circuits the pipeline"). Cada passo é uma função pura sobre o contexto (ForgeStep, front-end/types.ts:53–56) que escreve seu artefato em scopeDir — o orquestrador nunca muta o contexto.

produto · 0007-factory-forge-cursos · s1
packages/forge/src/front-end/index.ts:21–29
// trecho real, não editado
export const DEFAULT_FORGE_STEPS: readonly ForgeStepRegistry[] = [
  { name: 'grill', run: grillStep },
  { name: 'research', run: researchStep },
  { name: 'prototype', run: prototypeStep },
  { name: 'prd', run: prdStep },
  { name: 'issues', run: issuesStep },
  { name: 'goal', run: goalStep },
  { name: 'review', run: reviewStep },
];
#PassoArtefatoFonte (steps.ts)
1grillSCOPE.md — escopo afiado + critérios de aceitegrillStep :29–51
2researchRESEARCH.md — contexto e restriçõesresearchStep :55–73
3prototypePROTOTYPE.md — decisão consciente de prototipar (ou não)prototypeStep :77–95
4prdPRD.md — problema, solução, fases, validaçãoprdStep :99–121
5issuesissues.jsonl — uma issue por assertion do contratoissuesStep :123–140
6goalGOAL.md + validation-contract.md + alembic.plan.tsgoalStep :142–165
7reviewREVIEW.md — checklist de prontidão + próximo comandoreviewStep :195–213

Pense como… um cartório de obra: ninguém levanta parede sem a pasta carimbada — projeto, memorial, ART, alvará — e cada carimbo depende do anterior. Onde quebra: no cartório os carimbos são burocracia sobre papel; aqui cada "carimbo" é um arquivo que o passo seguinte LÊ de verdade (o Scope Gate consome o trio do passo 6).

Duplo modo, mesmo pipeline. Cada passo LLM-capaz checa ctx.offline || !ctx.adapters (ex.: grillStep, steps.ts:33): offline usa o fallback determinístico; online chama runModelPrompt com um system prompt próprio por passo. O formato do artefato é o mesmo nos dois modos — o escopo nunca depende de rede para existir.
2

Scope Gate: a run carrega o próprio escopo

O primeiro dos 5 gates da run é o Scope Gate: loadScope (packages/forge/src/scope.ts:37). Ele lê e valida os insumos (goal não pode ser vazio, scope.ts:44–47; contrato é parseado por Zod, scope.ts:62–67) e então materializa o layout completo do run-dir via createNewScope (scope.ts:151–269): o runId é content-addressed — runIdFor({ goal, contract, planPath }) (scope.ts:158), um run-<sha256 dos 16 primeiros hex> sobre JSON canônico (packages/swarm/src/ids.ts:19–30) — e GOAL.md, contrato e plan module são copiados para dentro do diretório.

fusao · 17-the-gate-pipeline · s0
packages/forge/src/scope.ts:196–200 — o plan é copiado como alembic.plan<ext>
// trecho real, não editado
const planExt = extname(input.planPath);
const planDestName = `alembic.plan${planExt}`;
const planDest = fs.joinPath(runDir, planDestName);
const planCopyResult = await copyText(input.planPath, planDest);
if (!planCopyResult.ok) return err(planCopyResult.error);
scope dir (do forge) GOAL.md validation-contract.md alembic.plan.ts (+ SCOPE/RESEARCH/PRD…) runIdFor({goal, contract, planPath}) sha256 canônico → 16 hex <dataDir>/runs/run-<hash>/ GOAL.md ← cópia validation-contract.md alembic.plan.ts ← cópia meta.json · index.json tasks.json · run-state.json LOOP-LOG.md · review.md council/ units/ workflows/ park/ course/ reports/ a run é autossuficiente: escopo + journal no mesmo lugar mesmo trio ⇒ mesmo runId (re-submeter cai no run-dir existente, não cria árvore nova — ids.ts:6–10)
scope.ts: createNewScope copia o trio, escreve os metadados e cria os 6 subdiretórios (scope.ts:246–253). O id é o hash do escopo — identidade por conteúdo.

Além das cópias, o gate escreve meta.json (goal + contrato + planPath + runId, scope.ts:202–215), semeia LOOP-LOG.md e review.md (scope.ts:230–244) e cria os subdiretórios council/ units/ workflows/ park/ course/ reports/ (scope.ts:246–253) — as gavetas que os gates seguintes (Council, Proof, Validator, Publish) vão preencher.

3

Resume: mesmo escopo ou nada

Com --resume <run-id>, loadScope desvia para loadExistingScope (scope.ts:77–148): reutiliza o run-dir, mas ANTES compara goal, planPath e contrato contra o meta.json gravado na criação. Qualquer divergência é um err imediato — "resume mismatch: GOAL.md differs from original run" (scope.ts:106–108), idem para plan (:109–111) e contrato (:112–114). Um resume não pode trocar de escopo por acidente; journals e cache existentes são preservados.

Fail-closed até no retorno O resume também exige que o plan module copiado ainda exista no run-dir (scope.ts:119–122) — se alguém apagou alembic.plan.ts de dentro da run, o gate recusa em vez de re-copiar silenciosamente. A fonte de verdade do escopo de uma run retomada é o RUN-DIR, não o diretório original.
4

Como um escopo vira run, de ponta a ponta

O caminho completo tem dois comandos. alembic forge "<prompt>" roda o front-end via runForgeFrontEndCli (apps/cli/src/commands.ts:2469–2533): cria scopeDir = <dataDir>/forge/<slug> (:2480), chama ANTES o planf3 (createPlan, :2487–2490 — lição 15) para gerar o plano HTML, injeta os artefatos extraídos como planArtifacts e então executa os 7 passos. Depois, alembic run --goal GOAL.md --plan alembic.plan.ts --yes entrega o trio ao Scope Gate e a run nasce.

alembic forge "<prompt>" → runForgeFrontEnd (curto-circuito no 1º erro) 1 grillSCOPE.md 2 researchRESEARCH.md 3 prototypePROTOTYPE.md 4 prdPRD.md 5 issuesissues.jsonl 6 goalo TRIO nasce aqui 7 reviewREVIEW.md GOAL.md + validation-contract.md + alembic.plan.ts alembic run --goal … --plan … --yes → loadScope (Scope Gate) → VM um passo falhou ⇒ err e o pipeline PARA — nenhum artefato meia-boca vira escopo (front-end/index.ts:47–53) o passo 6 aceita um plan .ts existente ou gera o starter com fases scope/build/proof (steps.ts:151–174)
Sete carimbos em fila; o trio do passo 6 é o que o Scope Gate copia. O REVIEW.md do passo 7 já imprime o comando exato do próximo passo (steps.ts:200).
7
passos registrados em DEFAULT_FORGE_STEPS
6
subdiretórios criados pelo Scope Gate no run-dir
19
ADRs em docs/adr/ — decisões que escopos citam

Bônus do pacote: um lifecycle puro

O forge também abriga lifecycle.ts — a máquina Stage/Gate do founder-core reescrita como primitivo PURO: estágios ordenados, cada um guardado por checks numéricos (gte|lte|eq|gt|lt), e checkLifecycleGate devolvendo Result onde a fonte original lançava exceção (packages/forge/src/lifecycle.ts:5–24). Sem relógio, sem aleatório — plan-VM-safe por construção, o mesmo contrato de determinismo da lição 14.

5

Cheque seu modelo mental

Você roda --resume <run-id> depois de editar o GOAL.md original. O que acontece?
Correto: scope.ts:106–108 compara o goal lido com o meta.json da run e recusa a divergência — um resume nunca troca de escopo por acidente. O mesmo cheque cobre planPath (:109–111) e contrato (:112–114). Quer o goal novo? É outra run (e, como o id é content-addressed, será OUTRO run-dir).
Qual passo do forge materializa o trio GOAL.md + validation-contract.md + alembic.plan.ts?
Correto: goalStep (steps.ts:142–165) escreve os três de uma vez — o contrato como { version: '1', assertions } e o plan module lido do planf3 ou o starter determinístico com fases scope/build/proof. O grill só afia o escopo (SCOPE.md); o review só faz o checklist e imprime o próximo comando.
Camada técnica — comandos copy-paste

Do prompt cru à run, com inspeção de cada artefato no meio:

# 1) forjar o escopo (7 passos; offline usa fallback determinístico)
alembic forge "adicionar exportação CSV ao cockpit"

# 2) inspecionar o que cada passo escreveu
ls ~/.alembic/forge/<slug>/
cat ~/.alembic/forge/<slug>/GOAL.md
cat ~/.alembic/forge/<slug>/validation-contract.md

# 3) virar run: o Scope Gate copia o trio para o run-dir
alembic run --goal <scope>/GOAL.md --plan <scope>/alembic.plan.ts --yes

# 4) conferir a materialização (meta.json + 6 subdirs)
ls <dataDir>/runs/run-<hash>/
cat <dataDir>/runs/run-<hash>/meta.json

# 5) retomar SEM trocar de escopo (mismatch ⇒ err)
alembic run --goal <scope>/GOAL.md --plan <scope>/alembic.plan.ts --resume run-<hash> --yes

# fontes desta lição
sed -n '21,29p'  packages/forge/src/front-end/index.ts
sed -n '151,269p' packages/forge/src/scope.ts

Regra de leitura: o diretório de escopo é rascunho; o run-dir é o contrato. Depois do Scope Gate, é o RUN-DIR que responde "o que esta run prometeu?".

O que levar desta lição
Pergunta de acompanhamento sugerida: "e quando a unidade de trabalho é CÓDIGO que precisa rodar isolado, com agente, branch e sandbox próprios?" — Próxima lição: @alembic/factory — a software factory vendorizada de sandcastle (MIT), o loop plan → implement → review → merge e as 7 fases de factoryPhaseSchema.