teqo-backup/docs/plans/relatorios-sem-truncamento.md

12 KiB
Raw Permalink Blame History

Relatórios e dossiês sem truncamento mecânico — reformular para caber ou ampliar o campo

Status: rascunho Atualizado em: 2026-09-17 Issue: #1136 Priority: P2 Impeccable: B — encaixe nas superfícies já desenhadas (relatório C163, dossiê/boletim C186); sem redesenho Design UI: N/A — superfície inalterada; alvo é o hi-fi já aprovado do C186 (e o do C163) Appetite: ~1–2 dias eng; um outcome verificável — gerar relatório/dossiê com texto longo e não ver nenhum "…", nenhum item sumido sem aviso, com o integral preservado no aprofundamento e a guarda de fit verde Responsável: —

Intenção

Ao gerar um relatório de cidade (C163) ou um dossiê/boletim (C186), informação se perde na última milha: ou o texto é cortado mecanicamente no meio da frase com "…" (C163, via excerpt()), ou itens inteiros somem em silêncio (C186, listas com slice e sem contador). Não é falta de espaço editorial — é o orçamento da superfície aplicado como corte cego. Regra do produto: se uma informação não cabe, o texto deve ser reformulado para caber perdendo o mínimo de informação, ou o campo/orçamento deve ser ampliado — nunca corte mecânico no meio, nunca item sumindo sem aviso. O pesquisador levanta o dado; o artefato não pode descartá-lo.

Persona e fluxo

  • Persona / contexto: o gabinete/assessoria prepara material pré-viagem; o researcher (humano ou skill) entrega respostas longas e ricas; o builder compõe a página 1/resumo (C163), o dossiê e o boletim (C186).
  • Job principal: ao abrir o PDF, ler a informação completa e confiável — e, se algo não coube no resumo, saber que existe e onde achá-lo, nunca descobrir depois que um trecho foi cortado no meio.
  • Fluxo desejado: researcher entrega o integral (answer/details) + um summary curto, completo e auto-contido quando a superfície de resumo exigir → o builder usa summary; sem ele, prefere answer e amplia o campo/orçamento enquanto a guarda de fit permitir → se ainda não couber, a linha aponta o aprofundamento sem reticências → toda lista truncada exibe contador ("e mais N"/"Mostrando X de Y") → o integral segue no aprofundamento → a guarda de fit mede scrollHeight e aborta se estourar.
  • Anti-goals de produto: corte mecânico com "…" em superfície de resumo; item sumindo em silêncio; espremer o layout para caber (a guarda continua fail-closed); redesenhar as superfícies; o builder reescrever conteúdo editorial (quem redige o summary é o researcher); varrer todo slice do repo além da família C163/C186.

Esboço de fluxo (B)

pesquisa (answer/details integral)
        ├── summary? ──sim──> superfície de resumo (pg1/cartões)
        └── não ─> prefere answer ─┤
                                   ▼
                    cabe no orçamento? ──sim──> renderiza
                                   └── não ─> amplia orçamento (guarda permite?) ──> renderiza
                                                                     └── não ─> linha aponta o aprofundamento SEM "…"

integral sempre preservado no aprofundamento (seção Fontes / páginas extras)
listas: slice → SEMPRE com contador "e mais N" / "Mostrando X de Y"
guarda de fit: scrollHeight > orçamento (+tolerância) → ABORTA (fail-closed)

Design UI (B)

  • Design UI: N/A — as superfícies têm design aprovado (relatório C163; dossiê e boletim C186). Este item não redesenha: ajusta conteúdo/orçamento dentro do hi-fi existente, sem componente/estado/layout novo. Se a execução exigir mudança material de layout, o designer entra e a mudança volta ao humano no PR.

Objetivo e aceite

  • Gerar relatório/dossiê/boletim com textos longos e não ver nenhum "…" em superfície de resumo.
  • Nenhuma lista truncada sem contador de itens restantes.
  • Texto integral sempre disponível no aprofundamento.
  • Guarda de fit verde (fail-closed preservado; nunca espremer).
  • Contrato summary documentado nos dois arquivos de pesquisa e instruído nas skills relatorio-cidade e dossie-solla-cidade.
  • Guardrails de produto: sem fonte não publica; empenho ≠ pagamento; esfera/abrangência nunca somadas; PII mínima; artefato gitignored; defeso.

