$0O funil é como o Alembic transforma um corpus de fontes brutas em sinais de negócio validados e learnings — a um custo que começa em exatamente $0 e só sobe à medida que candidatos provam valer o gasto. É uma cascata de quatro camadas: T0 determinística (grátis) → T1 local (~grátis) → T2 shortlist de fronteira medida → T3 council + painel verificador. Só um GO verificado emite. É o motor de dados do qual o ciclo de aprendizado se alimenta. Fonte: packages/harness/src/funnel.ts.
A camada mais barata toca 100% do corpus; cada camada seguinte é mais cara mas vê apenas os sobreviventes da anterior. A curva de custo verga na direção certa:
| Camada | O que faz | Custo |
|---|---|---|
| T0 | runT0Pipeline: walk → dedupe SHA-256 → valida contrato → score 6-dim → emite resíduo, sobre 100% do corpus (excluindo Repos/Models + Repos/Prompts) | $0 |
| T1 | runT1Extraction: um BusinessSignal por item de resíduo via o adapter LOCAL injetado; free-tier, então na prática nunca bloqueado por orçamento | ~$0 |
| T2 | runT2Shortlist: uma shortlist FRONTIER com portão de orçamento refina os sinais T1 mais fortes em lotes; toda chamada paga é medida | medido |
| T3 | runT3Council: um council sintético de 3 membros (otimista/analista/pessimista, atende MIN_VALID_AGENTS=3) + o painel verificador N-lentes | medido |
Um resultado T3 só emite quando ambos: a decisão de consenso é GO e o painel N-lentes aprovou a emissão (verificado, não estacionado). Um GO puro não basta:
// packages/harness/src/funnel.ts:496-514 (condensado) — o portão de emissão const verified = consensus.decision === 'GO' && isPanelEmissionApproved(report); // painel N-lentes: verificado, NÃO estacionado if (verified) { // emite arestas de oportunidade + learnings; expõe em verifiedSignals (PII-safe) }
Os BusinessSignal[] GO-verificados são expostos em FunnelReport.verifiedSignals — a ponte PII-safe para a marketing factory (distillAndMarket). A lição 18 cobre o painel; o ponto aqui é que o funil exige consenso e verificação independente antes de gastar esforço a jusante ou emitir qualquer coisa para fora.
Um sinal de um canal PRIVADO (whatsapp, discord, skool, circle) é redigido antes da chamada de modelo (extractionInput redige) e protegido de novo por assertRedactedForEmit antes de qualquer escrita (emitSafeSignal). Um sinal de canal privado não-redigido é descartado, nunca emitido. FunnelReport.t1PiiBlocked é um alarme não-zero. Governado pela ADR-0011.
Toda chamada paga (T2/T3) é envolvida por um BudgetGuard.check fail-closed antes do despacho; uma violação projetada bloqueia a chamada e a camada degrada em vez de estourar o gasto. A precificação sempre usa a tarifa de tier do registry (pricingModelId), nunca um override de catálogo — então um modelo de gateway sobrescrito que não está no registry ainda é medido contra o teto pela tarifa do seu tier. Você não consegue contornar o orçamento por acidente.
Resultados fluem para os dois stores via escritas append-only, content-addressed, validadas por schema; leituras da fonte permanecem read-only. As duas saídas são o grafo de oportunidades BUSINESS (Business/opportunity-graph.jsonl) e o store de LEARNINGS (Skills/learning/learnings.jsonl) — as duas cadeias de valor da ADR-0002.
etl forçaria o etl a depender para cima de adapters/council, invertendo o grafo de camadas. Então a T0 determinística fica em @alembic/etl (puro, só-contracts) e o orquestrador que chama T0→T3 vive em @alembic/harness. A estratificação é preservada por onde o código fica, não por convenção.GO mas o painel N-lentes estacionou uma lente. O funil emite o sinal?verifiedSignals.$0 — as camadas locais são o piso do design, não um fallback. alembic distill <corpus> --offline roda o pipeline inteiro com um registry de adapters offline e nunca toca uma API paga.pricingModelId), então até um modelo de gateway sobrescrito no catálogo é medido pelo seu tier. O budget guard vê o custo projetado independentemente de qual nome de modelo concreto foi fixado.