O @alembic/planf3 é a "Internalized Planf3 planning meta-skill" (descrição do próprio package.json): a disciplina de transformar UM prompt num plano HTML autocontido — purpose, problem, solution, fases com checklists, comandos de validação — e daí derivar GOAL.md, o contrato e um alembic.plan.ts inicial. alembic plan "<prompt>" é a porta; o fallback determinístico garante que SEMPRE sai um plano usável, com ou sem modelo. Nesta lição: o fluxo completo, os extratores que fecham a ponte com o forge, e as 7 imagens de teaching que documentam a filosofia do pacote.
createPlan(userPrompt, options) (packages/planf3/src/index.ts:244–269) materializa plan.html em <outputDir>/<slug>/ (default .alembic/plans/). A decisão central está em generatePlanWithModel (packages/planf3/src/model.ts:69–108): offline ou sem adapters ⇒ undefined — e o chamador cai no template determinístico (defaultPlan, index.ts:148–233); online, roteia para o T2 mais barato via pickAdapter(Tier.T2) (ou o modelo pinado por --model via adapterForModel), pede um JSON no schema planContentSchema (types.ts:15–24: title, purpose, problem, solution, phases[] com min 1, validationCommands, questionables, notes) e valida na borda. O retorno carrega a etiqueta: source: 'llm' | 'fallback'.
// trecho real, não editado if (options.offline || options.adapters === undefined) { return undefined; }
A robustez online também é defensiva: o texto do modelo passa por extractJsonObject (model.ts:17–35), que varre do último } de trás para frente até o { balanceado — "models wrap JSON in prose or fences, so we scan back… rather than trusting the whole reply" — e o resultado ainda precisa passar no planContentSchema.safeParse. Modelo que devolve lixo vira err tipado, e o comando cai no fallback (index.ts:259–265): o plano SEMPRE existe.
Pense como… um arquiteto com prancheta elétrica e régua-T: se a energia cair (offline), o desenho sai na régua-T — mais padronizado, menos personalizado, mas com TODAS as vistas obrigatórias. Onde quebra: um arquiteto cansado pula seções; o template não pula nunca — as 5 fases (Scope, Design, Build, Proof, Ship) e os 3 comandos de validação (pnpm -r typecheck, pnpm -r build, pnpm -w test) estão sempre lá (index.ts:170–223).
O comando alembic plan "<prompt>" (runPlanCommand, apps/cli/src/commands.ts:2586–2647) não para no HTML. Depois do createPlan, ele chama loadPlanHtml (packages/forge/src/plan-loader.ts:21–44) para EXTRAIR os artefatos do próprio HTML e escreve, lado a lado no plan-dir: GOAL.md (o goal extraído), validation-contract.md ({ version:'1', assertions } — com fallback ['GOAL.md is concrete','tests pass','publish gate clears'] se o plano não listou validação, commands.ts:2618–2623) e alembic.plan.ts (o starter com fases scope/build/proof). Ou seja: a saída de alembic plan é EXATAMENTE a entrada de alembic run --goal … --plan … — e do Scope Gate da lição 11.
extractGoalFromPlanHtml pega o <h1> (ou o primeiro parágrafo do purpose) e extractValidationFromPlanHtml varre os <li> da seção de validação (index.ts:276–304). O forge as consome em loadPlanHtml/compilePlanToModule (forge/src/plan-loader.ts:21–73) — um plano .html entregue ao runner vira um alembic.plan.ts irmão, gerado do próprio HTML. É por isso que ScopeInput.planPath aceita ".ts, .js, or planf3 .html" (forge/src/types.ts:15).O HTML gerado tem seções fixas — purpose, problem, solution, phases (cada fase com goal + checklist), validation (com o lembrete "🔁 The plan is not complete until every command passes", index.ts:135), questionables (opt-in via --questionable) e notes (index.ts:112–143). Essa anatomia não é invenção do pacote: é a skill Planf3 original internalizada — o diretório packages/planf3/skill/ (exportado como skillDir, index.ts:15) carrega o SKILL.md e 5 workflows: create-plan.md, build-plan.md, update-plan.md, update-references.md e image-generation.md.
Duas funções exportadas lançam erro honesto em vez de fingir: updateReferences — "not implemented. No reference index is maintained by planf3 yet" (index.ts:355–359) — e generateImages — "not implemented. Add images manually or use an image-generation skill" (index.ts:367–374). Já updatePlan FUNCIONA: regenera o plano a partir de um change-prompt e sobrescreve o arquivo no mesmo diretório, preservando os irmãos GOAL/contrato (index.ts:333–348).
O pacote carrega seu próprio material didático: 7 imagens em packages/planf3/images/, referenciadas pelo README.md do pacote (linhas 11, 58, 64, 93, 115, 135 e 147). Elas documentam a FILOSOFIA da meta-skill — consulte-as no repositório (esta lição as cita por nome, de propósito, para você abrir as originais):
| Arquivo (packages/planf3/images/) | O que ensina (alt do README) |
|---|---|
meta-skill-v5-multiplier-grid.png | Uma meta-skill se multiplica em incontáveis planos |
02_outsourced_planning.png | Entregar o plano a uma caixa-preta é entregar o resultado |
03_templated_engineering.png | Um template carimba a mesma forma estruturada toda vez |
04_plan_anatomy.png | A anatomia do plano — metadados, purpose, fases com checklists, loop de validação, notas |
05_five_workflows.png | Um prompt roteado a um de cinco workflows dedicados |
06_priorities.png | Performance > velocidade > custo — gaste tokens para vencer |
07_trifecta.png | Um plano, três leitores: você, seu time e seus agentes |
--online, o que alembic plan "prompt" produz?generatePlanWithModel devolve undefined offline (model.ts:74–77) e createPlan cai no defaultPlan (index.ts:259–265) — Scope, Design, Build, Proof, Ship + pnpm -r typecheck / build / -w test, sempre presentes. O plano existe SEM rede e SEM gasto; o modelo só melhora o conteúdo, nunca é pré-requisito.planf3.generateImages(planPath) — o que acontece ao chamar?index.ts:367–374 lança "planf3 generateImages is not implemented. Add images manually or use an image-generation skill." As 7 imagens de packages/planf3/images/ são diagramas ESTÁTICOS do README (material de teaching), não saída do código — confundir os dois seria exatamente o tipo de fabricação que o pacote recusa declarando o não-implementado em voz alta.Gerar um plano offline, inspecionar os 4 artefatos e abrir o material da skill:
# 1) plano offline ($0, determinístico; --online usa o T2 mais barato) alembic plan "adicionar exportação CSV ao cockpit" alembic plan "adicionar exportação CSV ao cockpit" --questionable # 2) os quatro artefatos lado a lado ls .alembic/plans/<slug>/ open .alembic/plans/<slug>/plan.html cat .alembic/plans/<slug>/GOAL.md cat .alembic/plans/<slug>/validation-contract.md # 3) fechar o circuito: o plano vira run (Scope Gate, lição 11) alembic run --goal .alembic/plans/<slug>/GOAL.md \ --plan .alembic/plans/<slug>/alembic.plan.ts --yes # 4) a skill internalizada e as 7 imagens de teaching ls packages/planf3/skill/workflows/ ls packages/planf3/images/ grep -n 'images/' packages/planf3/README.md # fontes desta lição sed -n '244,269p' packages/planf3/src/index.ts # createPlan sed -n '69,108p' packages/planf3/src/model.ts # generatePlanWithModel sed -n '2586,2647p' apps/cli/src/commands.ts # runPlanCommand
Regra de leitura: o plano é um artefato para TRÊS leitores (você, o time, os agentes) — se um dos três não consegue agir a partir dele, a seção correspondente está fraca.
undefined ⇒ fallback: o plano SEMPRE existe, com etiqueta source honesta (model.ts:74–77, index.ts:256–265).alembic plan escreve 4 artefatos; a saída é a entrada exata do alembic run (commands.ts:2625–2633).index.ts:276–304).updateReferences/generateImages lançam erro honesto (index.ts:355–374).packages/planf3/images/ são o manifesto visual da meta-skill — abra-as no repo.