Dados (intenção)

  • Vou apresentar dados? Sim, superfície neste item — o item decide como o dado já pesquisado é renderizado (orçamento de texto e contadores), nas superfícies impressas C163/C186.
  • Decisões desbloqueadas: o leitor decide com base no texto completo (não num fragmento cortado); o gabinete sabe o que ficou de fora do resumo (contador) e onde está o integral; o researcher sabe para qual orçamento redigir o summary.
  • Forma: adiada ao plano de implementação — aqui só restrições de produto: proibido "…" na superfície de resumo; contador em toda lista truncada; integral no aprofundamento; guarda de fit fail-closed; sem redesenho.

Dados da decisão (literais)

Corte mecânico com "…" — dono único no C163. excerpt() em scripts/lib/cityReportBlocks.mjs:91-94 (slice(0,max).trimEnd()+'…'). Caps: PAGE_ONE_ANSWER_MAX=90, PAGE_ONE_WHO_MAX=26, PAGE_ONE_RISK_MAX=70, PAGE_ONE_NAMES_MAX=20, PAGE_ONE_INDICATOR_DETAIL_MAX=70 (:64-79); SIGNAL_EXCERPT_MAX=180, SPEECH_SUMMARY_MAX=220, SPEECH_MENTION_MAX=220 (:53-55). Call-sites impressos na página 1: :154-161→:232,236,255,288,292,310,350,382; joinLimited :100-105→:198,248,322; excerpt(indicator.purpose, 70) :368. Integral preservado em buildResearchAnswersTable :1362-1383.

Descarte silencioso no C186. limit = list.slice(0,max) scripts/lib/dossieBlocks.mjs:96 (sem flag de resto) em :181-186 (MAX_ERA_NUMBERS=12), :190 (MAX_ERA_ACTIONS=4), :202-224 (MAX_DELIVERIES_PAGE_ONE=3), :226-233 (MAX_HOOKS_PAGE_ONE=2), :241-244 (MAX_PENDING_PAGE_ONE=4), :247-255 (MAX_REGION_ITEMS=4); único com contador: :333/:338 (MAX_SCOPE_LIST=5, guarda .total). Boletim: scripts/lib/dossieBulletin.mjs:44 (6) + :51 (14) = teto 20, sem contador (dossieBulletinRender.mjs:171-175).

Corte silencioso de caractere sem "…": scripts/lib/dossieCamara.mjs:46 (sumario ?? transcricao).slice(0,280), ITEMS_PER_LIST=20 :16,128; cityReportBlocks.mjs:870 row.body.slice(0,180); :1113 row.summary.slice(0,220); :1097 mentionExcerpt.slice(0,220).

Guarda de fit (não corta — aborta): C163 build-city-report.mjs:155-165 (PAGE_ONE_BUDGET_PX, −8px; aprofundamento não é medido); C186 dossiê build-dossie-solla-cidade.mjs:196-227 e boletim :241-249 (+10px). .sheet { height:297mm; overflow:hidden } (dossieRender.mjs:477-488, dossieBulletinRender.mjs:31-43).

Contrato do resumo (nova regra, literal): cada item de pesquisa passa a aceitar summary — curto, completo e auto-contido, redigido para caber no orçamento da superfície de resumo. Superfícies de resumo usam summary; aprofundamento continua com o integral (answer/details). Sem summary, prefere answer e amplia o orçamento enquanto a guarda permitir — "…" proibido; no limite, a linha aponta o aprofundamento sem reticências. Nenhum item some em silêncio (contador sempre). Guarda de fit fail-closed.

Contratos hoje: cityReportResearch.mjs:129-138,186 e dossieResearch.mjs:211-224,263 aceitam answer/details; news[].summary é validado e nunca renderizado; não há summary em items[].

Testes a atualizar: tests/unit/cityReportBlocks.unit.spec.ts:702,752,780; tests/unit/cityReportRender.unit.spec.ts:127,166; tests/unit/dossieBlocks.unit.spec.ts:155; tests/unit/dossieBulletin.unit.spec.ts:27,60.

