Replaneja o Briefing de capacitação C210: saem capa/objetivo/trajetória e as recomendações genéricas (roteiro de 5 passos, justificativa científica, evitar, conferir, limites); as 4 folhas passam a ser todas do recorte — defesas (princípios + posições com fonte), essencial (fatos-âncora com fase + pedido literal e uma linha de plano), qa 1/2 e qa 2/2. - briefingContent: contrato novo (plan no lugar de script.steps; avoid/checklist removidos; shed qa → defesas) e âncora item-level por sourceUrl — o id de checklist repete e o ledger resolvia pelo primeiro item; par errado falha fechado (normalizeQa não repassava o sourceUrl, corrigido). - briefingRender: 4 folhas novas (defesas/essencial/qa/qa-2) e companion .md; CSS portado do design (principles-copy/request-block/request-plan). - build-dossie-solla-briefing: âncoras e comentários do fluxo novo. - Design hi-fi refeito (4 cenas do recorte), skill e agente autor atualizados. - Specs briefingContent/briefingRender reescritos; gate:fast verde (lint, typecheck, 389 arquivos / 4310 testes).
28 KiB
Impl: Briefing de capacitação Solla 1313 — até 4 páginas para quem vai pedir voto no recorte (cidade · instituição · tema)
Replanejado em 2026-09-23 (4 folhas 100% recorte; âncora item-level por
sourceUrl;planno lugar do roteiro; sem evitar/conferir/limites): verdocs/changelog/2026-09-23-c210-replan.md, a skill atualizada e o designdocs/plans/briefing-capacitacao-solla-ui-design.html. Este plano fica como histórico do desenho original (C210).
Status: aprovado Atualizado em: 2026-09-22 Issue: #1252 Intenção: docs/plans/briefing-capacitacao-solla.md Appetite restante: ~3–4 dias eng (herdado). Quota concentrada na redação com lastro (subagente autor), no port das 4 folhas do hi-fi e na integração das 3 skills irmãs. C209 é dependência soft (sem bloqueio); sem C209 a folha de defesas imprime a linha de lacuna.
Leitura da intenção
- Outcome: um briefing de capacitação por recorte (cidade · instituição · tema), PDF A4 de até 4 páginas + companion
.md, derivado dos artefatos do dossiê (sem segunda pesquisa factual), com "o essencial" (fatos-âncora com fonte), "o que Solla defende" (herdado do C209 quando houver), roteiro do pedido de voto ("vote 1313" + plano de voto + compromisso nomeado), perguntas prováveis × melhores respostas (direita e esquerda), o que evitar e o que conferir no dossiê; entregue como 3º entregável pelas três skills de dossiê e invocável sozinho por recorte. - O que NÃO negociar: rótulo literal
Insumo interno de capacitação — não publicarem todas as folhas; teto de 4 páginas rígido; sem CTA público/marca publicável/defeso; sem fonte, o item não entra (âncora obrigatória); sem cenário eleitoral,estimatedVotes, projeção ou qualquer dado staff-only; empenho ≠ pagamento (fase acompanha o valor); esfera explícita nunca somada; leitura relativa/local (nunca % estadual absoluto); PII mínima; artefato gitignored; nenhum schema/migration/DB write/rota/componente React; "nunca um segundo pipeline" de pesquisa ou render. - O que reavaliar (hipóteses da "Direção no codebase"):
- "PDF via
emitHtmlPairPdf" — o dono exige[data-page="resumo"]e[data-page="boletim"]e emite 2 PDFs; o briefing é documento único. Saída: aditiva no dono (emitHtmlSinglePdf),emitHtmlPairPdfintocado. - "o teto de 4 páginas não é expressável hoje" — passa a ser: esqueleto fixo de 4 folhas (
data-pageadicionadas no port) + contagem de folhas + fit por folha + shed determinístico por prioridade (precedenteonBulletinOverflow) + fail-closed. - "subagente" — confirmado: a redação é o passo pesado de contexto e vira um subagente autor por recorte, espelhando o redator do dossiê (o
narrative.jsontambém é escrito por subagente). O orquestrador da skill coordena, audita e roda o build. - O hi-fi usa Tailwind CDN e não tem
data-page; o port troca por CSS inline (padrão da família) e adiciona as âncoras, classe a classe no resto. - As 3 SKILLs de dossiê são reescritas pelo C209 na mesma janela; tocar apenas seções pontuais (3º entregável) e rebasar (C210 é P1).
- "PDF via
Abordagem recomendada
flowchart LR
A["/briefing-capacitacao-solla cidade:Ilheus, instituicao:UFBA, tema:Saude"] --> B["orquestrador: resolve os recortes<br/>isMunicipalitySlug / resolveInstitutionName / resolveSpeechTopic"]
B --> C["subagente autor (1 por recorte)<br/>lê research + voz solla-comunicacao"]
C --> D["slug.briefing.json<br/>Q&A/roteiro/evitar/conferir só com fato-âncora"]
D --> E["build-dossie-solla-briefing.mjs<br/>normaliza + resolve âncoras contra bulletinFacts"]
F["dossiê já pesquisado<br/>slug.a/b/c.research.json + snapshot"] --> E
E --> G["briefingContent: plano de impressão<br/>+ shed determinístico por prioridade"]
G --> H["briefingRender: 4 folhas data-page + companion .md"]
H --> I["emitHtmlSinglePdf<br/>maxPages=4 · fit por folha · fail-closed"]
I --> J["docs/research/dossie-solla-<recorte>/slug-YYYY-MM-DD-briefing.pdf/.md"]
K["skills dossie-solla-{cidade,instituicao,tema}<br/>dossiê + boletim + briefing na mesma invocação"] --> A
Opções consideradas (forma geral): (a) skill/command/build novos do briefing consumindo o engine do dossiê (buildDossierReport, dossieResearch, dossieUnit, buildPdf) | (b) um modo briefing dentro dos 3 builders existentes | (c) um segundo pipeline de pesquisa/render dedicado.
Recomendação: (a) — o engine já é unit-aware; o briefing é um consumidor do dossiê (ledger bulletinFacts), não um quarto recorte. Um dono por concern, custo de reverter baixo (scripts + prosa, nada em produção). A integração nas 3 skills cita a skill e chama o mesmo build.
Rejeitadas: (b) acopla o teto/emit de documento único ao caminho pinado dos 3 builders publicados (risco de regressão par a par) e multiplica o parse em 3 CLIs; (c) reabre a "sem segunda pesquisa factual" e twin-a as guardas de fonte/esfera/fase.
Decisões de engenharia (A–F)
A. Onde mora o pipeline do briefing
Opções: A1) builder novo unit-aware (scripts/build-dossie-solla-briefing.mjs) + scripts/lib/briefingContent.mjs (contrato/validação/plano de impressão) + scripts/lib/briefingRender.mjs (HTML/MD), reusando o engine; A2) modo --briefing nos três builders build-dossie-solla-{cidade,instituicao,tema}.mjs; A3) pipeline próprio de pesquisa/render do briefing.
Recomendação: A1 — script unit-driven (--unit=municipality|institution|theme, default municipality), offline (não busca Câmara/Portal/IBGE; deriva dos research.json + snapshot), consumindo buildDossierReport/bulletinFacts. O fluxo dos dossiês chama este mesmo script para o 3º entregável.
Rejeitadas: A2 — os builders já fazem rede e emitem par; um modo briefing ali duplica a plumbing de cache/rede em 3 CLIs e toca o caminho com obrigação de byte-parity; A3 — proibido pela intenção ("nunca um segundo pipeline") e twin das guardas de fonte.
B. Forma do conteúdo autoral e onde validar
Opções: B1) contrato novo <slug>.briefing.json validado em scripts/lib/briefingContent.mjs, com âncoras resolvidas contra report.bulletinFacts (o ledger "só fato com fonte" que já é dono no dossiê); B2) estender dossieResearch.mjs com o contrato do briefing; B3) orquestrador escreve o JSON lendo os research.json direto.
Recomendação: B1 — arquivo autoral único por recorte, escrito pelo subagente autor a partir dos itens com fonte (análogo ao narrative.json), validado fail-closed no build. Shape mínimo:
{
"unitId": "municipality", // municipality | institution | theme
"municipalitySlug": "ilheus", // chave = slugField da unidade (institutionSlug | themeSlug)
"generatedAt": "2026-09-22T12:00:00.000Z", // obrigatório
"subtitle": "Piemonte da Diamantina · consulta antes e durante o contato", // opcional
"lede": "≤380 chars — como usar o essencial",
"essential": [ // ≥3; cada item: factId XOR gapReason
{ "factId": "era_b_equipamentos", "title": "≤120", "note": "≤200" },
{ "gapReason": "sem fala própria localizada", "title": "…", "note": "…" }
],
"defenses": [ { "factId": "…", "title": "…", "note": "…" } ], // C209; [] = linha de lacuna
"script": { "steps": [ { "title": "≤90", "note": "≤220" } ] }, // ≥3 passos; o pedido é literal do renderer
"qa": [ // ≥4, com ≥1 "direita" e ≥1 "esquerda"
{ "side": "direita|esquerda|entrega", "question": "≤180",
"acknowledge": "≤200", "answer": "≤520", "close": "≤200",
"factId": "era_b_sesab" /* XOR */ "gapReason": "sem registro localizado" }
],
"avoid": [ { "title": "≤120", "note": "≤200" } ], // ≥3
"checklist": { "beforeAnswer": ["≤200"], "unsure": ["≤200"] } // ≥2 em cada
}
Regras de validação (todas throw → o builder die com o campo no erro): unitId casa com a unidade do build; slug + generatedAt presentes; cada item exige exatamente um de factId | gapReason; factId tem de resolver em bulletinFacts com sourceUrl (âncora, não texto novo); side no enum; Q&A cobre os dois lados; mínimos por lista. Deny-list recursiva de chaves (estimatedVotes, estimated, scenario, cenario, projecao, projection, staffOnly, internalVotes, polls) — o contrato não tem campo numérico; valor/fase vêm do fato-âncora no render. Caps de texto são warning (console.warn); quem falha fechado é o fit guard. Arquivo ausente → die com o ponteiro da skill (o briefing não tem fallback determinístico, ao contrário do narrative.json).
Rejeitadas: B2 — mistura dois ciclos de vida (pesquisa por era × derivado autoral de 1 arquivo) e exporia o contrato do briefing aos researcher subagents; B3 — obriga o orquestrador a reter o corpo dos research.json (contra a regra explícita da família) e serializa a redação no lote.
C. Teto rígido de ≤4 páginas
Opções: C1) esqueleto fixo de 4 folhas (data-page="essencial|defesas|qa|evitar") + contagem de folhas + fit por folha + shed determinístico por prioridade com resto declarado no PDF (.line-meta: e mais N no briefing completo (.md)) e completo no .md + fail-closed quando o mínimo não cabe; C2) fail-closed puro (sem shed); C3) teto mole com continuação (modelo dossiê).
Recomendação: C1 — o renderer emite sempre 4 folhas (sem continuação, por construção); o builder mede (measurePageOverflows dentro do emitHtmlSinglePdf) e, se uma folha estourar, aplica trimBriefing(content) — função pura e determinística em briefingContent.mjs que remove um item por vez na ordem: qa (até o mínimo 4) → defenses (até 0) → checklist (até 2+2) → avoid (até 3) — re-renderiza e mede até estabilizar (teto de tentativas compartilhado MAX_FIT_ATTEMPTS, precedente do boletim C190); o QA só perde um item cuja remoção preserva os dois lados. Essential, roteiro e identificação nunca são cortados; se nem os mínimos couberem, die apontando a lista a encurtar. O resto é declarado na folha (.line-meta, estilo já especificado no design) — nunca em silêncio. A guarda de páginas é a proteção real: .sheet{overflow:hidden} esconde corte, então contagem ≤4 + altura por folha + fail-closed são obrigatórios e testados.
Rejeitadas: C2 — transforma cada build em tentativa/erro e ignora o precedente C190 (onBulletinOverflow); C3 — o produto decidiu rígido no gate; teto mole destrói o valor ("estudar/consultar em ≤4 páginas").
D. PDF de documento único
Opções: D1) aditivo no dono: exportar emitHtmlSinglePdf(browser, { html, pdf, maxPages = 4, requiredAnchors, onOverflow = null }) em scripts/lib/buildPdf.mjs, reusando os helpers privados (openPrintPage, measurePageOverflows, assertPageFits, printA4Pdf) e mantendo emitHtmlPairPdf intocado; D2) generalizar emitHtmlPairPdf para documents: [] (1..N); D3) exportar printA4Pdf e montar a guarda no builder do briefing.
Recomendação: D1 — superfície mínima, um só dono do mm→px/emit, zero mudança no caminho dos 3 builders publicados. O emit único: exige todas as requiredAnchors, conta [data-page] (guarda pura assertPageCount ≤ maxPages), mede overflows, chama onOverflow (rebuild) até estabilizar, e imprime. O shed fica no builder (que tem o conteúdo), não no emitter.
Rejeitadas: D2 — mexe no contrato emitido dos 3 builders por um ganho que não existe hoje (generalização especulativa); D3 — twin do loop/guarda fora do dono.
E. Companion .md
Opções: E1) renderBriefingMd(content, { facts, unit, report }) derivado do mesmo conteúdo normalizado (+ ledger), carregando tudo (inclusive o que o PDF cortou), com [fonte](url) por fato-âncora; E2) derivar do HTML; E3) só PDF.
Recomendação: E1 — título + recorte + rótulo literal + seções espelhando as 4 folhas + roteiro com o literal "vote 1313" + Q&A em prosa com link de fonte + evitar/conferir + "Limites e defeso" + data de geração. O .md é a versão de referência (o PDF é a de campo).
Rejeitadas: E2 — parse de HTML é frágil, perde links/fonte; E3 — decisão do gate ("companion sim").
F. Integração nas 3 skills + command/registry
Opções: F1) uma skill/command briefing-capacitacao-solla, com seletor literal de recorte (cidade:<token> · instituicao:<token> · tema:<token>, lote por vírgula reusando os resolvedores das irmãs); as 3 SKILLs de dossiê citam a skill e ganham o 3º entregável; F2) três skills gêmeas (uma por recorte); F3) embutir o briefing nos fluxos existentes sem skill própria.
Recomendação: F1 — a skill é a fonte canônica do fluxo (parse → autor → auditoria → build → summary); as SKILLs irmãs só citam e listam o 3º entregável. Mudam nos 3 SKILLs: description:, "Quando usar", "Lote" (uma entrada = dossiê + boletim + briefing), saídas do passo 6 (-briefing.pdf + -briefing.md), summary do passo 7, guardrails do briefing (rótulo, ≤4 páginas, insumo interno, sem staff-only, sem segunda pesquisa) e Referências (skill + builder). Atualizar a description: dos 3 commands (texto de TUI).
Rejeitadas: F2 — triplica prosa para o mesmo pipeline (drift garantido); F3 — a intenção decidiu skill única invocável sozinha.
Componentes / mudanças
Skill / command / subagente (agêntico)
.agents/skills/briefing-capacitacao-solla/SKILL.md(novo) — fonte canônica. Seções:Quando usar,Lote (recortes),Pipeline (etapas),Recibo do autor,Contrato do briefing.json,Conteúdo (4 folhas),Guardrails,Troubleshooting,Referências. Cita o hi-fi aprovado como fonte de verdade do port e a voz (solla-comunicacao)..opencode/commands/briefing-capacitacao-solla.md(novo) — frontmatter sódescription:(semmodel:); corpo cita a skill pelo nome exato,$ARGUMENTSe.agents/skills/briefing-capacitacao-solla/SKILL.md..opencode/agent/briefing-capacitacao-solla.md(novo) —mode: subagent, sem pin openai: lê osresearch.json/narrative.jsondo recorte + a voz, escreve<researchDir>/<slug>.briefing.jsone devolve só o recibo. Proibido: pesquisa web nova, editar research, rodar build/ssh, commitar..agents/skills/dossie-solla-{cidade,instituicao,tema}/SKILL.md(editar, pontual) — 3º entregável conforme F1..opencode/commands/dossie-solla-{cidade,instituicao,tema}.md(editar,description:+ 1 frase) — citar o briefing.
Build (scripts, sem app)
scripts/build-dossie-solla-briefing.mjs(novo) — unit-aware, offline: lê snapshot +<slug>.{a,b,c}.research.json(mesmos asserts/ENOENT→lacuna dos irmãos), monta o report combuildDossierReport({ snapshot, research, generatedAt, unit })(sememendas/camara/health), lê o<slug>.briefing.json, normaliza/valida, renderiza, mede/ajusta o plano de impressão e emite 1 PDF +.md. Flags:--unit,--snapshot,--research-dir,--out-dir,--generated-at. Saídas:<outDir>/<slug>-<YYYY-MM-DD>-briefing.pdf/.mde cache<researchDir>/<baseName>.briefing.html(dirs já gitignored).scripts/lib/briefingContent.mjs(novo) —normalizeBriefingContent(raw, { unit, facts })(fail-closed + warnings),briefingAnchorFact(facts, factId),trimBriefing(content)(ordem/mínimos do C; o QA preserva os dois lados),briefingSubjectName(report, content)(nome do recorte, embriefingRender.mjs). Puro.scripts/lib/briefingRender.mjs(novo) —renderBriefingHtml(content, { unit, report })(4<article class="sheet" data-page="…">, literais de renderer: rótulo interno, "Como usar", "Por que assim", "Régua visível", linha do pedido comvote 1313, limites/defeso, rodapésfolha N/4; CSS inline portado classe a classe do hi-fi, sem Tailwind) erenderBriefingMd(content, { unit, report }). ReusahtmlEscape/htmlWithLinks(reportText.mjs).scripts/lib/buildPdf.mjs(editar, aditivo) —emitHtmlSinglePdf+ guarda puraassertPageCount;emitHtmlPairPdfe demais exports intocados.scripts/lib/dossieUnit.mjs(editar, aditivo) —snapshotField: 'municipality'emMUNICIPALITY_UNIT(hoje sósubjectlê o campo);briefingNoun(cidade|instituição|tema) eoutDirnos 3 descritores; nenhum leitor existente muda e os 3 builders não são refatorados.
Testes (unit, tests/unit/)
briefingContent.unit.spec.ts,briefingRender.unit.spec.ts,briefingSkill.unit.spec.ts(novos);buildPdf.unit.spec.ts(aditivo: export +assertPageCount);opencodeCommands.unit.spec.ts(adicionar'briefing-capacitacao-solla'ao array);dossieBatchSkill.unit.spec.ts+dossieInstitutionBatchSkill.unit.spec.ts+dossieThemeBatchSkill.unit.spec.ts(aditivo: 3º entregável).- Migration: sem migration (nenhum schema/DB).
- Access / Consent: n/a (nenhuma collection/gravação; PII mínima por contrato).
- UI: Impeccable C; o hi-fi
docs/plans/briefing-capacitacao-solla-ui-design.htmljá passou o gate (design aprovado) — implementação é port classe a classe para CSS inline, sem shell React (o "UI" é o HTML de impressão). O resto declarado do shed reusa.line-meta(estilo já especificado) — non-trigger; qualquer outra superfície ausente aciona odesigner.
Dados → forma (pergunta 3)
- Forma escolhida: listas de linhas curtas com fonte por âncora (essencial/defesas), um bloco literal de pedido em destaque (
.request-linecom "vote 1313"), Q&A em duas colunas (pergunta | reconhecer→fato→fechar) e listas de evitar/conferir; nenhum gráfico, placar ou tabela numérica. É a forma mais pobre que ainda desbloqueia a decisão da persona (o que citar, como responder, o que evitar), cabe em 4 folhas e não convida a somar esfera nem a expor cenário. - Rejeitadas: dashboard/KPIs (perde o lastro item a item e expõe leitura absoluta); tabela de números (briefing é fala, não planilha; estoura o teto); prosa longa (não se consulta em campo); qualquer forma com número sem fase/fonte (anti-goal).
Fases verificáveis
- Tracer — contrato + conteúdo + render, sem PDF (meio dia).
briefingContent.mjs+briefingRender.mjs+ specs com fixture derivada denormalizeDossierResearchInput/buildDossierReport(padrão dodossieRender.unit.spec.ts): 4 âncoras, rótulo literal ×4, "vote 1313", link de fonte por Q&A ancorada, MD superset com budget reduzido, deny-list barrando staff-only. Prova o encadeamento mais arriscado (âncora + teto) cedo, sem Chromium. - Emit único + builder.
emitHtmlSinglePdf+assertPageCountaditivos + teste;build-dossie-solla-briefing.mjscom o loop medido de shed; rodar em 1 recorte com fixturebriefing.json(dados gitignored), conferindo PDF de 4 páginas, rótulo em todas e.mdcompleto. - Superfície agêntica.
SKILL.md+ command + subagente autor +briefingSkill.unit.spec.ts+ registry (opencodeCommands.unit.spec.ts). - Integração nas 3 skills + contrato dos irmãos. Editar as 3 SKILLs e as 3
description:de command; adicionar oitdo 3º entregável nos 3 specs de lote; conferir o gitignore comgit check-ignore -vnos dirs reusados;docs/changelog/2026-09-22-c210.md. - Gates.
pnpm gate:fast(lint 0 warnings,tsc --noEmit, unit) → build real de 1 recorte (≤4 páginas, rótulo, fontes no.md) →pnpm push(o PR CI roda knip — exports em ERROR —, cycles, int, build e o e2e curado).
Testes previstos por camada
- Unit:
briefingContent(validação fail-closed, âncoras, ordem/mínimos do shed, deny-list, resto declarado),briefingRender(âncoras, literais, fontes, escape, MD superset),briefingSkill(skill/agent/command: seções, literaisInsumo interno de capacitação — não publicar/vote 1313/4 páginas,mode: subagent, semmodel:),buildPdf(aditivoemitHtmlSinglePdf+assertPageCount),opencodeCommands(novo comando no array), 3*BatchSkill(3º entregável + skill citada). Nenhum export novo sem consumidor (knip). - Int: nenhum — não há fronteira Payload/DB (scripts puros + leitura de JSON;
tests/int/*intocados). - E2E: nenhum — não há superfície de runtime no app; o PDF é artefato de script e a verificação é o build real (aceite manual), como no C186.
Rabbit holes / Não escopo (engenharia)
- Segundo pipeline de pesquisa/render; qualquer fetch de rede no build do briefing (Câmara/Portal/IBGE/SIOPS); forkar
dossieResearch/buildDossierReport/dossieRender. - Editar
emitHtmlPairPdf/dossieRender/os 3 builders para "reusar" o briefing; tocar a saída byte a byte dos artefatos publicados. - "Teto mole", continuação, pack grow/shrink no briefing (o produto quer 4 folhas fixas e curtas).
- Gerar o conteúdo autoral dentro do build determinístico (LLM no script); template automático de Q&A sem fato-âncora.
- Abrir o briefing para cenário/estimativa/projeção/staff-only ("só um numerinho") ou CTA/marca publicável ("aproveita e vira peça").
- Schema/migration/collection/rota/componente React/índice agregado de lote.
- Reescrever as seções de conteúdo das 3 SKILLs (isso é C209); aqui só o ponteiro do 3º entregável.
Riscos e mitigação
- Regressão dos 3 builders/dossiê por tocar
buildPdf/dossieUnit: mudanças aditivas com rejeitadas registradas;emitHtmlPairPdfe renderers intocados; specs existentes verdes nogate:fast/PR CI. - Corte escondido pelo
.sheet{overflow:hidden}: a guarda mede toda folha e conta as folhas (maxPages=4); qualquer estouro vira shed oudie— nada de sair cortado. - Conteúdo autoral sem lastro: âncora obrigatória resolvida contra
bulletinFacts(fonte garantida),gapReasonexplícito para lacuna, deny-list de staff-only/cenário, auditoria de citações do orquestrador, voz com dono único (solla-comunicacao, sem gemar). - Teto apertado com conteúdo real: shed determinístico + resto declarado no PDF e completo no
.md;dieorientando a lista a encurtar quando o mínimo não cabe; caps de autor são warnings. - C209 ausente/atrasado (soft):
defensesé opcional e, sem itens, a folha imprime a linha de lacuna ("sem registro localizado"); C210 não bloqueia nem é bloqueado. - Vazamento de artefato no repo público: dirs já gitignored (
/data/dossie-solla-*/,/docs/research/dossie-solla-*/); conferir comgit check-ignore -v; subagente proibido de commitar; contrato sem PII. - Ambiguidade no comando standalone (ex.:
cidade:Salvador): seletor de recorte literal + resolvedores das irmãs; token inválido/ambíguo falha fechado com o motivo no summary, sem inventar slug. - Drift entre a prosa da skill e o validador:
briefingSkill.unit.spec.tspina literais/regras (rótulo, "vote 1313", 4 páginas, campos do contrato, ordem do shed). - Rebase com C209 nas 3 SKILLs: C210 é P1 e toca só seções pontuais (saídas/summary/guardrails/referências); resolver conflito preservando o 3º entregável.
Adiado com gatilho (triage do simplify, 2026-09-22)
- Consumo do C209 (fronteira
defenses): hoje o autor redige as defesas a partir dosresearch.json; quando o C209 (#1251) mergear/exposer o artefato da dimensão "o que defende", o briefing consome o dono em vez de re-redigir. — Gatilho: C209 mergeado com o artefato de defesas. - Contagem real de páginas do PDF:
assertPageCountconta as folhas[data-page]; o 1:1 folha↔página é por construção (.sheet{break-after:page}- A4) e a contagem real (4) foi conferida com
pdfinfono build. — Gatilho: build real compdfinfo≠ nº de folhas, ou mudança no CSS de impressão/Chromium.
- A4) e a contagem real (4) foi conferida com
- Helper de parse compartilhado dos builders: o 4º builder repete o padrão
readJson/readEraResearchdos 3 irmãos (convenção da família). — Gatilho: 5º consumidor do parse, ou primeira correção de parse a aplicar em ≥2 builders. - Spec do builder por unit: o seam
themeSlugdo contrato é testado; o builder--unit=themefoi provado por build real (aceite manual, como no C186). — Gatilho: primeira alteração embuild-dossie-solla-briefing.mjsou relato de quebra por--unit.
Explicitamente fora (triage do simplify, 2026-09-22)
- Unificar a prosa do 3º entregável nas 3 SKILLs irmãs: cada skill é lida por si; a extração criaria acoplamento pior que a repetição (decisão registrada).
- Numerar o
ol.qa-list:list-style: noneé o markup do hi-fi aprovado no gate de design; numeração não é requisito.
Aceite de engenharia
- Aceite de produto da intenção coberto: briefing por recorte (cidade · instituição · tema), PDF A4 ≤4 páginas +
.md, com identificação, essencial com fonte, "o que defende" (C209 ou lacuna), roteiro ("vote 1313" + plano + compromisso nomeado), Q&A dos dois lados, evitar e conferir. - Integração literal: as 3 SKILLs citam
briefing-capacitacao-sollae entregam dossiê + boletim + briefing na mesma invocação; skill/command invocáveis sozinhos por recorte; mesmo build (sem fork). - Rótulo
Insumo interno de capacitação — não publicarem todas as folhas; sem CTA/marca publicável; artefato gitignored. - Guardrails: sem fonte o item não entra (âncora); empenho ≠ pagamento (fase do fato-âncora); esfera explícita nunca somada; leitura relativa; sem cenário/estimativa/staff-only; PII mínima.
- Teto rígido com prova: contagem de folhas ≤4 + fit por folha + shed determinístico (ordem/minimos testados) + resto declarado + fail-closed;
.mdsuperset. - Um dono por concern:
buildDossierReport/bulletinFacts(fatos),dossieResearch(pesquisa),dossieUnit(vocabulário),buildPdf(emit),reportText(escape/links),solla-comunicacao(voz); nenhum twin; 3 builders/renderers intocados. - Sem schema/migration/DB write/rota/componente React; build offline (sem segunda pesquisa factual); nenhum dado staff-only renderizado.
- Identificadores em inglês; português só em literais visíveis/labels; C210 literais verbatim.
- Testes unit novos verdes (
briefingContent,briefingRender,briefingSkill,buildPdfaditivo,opencodeCommands, 3*BatchSkill); int/e2e justificadamente ausentes. pnpm gate:fastverde; build real aceito em 1 recorte;pnpm push; changelogdocs/changelog/2026-09-22-c210.md.
Self-score (decision-quality, gate ≥4/5)
- Decisões caras têm rejeitadas? A–F com Opções/Recomendação/Rejeitadas explícitas (pipeline, contrato autoral, teto, emit único, companion, integração). — 5/5
- Abordagem cabe no appetite (3–4 dias)? Sim: scripts + prosa + unit, sem schema/rede, tracer no dia 1 e reuso máximo do engine; a quota está na redação com lastro e no port das 4 folhas. — 5/5
- Rabbit holes nomeados? Sim: segundo pipeline, fork do emit/render, teto mole, conteúdo inventado no build, cenário/staff-only, schema/rota, reescrita C209. — 5/5
- Depth check: reusa shell/helpers existentes? Sim —
buildDossierReport/bulletinFacts,dossieResearch,dossieUnit,buildPdf,reportText,cli, resolvedores das 3 skills, vozsolla-comunicacao; o que é novo tem responsabilidade própria (contrato autoral, plano de impressão, render do briefing). — 4/5 (o único ponto de atenção é o aditivo embuildPdf/dossieUnit, coberto por specs e pela regra "não tocar os builders"). - Intenção (aceite de produto) permanece satisfeita? Sim — ≤4 páginas, 3º entregável nas 3 skills, rótulo literal, Q&A dos dois lados, sem cenário/staff-only, derivado do dossiê; a engenharia não reescreveu o outcome. — 5/5
Total 24/25 (média 4,8/5) — passa o gate ≥4/5.