Direção no codebase (hipótese)

  • Dono único de orçamento/composição de texto substitui o excerpt() do C163, reusado por C163 + C186 (não gemar caminho paralelo).
  • Contrato summary nos dois arquivos de pesquisa (cityReportResearch.mjs, dossieResearch.mjs).
  • Renderers C163/C186 preferem summary; integral permanece no aprofundamento.
  • Contadores "e mais N"/"Mostrando X de Y" em todas as listas hoje silenciosas (dossiê/boletim; dossieCamara.mjs).
  • Testes unit dos caps revisados e do novo contrato; skills relatorio-cidade e dossie-solla-cidade instruem o researcher a redigir summary.

Dependências

  • Soft com C187 (dossiê por instituição) — o novo item deve nascer sem truncamento; este item conserta o dono que o C187 reusa. Sem bloqueio.
  • Nenhuma dura.

Fora de escopo

  • Redesenhar as superfícies (o design aprovado do C186/C163 permanece).
  • Persistir dado novo, schema ou migration; alterar contratos públicos/URL.
  • Mudar o conteúdo editorial além do que cabe — o summary é redigido pelo researcher.
  • build-solla-ceuci-salvador-report.mjs (C157): só se for trivial e no mesmo dono; senão, item sucessor.

Rabbit holes de produto

  • "Fazer caber" reescrevendo no builder. Se alguém "só completar": o builder vira redator. Corte neste item: o builder não redige; sem summary, aponta o aprofundamento.
  • Um summary global em vez de por item. Corte neste item: summary por item, auto-contido por superfície.
  • Ampliação de orçamento vira licença para a página crescer. Corte neste item: a guarda de fit fail-closed é o teto.
  • Varrer todo slice silencioso do repo. Corte neste item: só a família C163/C186 (C157 como sucessor, se não for trivial).
  • Confundir "e mais N" (lista) com "…" (frase). Corte neste item: são dois defeitos distintos, com remediações distintas — tratar ambos, sem trocar um pelo outro.

Questões em aberto (produto)

  • Campo summary vs só ampliar orçamento? Opções: A) summary por item + ampliação como fallback | B) só ampliar orçamento. Recomendação: A — resolve na origem e dá ao researcher controle editorial. (assumido — validar)
  • "…" sempre proibido ou só no aprofundamento? Opções: A) proibir nas superfícies de resumo, manter no aprofundamento | B) proibir em tudo. Recomendação: A — o aprofundamento já carrega o integral. (assumido)
  • Cobrir C157 agora ou sucessor? Opções: A) incluir | B) sucessor. Recomendação: B — C157 não compartilha o dono reusado por C163/C186. (assumido)
  • Prioridade P1 vs P2? Opções: A) P1 (perda de informação visível) | B) P2. Recomendação: B — reavaliar para P1 se o C187 entrar em produção imediatamente. (assumido)

Referências

  • GitHub Issue # (após pnpm agent:register)
  • scripts/lib/cityReportBlocks.mjs:53-55,64-79,91-94,100-105,154-161,163-177,359-378,870,1097,1113,1362-1383
  • scripts/lib/cityReportRender.mjs:243-244,470,531,631,872,901,1185; scripts/lib/cityReportResearch.mjs:129-138,186
  • scripts/lib/dossieBlocks.mjs:96,181-186,190,202-233,241-255,333,338; scripts/lib/dossieBulletin.mjs:44,47-48,51; scripts/lib/dossieBulletinRender.mjs:171-175
  • scripts/lib/dossieCamara.mjs:16,46,128; scripts/lib/dossieRender.mjs:477-488; scripts/lib/dossieResearch.mjs:211-224,263
  • scripts/build-city-report.mjs:155-165; scripts/build-dossie-solla-cidade.mjs:196-227,241-249
  • Testes: tests/unit/cityReportBlocks.unit.spec.ts:702,752,780; tests/unit/cityReportRender.unit.spec.ts:127,166; tests/unit/dossieBlocks.unit.spec.ts:155; tests/unit/dossieBulletin.unit.spec.ts:27,60
  • Skills: .agents/skills/relatorio-cidade/SKILL.md, .agents/skills/dossie-solla-cidade/SKILL.md; AGENTS.md (edite o dono, não gema um irmão)