# Changelog — Calculadora SOx Scope · V2 Tier 1

**Data:** 2026-05-12
**Versão:** V1 → V2 (Tier 1 — Must-have)
**Arquivo afetado:** `sox-scope.html`
**Referência:** MD de recomendações `2fe3d47f-calculadorasoxv2recomendacoes.md`, seção 12 (Roadmap de Priorização para V2), Tier 1 — itens #1 a #8.
**Escopo desta entrega:** 8 itens do Tier 1 (must-have). Tier 2 (10 itens) e Tier 3 (10 itens) ficam parqueados aguardando aprovação.

---

## Resumo executivo

A V2 Tier 1 reorganiza o fluxo de entrada da calculadora (pré-triagem de 3 perguntas → escolha obrigatória de persona → form completo), substitui o wording uniforme V1 por **três wordings completos** alternáveis (Tech / Finanças / GRC), adiciona **"não sei" universal** com peso +1 e indicador de confiança ponderado no veredito, **bifurca Q3** com proxy de materialidade via receita (~1%), introduz **exit-early com confirmação** quando não há nexo, reformula textualmente Q6/Q10/Q11/Q12/Q14 (com Q14 renomeada "Redutor de escopo"), e injeta um **sistema de tooltips** (16 termos do Anexo A do MD) com popover tap-friendly em 12 pontos do HTML. O arquivo cresceu de 1134 para 2427 linhas (~+114%). Pesos da rubrica e thresholds 18/32 estão preservados — recalibração fica pro Tier 3.

---

## Mudanças por item

### T1.1 — Pre-screening de 3 perguntas (item #1 do roadmap)

**Técnico.** Nova seção `#prescreen` no topo do body, antes do persona-gate. 3 radios (`ps1`, `ps2`, `ps3`) com opções Sim/Não/Não sei. Função `evaluatePrescreen()` retorna `{ exit: 'out'|'continue', confidence, answers }`. Função `showPrescreenVerdict()` renderiza banner Out in-page sem submeter o form completo. Função `proceedFromPrescreen()` orquestra a próxima etapa (pula o persona-gate se houver `localStorage.sox_persona`).

**UX.** Usuário vê 3 perguntas (valores monetários? integração contábil? consumo por Controladoria?). Feedback inline depois de responder as 3 (verde se Out provável, neutro se segue pro form). Botão "Continuar →" habilitado só com as 3 respondidas. Veredito Out direto traz banner verde, justificativa textual ("a aplicação não processa valores monetários, não se integra com sistema contábil/fiscal e não gera output usado por Controladoria"), recomendação (NIST CSF / ISO 27001 / BCB 85) e três CTAs: imprimir, completar form mesmo assim, ou refazer pré-triagem.

**Decisão Pedro.** Decisão #1 — **veredito Out direto na mesma página + taxa de confiança + justificativa**. Confiança: 95% se 3 "Não" definitivos; 78% se 2 "Não" + 1 "Não sei". Se ≤1 "Não" e 2+ "Não sei", segue pro form (incerteza insuficiente pra concluir Out).

### T1.2 — Roteamento por persona obrigatório (item #2 do roadmap)

**Técnico.** Nova seção `#personaGate` com 3 `.persona-card` (botões com `data-persona="tech|financas|grc"`). Dicionário `WORDINGS` com 30 slots × 3 personas = **90 textos completos**. Função `applyPersona(p)` percorre todos os `[data-wq="<slot>"]` e substitui `innerHTML`, re-anexa tooltips, esconde gate, revela form + pill. Pill `#personaActive` mostra emoji + nome + botão "trocar" que dispara `returnToGate()`. Persistência via `localStorage.sox_persona`.

**UX.** Após pré-triagem (ou skip), usuário escolhe entre 3 cards: 💻 Tech (PO/Tech Lead/SRE), 📊 Finanças (Controller/FP&A), 🛡️ GRC (Compliance/Auditoria). Wording dos labels, helps e descrições de bloco muda completamente conforme persona. Pill no topo permite trocar persona a qualquer momento sem perder respostas.

**Decisão Pedro.** Decisões #2 (obrigatório como gate) e #3 (3 versões completas, não simplificado para 2). Wordings Tech traduzem jargão inline, Finanças usa terminologia contábil direta, GRC adota linguagem de auditor (PCAOB AS 2201, judgement/estimate, IPE/Key Reports, CUEC, RMM).

### T1.3 — "Não sei" universal nas Qs críticas (item #3 do roadmap)

**Técnico.** Adicionado 4º (ou 5º em Q9 e Q14) radio com `value="unknown"` em Q5, Q6, Q8, Q9, Q10, Q11, Q12, Q13, Q14. Q3 e Q7 já tinham. Maps de scoring atualizados: `levelMap.unknown = 1`, `customMap.unknown = 1`, `hostMap.unknown = 1`, e ternários para Q10/Q11/Q12/Q13/Q14 com fallback `unknown ? 1 : 0`. `unknownFlags` agora exposto no retorno de `score()` para alimentar T1.4.

**UX.** Em vez de chutar, o respondente pode marcar "Não sei". Resposta entra na contagem de confiança ponderada.

**Decisão Pedro.** Decisão #4 — **"Não sei" pesa +1** (incerteza vira risco, em vez de zero).

### T1.4 — Indicador de confiança no veredito (item #4 do roadmap)

**Técnico.** Constante `CONF_WEIGHTS` mapeia peso máximo por pergunta crítica (Q3=10, Q5/Q6/Q7/Q8/Q10/Q14=4, Q9/Q11/Q12/Q13=3, totalWeight=46). Função `computeConfidence(r)` retorna `pct = round((1 - unknownWeight/totalWeight) * 100)`. Função `renderConfidence(r)` injeta no `#confidenceBox` (dentro do banner do resultado) os elementos `.confidence-pct`, `.confidence-fill` (com classe `high`/`mid`/`low`), `.confidence-note` e `.confidence-detail`.

**UX.** Banner do resultado agora exibe: % de confiança grande, barra horizontal preenchida com cor semafórica, nota contextual ("Confiança alta", "Co-validação recomendada", "Co-validação obrigatória") e contagem ("X perguntas marcadas como 'não sei' — peso ponderado Y de Z").

**Decisão Pedro.** Decisões #5 (**ponderada pelo peso da pergunta** — Q3 "não sei" custa mais) e #6 (**barra horizontal + cores semafóricas** — verde ≥80%, amber 50-79%, vermelho <50%).

### T1.5 — Bifurcação Q3 com proxy de materialidade (item #5 do roadmap)

**Técnico.** Q3 dividida em três sub-perguntas: `q3a` (sabe a materialidade?), `q3known` (faixa relativa, igual à V1), `q3vol` (volume absoluto em R$, 4 buckets), `q3rev` (receita líquida, 4 buckets). Função `computeQ3Proxy()` mapeia volume/receita para valores centrais em R$ milhões e calcula `ratio = volM / (revM * 0.01)`, mapeando para `high` (≥1.0) / `medhigh` (≥0.5) / `med` (≥0.25) / `low` (<0.25). Função `getQ3Effective()` retorna o valor "como se" fosse Q3 V1 (alimenta `score()` sem mudar a rubrica). Função `updateQ3ProxyHint()` exibe ao vivo a estimativa ("materialidade implícita ≈ R$ 30 mi (1% de R$ 3 bi de receita). Volume ≈ R$ 200 mi → volume ≥ 100% da materialidade estimada"). `isQ3Answered()` trata caso especial no progress.

**UX.** Se usuário sabe materialidade, vê a Q3 original (V1). Se não sabe ou marca "não sei se sei", aparecem **duas perguntas em buckets de ordem de grandeza** (volume da app + faturamento da empresa) e um hint azul explicando a estimativa em tempo real. Rationale do veredito acrescenta `(estimado via proxy de ~1% da receita: materialidade ≈ R$ X, volume da app ≈ R$ Y)`.

**Decisão Pedro.** Decisão #7 — **proxy implementado** (~1% receita líquida). Faixas: receita >R$10bi → mat ~R$200M; R$1-10bi → R$10-100M; R$100M-1bi → R$1-10M; <R$100M → ~R$500k.

### T1.6 — Tooltips/glossário inline (item #6 do roadmap)

**Técnico.** Dicionário `TIPS` com 16 entradas (significant-account, materialidade, performance-materiality, RMM, ITAC, ITGC, IPE, SoD, SOC 1, CUEC, julgamento, top-down, key-system, redutor, COTS, ECL/IFRS 9), cada uma com `{title, body, yes, no}`. Componente `<button class="tip-trigger" data-tip="<key>">termo</button>` — JS `attachTooltips(root)` percorre, anexa popover dinamicamente com `buildPopoverHTML()`, posiciona via `positionPopover()` (alinhamento à esquerda ou direita conforme viewport), gerencia open/close via click/Enter/Space, ESC e click-fora. CSS `.tip-trigger` (border-bottom dotted + ⓘ super) e `.tip-popover` (max 320px, sombra). Estilo `@media print` esconde popovers.

**UX.** Em 12 pontos do HTML (descrições de bloco, helps específicos, sidebar), termos técnicos têm sublinhado pontilhado + ⓘ. Click/tap abre balão com definição em 1 linha + exemplo "Sim:" + exemplo "Não:". Tap-friendly (sem dependência de hover).

**Decisão Pedro.** Decisão #8 — **ⓘ ao lado do termo + popover on-click**. Implementado com `<button>` que envolve o termo (área de tap maior que só o ícone).

### T1.7 — Reformulação textual de Q6/Q10/Q11/Q12/Q14 (item #7 do roadmap)

**Técnico.** Substituição direta dos `<label>` e `<p class="question-help">` nas 5 perguntas. **Pesos preservados** (Q14: yes-strong=-4, yes-weak=-1, no=+2). Layout de Q10/Q11/Q12 mudou de `options inline` para `options` (vertical) por causa de labels mais longas.

**UX.** Q6 ("envolve premissas que podem mudar?"), Q10 ("existe lógica crítica dentro do sistema?"), Q11 ("gera relatório/extrato/export usado pela Controladoria?"), Q12 ("o sistema impede tecnicamente que o mesmo usuário aprove + execute + concilie?"), Q14 ("Redutor de escopo — em quanto tempo o erro seria detectado fora dela?"). Q14 renomeada formalmente.

**Decisão Pedro.** Decisão #9 — **só wording novo, pesos preservados** (recalibração de thresholds parqueada pra T3.1).

### T1.8 — Exit-early com confirmação (item #8 do roadmap)

**Técnico.** Banner `#exitEarlyBanner` (verde) renderizado após Q2. Função `checkExitEarly()` dispara a cada change em Q1 ou Q2. Lógica: `q1 === 'no' && (q2 vazio || único item === 'none')` → revela banner. Dois handlers: `exitEarlyBtn` chama `score()` + `render()` direto; `continueBtn` define `banner.dataset.dismissed = '1'` (impede reaparecer no mesmo estado). Se respostas mudam, `dismissed` é deletado e re-habilita.

**UX.** Banner verde "✓ Exit-early disponível" com explicação e dois CTAs: "Concluir como Out-of-Scope →" / "Continuar respondendo pra documentar".

**Decisão Pedro.** Decisão #10 — **pede confirmação antes de mostrar veredito final** (não submete automaticamente).

---

## Métricas de impacto

| Métrica | V1 | V2 Tier 1 | Δ |
|---|---|---|---|
| Linhas totais | 1134 | 2427 | +1293 (+114%) |
| Tooltips no dicionário | 0 | 16 termos | +16 |
| Triggers de tooltip no HTML | 0 | 12 pontos | +12 |
| Perguntas com "Não sei" | 2 (Q3, Q7) | 11 (todas críticas) | +9 |
| Wordings completos disponíveis | 1 (auditor) | 3 (Tech/Finanças/GRC) | +2 personas |
| Slots de texto controlados por `data-wq` | 0 | 32 | +32 |
| Etapas no fluxo de entrada | 1 (form direto) | 3 (pré-triagem → persona → form) | +2 |
| Botões/sections novos no body | — | 8 (prescreen, prescreen-verdict, persona-gate, persona-active, exit-early, confidence-box, 2 q3-branches) | +8 |
| Funções JS novas | — | ~20 (computeQ3Proxy, getQ3Effective, evaluatePrescreen, showPrescreenVerdict, initPrescreen, initPersona, applyPersona, returnToGate, getSavedPersona, proceedFromPrescreen, attachTooltips, positionPopover, computeConfidence, renderConfidence, checkExitEarly, toggleQ3Branches, updateQ3ProxyHint, isQ3Answered, fmtMoneyM, buildPopoverHTML) | +20 |

---

## Backups criados

Cópias defensivas do estado V1 antes de qualquer edição:

- `C:\Users\pedro\Claude\Website\itgov\calculadoras\sox-scope.v1-backup.html`
- `C:\Users\pedro\Claude\Website\itgov\calculadoras\sox-demo.v1-backup.html`

`sox-demo.html` **não foi tocado** neste Tier (alterações em sox-demo estão previstas pro Tier 2 — sublabel Borderline no Cenário 4 — e Tier 3 — revisão do Cenário 3, novos cenários co-fill e first-year).

---

## Pontos de atenção pra QA

1. **Persona ⇄ localStorage.** Ao recarregar a página, persona salva pula o gate (vai direto pro form após prescreen). Pra resetar: clicar em "trocar" no pill ou limpar `localStorage.sox_persona` no DevTools.
2. **Q3 proxy.** Testar combinações de volume × receita. Exemplos esperados:
   - Volume R$ 200M (gt100) + Receita R$ 3bi (1-10b) → ratio 200/(3000×0.01) = 6.7 → `high`
   - Volume R$ 30M (10-100) + Receita R$ 20bi (gt10b) → ratio 30/200 = 0.15 → `low`
   - Volume R$ 3M (1-10) + Receita R$ 300M (100m-1b) → ratio 3/3 = 1.0 → `high`
3. **Confiança.** Marcar várias "Não sei" deve baixar a barra. Q3 "não sei" sozinha derruba ~22% (peso 10 de 46 total).
4. **Exit-early.** Marcar Q1=Não e Q2 com checkbox em "Nenhum dos acima" — banner verde deve aparecer com 2 CTAs.
5. **Pre-screening Out direto.** Marcar 3 "Não" → "Continuar" → veredito Out in-page com confiança 95%, justificativa textual e botão de imprimir.
6. **Wording swap.** Trocar persona deve mudar o texto de todos os labels/helps marcados com `data-wq`. Tooltips devem permanecer funcionais após swap (`attachTooltips` re-roda).
7. **Print.** CSS `@media print` esconde nav, breadcrumb, progress, sidebar, actions e tooltips popovers. Banner do veredito imprime com cores adequadas (override `.confidence-fill` para tons sólidos compatíveis com B&W).

---

## Próximos passos

**Tier 2 (Should-have, 10 itens):**
- Co-fill via URL serializada (encoding base64 do estado completo).
- Show your work + sensitivity analysis no resultado (Top 3 + / Top 3 −, "se Q3 mudasse de X para Y…").
- Memorando PDF estruturado (lib externa, ainda a definir entre jsPDF / html2pdf / window.print).
- Comentários textuais por pergunta.
- Q15 first-year SOx (+2 RMM se primeira avaliação).
- Sublabel pro Borderline (provável Key / Limited / Out).
- Ponderação Q2 (Receita/Tesouraria/Crédito/Fechamento pesam +2; demais +1) — atenção: pode exigir recalibração dos thresholds 18/32.
- Alertas inline de inconsistência (Q1=não × Q2 com processos selecionados).
- Decision tree visual antes do form.
- Anchoring com 2-3 cenários (CoreLedger/TaxLake/NeoTalk) no topo do form.

**Tier 3 (Nice-to-have, 10 itens):** recalibração de thresholds contra ground truth, página `/sox-metodologia`, Q16 fechamento crítico, revisão do Cenário 3 da demo, cenários co-fill e first-year, telemetria, histórico/versionamento, botão "discutir com auditoria", mobile-first review.

---

## Notas técnicas

- **Persistência.** Apenas `localStorage.sox_persona` (chave única) é gravada. Estado das respostas **não é** persistido (cada refresh começa do zero). Persistência completa fica pro Tier 2 (T2.1 — co-fill via URL).
- **Proxy de materialidade.** Implementado com fórmula `materialidade ≈ 1% × receita líquida`. Os bucket values centrais (200M, 30M, 3M, 0.5M para volume; 20.000M, 3.000M, 300M, 50M para receita) são heurísticas razoáveis; podem ser refinadas com telemetria (T3.7) ou estudo de ground truth (T3.1).
- **Thresholds inalterados.** 18 (Limited) e 32 (Key) permanecem. Se T2.7 (ponderação Q2) for aprovada, recalibração pode ser necessária antes de fechar Tier 2.
- **Pesos de confiança.** Hardcoded em `CONF_WEIGHTS` no JS. Não derivam automaticamente do scoring — se a rubrica for recalibrada, atualizar manualmente.
- **Acessibilidade.** Tooltips com `role="button"`, `tabindex="0"`, `aria-expanded` e `aria-label`. Popover com `role="tooltip"`. Falta: progress bar com `role="progressbar"` + `aria-valuenow` (parqueado pro T3.10 — mobile-first review).
- **Compatibilidade.** Atributo `[hidden]` reforçado com `display: none !important` global pra evitar override por `main { display: grid }`. Cores semafóricas usam #4ade80 / #fbbf24 / #f87171 (contraste OK em fundo escuro do banner do resultado).
- **HTML "buttons" dentro de labels.** Todos os `tip-trigger` são `<button type="button">` com `e.stopPropagation()` no click — não disparam submit acidental, não ativam o radio adjacente.
- **Mount stale no shell.** Durante implementação, `mcp__workspace__bash` reportou tamanho cached do arquivo (46134 bytes pré-edits) enquanto os file tools (Read/Edit/Grep) operavam corretamente sobre o estado atualizado. Validação foi feita via Grep direto. Sem impacto no produto final.

---

## Hotfixes pós-Tier 1

### HF-001 — Botão "Continuar" do pre-screening eternamente desabilitado (2026-05-16)

**Sintoma reportado pelo Pedro.** Ao responder as 3 perguntas do pre-screening, o botão "Continuar →" não habilitava — permanecia desabilitado independente das respostas. Feedback inline também não aparecia. Sem caminho viável de avançar (nem para o form completo, nem para o veredito Out direto). Botão "Pular pré-triagem" funcionava (escapava do prescreen).

**Causa raiz.** A função utilitária `getRadio(name)` (linha 1742 da versão entregue) buscava radios via `form.querySelector(...)`, onde `form = document.getElementById('soxForm')` é o elemento `<form>` que contém Q1–Q14. Os radios do pre-screening (`ps1`, `ps2`, `ps3`) ficam **fora** desse form, em `<section id="prescreen">` no topo do body. Portanto `form.querySelector('input[name="ps1"]:checked')` **sempre retornava `null`**, fazendo:

1. `evaluatePrescreen()` retornar `null` no guard `if (!p1 || !p2 || !p3) return null;`
2. `updatePrescreenState()` tratar como "ainda incompleto" → mantinha `btn.disabled = true` e `feedback.hidden = true` permanentemente.

A mesma falha afetaria silenciosamente qualquer futuro radio fora do form com nome lido por `getRadio`. Bug clássico de scoping em refatoração.

**Correção aplicada.** Trocada a busca de `form.querySelector` / `form.querySelectorAll` para `document.querySelector` / `document.querySelectorAll` em `getRadio` e `getChecks`. Todos os atributos `name` no documento são únicos (q1–q14, q3a, q3known, q3vol, q3rev, ps1–ps3), então a busca global é segura — não há risco de colisão. Comentário inline (`// HOTFIX 2026-05-16: ...`) deixado no código pra documentar a decisão e evitar regressão.

```js
// Antes:
function getRadio(name) {
  var checked = form.querySelector('input[name="' + name + '"]:checked');
  return checked ? checked.value : null;
}

// Depois:
function getRadio(name) {
  var checked = document.querySelector('input[name="' + name + '"]:checked');
  return checked ? checked.value : null;
}
```

Mesma mudança em `getChecks`. Total: 2 linhas alteradas (lookup de seletor) + 4 linhas de comentário explicativo.

**Validação programática.** Script `outputs/test-prescreen.js` rodado com Node 22, simulando o DOM em dois modos (form-scoped vs document-scoped) e validando 10 cenários da `evaluatePrescreen()`:

| # | Cenário | Esperado | Pré-fix | Pós-fix |
|---|---|---|---|---|
| 1 | 3 Sim | `continue, has-yes` | ❌ null | ✅ |
| 2 | 3 Não | `out, 95%, 0 unknowns` | ❌ null | ✅ |
| 3 | 2 Sim + 1 Não | `continue, has-yes` | ❌ null | ✅ |
| 4 | 2 Não + 1 Não sei | `out, 78%, 1 unknown` | ❌ null | ✅ |
| 5 | 1 Não + 2 Não sei | `continue, too-uncertain` | ❌ null | ✅ |
| 6 | 3 Não sei | `continue, too-uncertain` | ❌ null | ✅ |
| 7 | 1 Sim + 2 Não | `continue, has-yes` | ❌ null | ✅ |
| 8 | 1 Sim + 1 Não + 1 IDK | `continue, has-yes` | ❌ null | ✅ |
| 9 | parcial (sem ps3) | `null` | ✅ | ✅ |
| 10 | vazio | `null` | ✅ | ✅ |

**Resultado.** Pré-fix: 2/10 passam (apenas os casos onde `null` era de fato a resposta esperada — falso "passa"). Pós-fix: **10/10 passam**. Bug reproduzido e correção validada.

**Efeitos colaterais analisados.** Mudar `getRadio`/`getChecks` para escopo global potencialmente afeta todos os outros chamadores (`score()`, `isQ3Answered()`, `toggleQ3Branches()`, `computeQ3Proxy()`, `updateQ3ProxyHint()`, `getQ3Effective()`, `checkExitEarly()`, `updatePrescreenState()`). Como todos os `name` lidos por essas funções estão dentro do form OU no prescreen (sem duplicação no documento), a mudança é segura e mantém comportamento idêntico para o form. Nenhum outro componente da página usa radios fora desses dois escopos.

**Arquivos tocados pelo hotfix:**
- `sox-scope.html` — 2 linhas alteradas + 4 linhas de comentário (em torno da linha 1742).
- `outputs/test-prescreen.js` — script de teste programático (não vai pra produção; fica no workspace de outputs como evidência da validação).

**Lições.** O bug não foi pego durante o desenvolvimento porque o pre-screening foi a última feature implementada (T1.1) e a função utilitária `getRadio` foi escrita pensando apenas no form principal (estabelecida na V1). Pra evitar repetir: refatorar `getRadio` pra aceitar um parâmetro `root` explícito quando o Tier 2 introduzir mais áreas (co-fill via URL pode trazer estado pra outros escopos).

---

# Tier 2 — Should-have (2026-05-16)

> **📄 Movido pra arquivo dedicado:** [`2026-05-12-changelog-sox-scope-v2-tier2.md`](2026-05-12-changelog-sox-scope-v2-tier2.md).
> Mantido aqui inline pra histórico de leitura sequencial (Tier 1 → Hotfix → Tier 2 num mesmo arquivo).
> A versão "canônica" e expandida do Tier 2 está no arquivo dedicado — atualizações futuras só naquele arquivo.

**Status:** Entregue, aguardando QA do Pedro.
**Versão:** V2 Tier 1 → V2 Tier 1+2
**Arquivos afetados:** `sox-scope.html` (principal), `sox-demo.html` (sublabel do Cenário 4 + fórmula).

## Resumo executivo do Tier 2

O Tier 2 adiciona ao Tier 1: alertas inline de inconsistência Q1↔Q2; Q15 first-year SOx (+2 ao RMM); ponderação Q2 por processo (Receita/Tesouraria/Crédito/Fechamento = +2; demais = +1; cap 5) — validada empiricamente nos 5 cenários da demo com **zero drift de veredito**; sublabels Borderline (provável Key/Limited/Out) refletidos também no Cenário 4 da demo; comentários textuais por pergunta com contagem; co-fill via URL serializada em hash base64 (round-trip validado em 6/6 testes); show your work no banner do resultado (Top 3 positivos + Top 3 redutores + sensitivity por "não sei"); memorando PDF estruturado com jsPDF (cabeçalho navy, veredito colorido, justificativa, pontuação, comentários do respondente, linha de assinatura, paginação); anchoring com 3 cenários no topo do form; decision tree visual SVG com destaque do caminho percorrido em tempo real.

## Mudanças por item

### T2.8 — Alertas inline de inconsistência (item #16 do roadmap)

**Técnico.** Função `checkInconsistency()` registrada no listener de change do form. Dispara sempre que Q1 ou Q2 mudam. Lógica: `q1 === 'no' && q2.length > 0 && q2.indexOf('none') === -1` → revela banner amber.

**UX.** Banner amber abaixo de Q2 ("⚠ Possível inconsistência. Q1 indica que a aplicação não tem impacto financeiro, mas Q2 marca processo(s) significativo(s). Reconciliar antes de prosseguir: provavelmente Q1 deveria ser 'Indiretamente — alimenta um sistema em escopo'…"). Não bloqueia submit.

**Decisão Pedro.** Decisão #15 — começar conservador, só com Q1 vs Q2. Combinações adicionais (Q14=forte ∩ Q10/11/12 todos sim; Q9=saas ∩ Q8=custom) ficam pro Tier 3 com base em telemetria.

### T2.5 — Q15 first-year SOx (item #13 do roadmap)

**Técnico.** Nova pergunta no Bloco 3 (RMM), depois de Q9. Pesos: `yes=0, no=2, unknown=1`. Adicionado ao `score()` em `block3Score`. `TOTAL_QUESTIONS` 14 → 15. `CONF_WEIGHTS.q15 = 2`. Wordings para Tech/Finanças/GRC. `unknownFlags.q15` exposto.

**UX.** Nova pergunta inline com 3 opções (Sim/Não/Não sei). Wording adaptado por persona. Quando "Não", rationale acrescenta linha "é primeira avaliação SOx (first-year), elevando o RMM por imaturidade de controles".

**Decisão Pedro.** Decisão #11 — inserido no Bloco 3 (RMM) conforme MD. Decisão #12 — peso 2 em `CONF_WEIGHTS` (consistente com impacto no RMM). Decisão #20 — 3 wordings completos com terminologia adequada (Tech: "auditoria SOx"; Finanças: "SOx attestation"; GRC: "PCAOB Year 1 / Year 2+ audit").

### T2.7 — Ponderação Q2 + validação empírica (item #15 do roadmap)

**Técnico.** Substituído `block1Score += Math.min(q2.length, 3) * 1` por iteração sobre `Q2_WEIGHTS = { revenue: 2, treasury: 2, loans: 2, close: 2, purchases: 1, payroll: 1, inventory: 1, tax: 1 }` com cap em 5. **Thresholds 18/32 preservados após validação empírica.**

**Validação programática.** Script `outputs/test-q2-weighting.js` rodado com Node 22. Comparou V1 (uniforme) × V2 (ponderada+Q15) nos 5 cenários da demo + 3 cenários de borda hipotéticos:

| Cenário | V1 Total | V2 Total | Δ | V1 Veredito | V2 Veredito |
|---|---|---|---|---|---|
| 1 — CoreLedger | 48 | 50 | +2 | Key | Key |
| 2 — TaxLake | 25 | 25 | 0 | Limited | Limited |
| 3 — CardCore | 27 | 28 | +1 | Dependency | Dependency |
| 4 — CreditRisk | 46 | 48 | +2 | Borderline | Borderline |
| 5 — NeoTalk | 3 | 3 | 0 | Out | Out |
| Borda A — Out→Limited? | 8 | 9 | +1 | Out | Out |
| Borda B — Limited→Key? | 20 | 22 | +2 | Limited | Limited |
| Borda C — first-year + ponderação | 24 | 28 | +4 | Limited | Limited |

**Resultado.** Zero drift de veredito. Thresholds 18/32 mantidos. Fallback B (20/34) não foi necessário.

**Decisão Pedro.** Decisão crítica #14 — opção C (validação empírica). Resultado dispensou o fallback B.

### T2.6 — Sublabel Borderline + atualização Cenário 4 demo (item #14 do roadmap)

**Técnico.** Em `score()`, quando `verdictKey === 'borderline'`, calcula `borderlineSubKey` com regra:
- `total >= 32 || (b2 >= 8 && itac+ipe >= 4)` → `'key'`
- `total >= 18` → `'limited'`
- caso contrário → `'out'`

Exposto no retorno. `buildControls()` ajustado: quando Borderline com sublabel Key/Limited, mostra ITGC esperados provisórios + nota sobre como defender o escopo.

**UX.** Banner do resultado mostra "Borderline · provável Key System" (ou Limited / Out) em vez de "Borderline — discussão com auditoria". Cenário 4 da demo (CreditRisk Suite) atualizado para "Borderline · provável Key System" com gate-trigger explicando que `total (46) ≥ 32 e B2 ≥ 8 com ITAC+IPE ≥ 4 — a aplicação bateria em Key se a materialidade fosse confirmada`. Fórmula da demo menciona os sublabels.

**Decisão Pedro.** Decisão crítica #13 — proposta aprovada exatamente como sugerida; Cenário 4 atualizado.

### T2.4 — Comentários por pergunta (item #12 do roadmap)

**Técnico.** Função `initComments()` percorre `.calc-form [data-wq]` com padrão `qN-label`, identifica cada pergunta única e injeta dinamicamente um `<button.comment-toggle>💬 comentar</button>` posicionado absoluto no canto superior direito de cada `.question`, mais um `<div.comment-box hidden>` com `<textarea maxlength="500">`. Função `getAllComments()` percorre os textareas e retorna `{ qN: 'texto' }`. Função `updateCommentsSummary()` mostra "· X comentários" no header do progress.

**UX.** Botão 💬 aparece no canto de cada uma das 15 perguntas. Click expande textarea (max 500 chars, com contador). Botão muda de cor/label ("1 comentário") quando há conteúdo. Contagem global aparece no header.

**Decisão Pedro.** Decisão #10 — ícone discreto, click expande inline. Decisão #7 — comentários vão pro PDF (implementado em T2.3). Decisão #21 — botão "Copiar link colaborativo" (vinculado a T2.1).

### T2.1 — Co-fill via URL (item #9 do roadmap)

**Técnico.** Funções `buildState()`, `encodeState()` (base64 com UTF-8 via `btoa(unescape(encodeURIComponent(json)))`), `decodeState()`, `applyState()`, `copyShareLink()`, `getInitialState()`. Estado serializado: `{ v: 2, ans: { q1, q2[], q3a, ..., q15, appName, owner }, com: { qN: 'comentário' } }`. **Não inclui persona** (decisão #2). Hash format: `#state=<base64>`. Fallback de clipboard via `document.execCommand('copy')` em browsers antigos. Toast feedback. Banner azul "Avaliação compartilhada carregada" quando state vem da URL.

**Validação programática.** Script `outputs/test-co-fill.js` rodado com Node 22. 6 cenários de round-trip (estado mínimo, completo, com proxy, UTF-8, comentário extremo 500 chars, versão inválida). **6/6 passou**. URL típica (CoreLedger com 2 comentários) = 659 chars, bem abaixo do limite 2000.

**UX.** Botão "🔗 Copiar link colaborativo" nas actions do form e do resultado. Toast confirma cópia. Aviso se URL > 1800 chars. Ao abrir URL com `#state=…`, prescreen é pulado, form revelado já com respostas preenchidas e banner explicativo no topo.

**Decisão Pedro.** Decisões críticas #1 (hash), #2 (não inclui persona), #3 (comentários incluídos), #4 (abreviar/limitar 500 chars), #21 (botão "Copiar link colaborativo").

### T2.2 — Show your work + sensitivity (item #10 do roadmap)

**Técnico.** Função `getResponseImpacts(r)` retorna lista `{ qid, label, detail, pts }` para todas as perguntas respondidas com peso != 0 (incluindo Q14 negativo). Função `buildShowYourWork(r)` separa positivos/negativos, ordena por magnitude, pega Top 3 de cada. Sensitivity itera `r.unknownFlags` e produz hints (ex.: "Q5 marcada como 'não sei' — se mudar para 'high (+3)' varia o total").

**UX.** Nova seção "Por que esse veredito · Show your work" no result-body, entre "Pontuação por bloco" e "Controles esperados". Cada item: tag azul com QID, label da pergunta, detalhe da resposta (itálico), pontos à direita. Pontos negativos em verde. Sensitivity como lista separada. Quando não há "não sei", mostra "Nenhuma resposta marcada como 'não sei' — o veredito tem confiança máxima".

**Decisão Pedro.** Decisão #5 — sensitivity foca apenas em "não sei" (foco didático). Decisão #6 — Q14=yes-strong/yes-weak entram no Top 3 redutores com label dedicado.

### T2.3 — Memorando PDF estruturado (item #11 do roadmap)

**Técnico.** jsPDF 2.5.1 carregado via CDN cdnjs (`defer` no `<head>`). Função `exportPDF()` lê `lastResult` (variável guardada por `render()`) e monta PDF A4 portrait com:

1. Cabeçalho navy 90pt com título e timestamp.
2. Identificação (app, owner, persona).
3. Caixa de veredito colorida conforme tipo (vermelho Key, amber Limited, azul Dependency, cinza Borderline, verde Out).
4. Barra de confiança com fill semafórico.
5. Tabela "Pontuação por bloco" (5 linhas).
6. Justificativa (texto multilinha).
7. Show your work — Top 3 positivos + Top 3 redutores.
8. Comentários do respondente (se houver) — sectioned por pergunta.
9. Controles esperados + próximos passos.
10. Linha de assinatura ("Aprovado por: ____ Data: ____").
11. Disclaimer.
12. Rodapé com paginação em todas as páginas.

Função `pageBreakIfNeeded(extra)` injeta `addPage()` quando y + extra ultrapassa H - 60.

**UX.** Botão "📄 Exportar memorando PDF" como primary action no banner do resultado (substitui "Imprimir / Salvar PDF" como ação principal; "Imprimir página" mantém como secundária). Nome do arquivo gerado: `YYYY-MM-DD-sox-scope-<appName>.pdf`. Toast warning se jsPDF ainda não carregou.

**Decisão Pedro.** Decisões críticas #7 (jsPDF), #8 (logo do ItGov no cabeçalho — implementado como título textual com nome do produto; logo image opcional fica pro Tier 3 quando tivermos a imagem como dataURL embedded), #9 (linha de assinatura), #22 (sublabel Borderline aparece no PDF — vem do `r.verdict` que já inclui).

### T2.10 — Anchoring com 3 cenários (item #18 do roadmap)

**Técnico.** Div `.anchoring` antes do `progress-wrap`, hidden por padrão. Revelado em `applyPersona()`, escondido em `returnToGate()`. 3 cards estáticos com cores correspondentes aos vereditos.

**UX.** Antes do form principal, banner com 3 cards: CoreLedger BR (Key, vermelho), TaxLake (Limited, amber), NeoTalk (Out, verde). Cada card mostra nome + badge colorido + 1 frase explicativa + total típico. Calibra o respondente antes dele projetar percepções.

**Decisão Pedro.** Decisão #18 — CoreLedger/TaxLake/NeoTalk (extremos pra calibração; pula Borderline pra não confundir). Decisão #19 — estático, sem auto-fill (Tier 3 considera).

### T2.9 — Decision tree visual com destaque (item #17 do roadmap)

**Técnico.** Botão collapsible "Ver lógica de classificação" abre `<svg>` inline com viewBox 760×580. 4 caixas decisórias (Q1, Q13, Q3, threshold Key, threshold Limited) + 5 caixas de veredito coloridas. Edges como `<path>` com IDs estáveis. Função `highlightTreePath()` chamada no listener de change: limpa `.active` e re-aplica conforme respostas atuais. Usa `lastResult` se disponível pra calcular thresholds; caso contrário, tenta `score()` se todos os radios essenciais estão preenchidos. Cores customizadas por veredito (rect fill + stroke + text fill).

**UX.** Botão discreto no topo do form ("▶ Ver lógica de classificação"). Click expande SVG abaixo. Caminho percorrido pela resposta atual fica em azul navy com stroke-width maior. Atualização em tempo real conforme usuário responde. Legenda explica como ler.

**Decisão Pedro.** Decisão #16 — opção C implementada (SVG + JS com highlight do caminho atual). Decisão #17 — collapsible no topo do form. Decisão #23 — só fluxo, sem pesos (pesos ficam na demo / rubrica separada).

## Métricas de impacto (Tier 1+2 acumulado)

| Métrica | V1 | V2 Tier 1 | V2 Tier 1+2 | Δ Tier 2 |
|---|---|---|---|---|
| Linhas totais | 1134 | 2427 | ~3300 | +870 |
| Perguntas no form | 14 | 14 | 15 (+Q15) | +1 |
| Tooltips no dicionário | 0 | 16 | 16 | 0 |
| Cenários da demo destacados como anchoring | 0 | 0 | 3 | +3 |
| Funções JS novas | 0 | 20 | 36 | +16 |
| Slots do dicionário WORDINGS | 0 | 30×3 | 32×3 | +2 (Q15 label + help) |
| CONF_WEIGHTS total | — | 46 | 48 | +2 |
| Pesos Q2 ponderados | uniforme +1 | uniforme +1 | 8 entradas (cap 5) | calibrado |
| Endpoints de export | print | print | print + PDF + share | +2 |
| SVG inline | 0 | 0 | 1 (decision tree) | +1 |
| Vereditos refinados | 5 | 5 | 5 + 3 sublabels Borderline | +3 |
| Validações programáticas | 0 | 1 (pre-screening) | 3 (+ Q2 + co-fill) | +2 |
| Round-trip de estado serializável | n/a | n/a | sim, base64 com versionamento | novo |

## Backups e arquivos auxiliares

**Já existentes do Tier 1:**
- `sox-scope.v1-backup.html`
- `sox-demo.v1-backup.html`

**Scripts de validação (outputs, fora de produção):**
- `outputs/test-prescreen.js` — validação do hotfix HF-001
- `outputs/test-q2-weighting.js` — validação empírica T2.7 (drift = 0)
- `outputs/test-co-fill.js` — round-trip T2.1 (6/6)

## Pontos de atenção pra QA do Tier 2

1. **Q15 first-year.** Adicionada no Bloco 3 com 3 opções. Marcar "Não" deve adicionar +2 ao RMM e justificar no rationale.
2. **Q2 ponderada.** Selecionar 4 processos com peso 2 cada (revenue/treasury/loans/close) deve atingir cap em 5. Selecionar 5+ processos não-pesados (purchases/payroll/inventory/tax) também atinge cap em 5.
3. **Sublabel Borderline.** Forçar Q3=não sei + alta pontuação deve mostrar "provável Key". Forçar Q3=não sei + total baixo deve mostrar "provável Out".
4. **Cenário 4 da demo** (sox-demo.html). Verificar que o veredito mostra "Borderline · provável Key System".
5. **Comentários.** Adicionar comentários em 3 perguntas → header deve mostrar "3 comentários". Botão muda de cor. Limpar respostas (botão Reset) NÃO limpa comentários atualmente — comportamento esperado.
6. **Co-fill via URL.**
   - Preencher form parcial + comentários → "Copiar link colaborativo" → toast de confirmação.
   - Abrir URL em nova aba → prescreen pulado, banner azul aparece, respostas preenchidas, comentários restaurados.
   - Persona NÃO transferida (cada um escolhe a sua).
7. **Show your work.** Após calcular, verificar:
   - Top 3 positivos ordenados por magnitude (Q1 ou Q3 normalmente lideram).
   - Top 3 redutores quando aplicável (Q14=yes-strong/yes-weak).
   - Sensitivity hints apenas para "não sei".
8. **PDF.**
   - Calcular → click "📄 Exportar memorando PDF".
   - Verificar download `YYYY-MM-DD-sox-scope-<app>.pdf` com paginação correta.
   - Cabeçalho navy, veredito colorido, comentários do respondente (se houver), linha de assinatura no fim.
9. **Anchoring.** 3 cards visíveis no topo do form após persona escolhida. Pra usuário recorrente (persona salva), aparece direto.
10. **Decision tree.** Click "▶ Ver lógica de classificação" expande SVG. Conforme responde, caminho destacado muda de azul claro para azul navy. Após calcular, caminho final inteiro fica destacado.
11. **Inconsistência Q1/Q2.** Marcar Q1=não → marcar Q2=Receita → banner amber deve aparecer entre Q2 e o exit-early banner.

## Notas técnicas Tier 2

- **jsPDF via CDN.** Carregado com `defer` no `<head>`. Caso o usuário clique no botão de export antes do load, mostra toast warning. Funciona offline depois do primeiro acesso (cache do browser).
- **Encoding UTF-8 no base64.** `btoa(unescape(encodeURIComponent(json)))` para encode e o inverso para decode. Compatível com acentos portugueses e caracteres especiais.
- **Versionamento do estado.** `STATE_VERSION = 2`. `decodeState` rejeita versões diferentes — futura migração precisa de função `migrateState(state)`.
- **Highlight do tree em estado parcial.** Quando faltam respostas pra calcular total, o tree destaca só até onde dá. Quando persona é trocada, o highlight é recalculado.
- **Comentários NÃO são incluídos no reset do form.** O `<form reset>` só limpa inputs nativos. Pra limpar comentários explicitamente, usuário precisa esvaziar manualmente. Decisão consciente — comentários costumam ser custosos de re-escrever e o usuário pode querer manter durante uma rodada de testes de respostas.
- **Persistência.** localStorage continua só `sox_persona`. Estado completo do form fica no hash da URL (T2.1). Combinar futuramente: lembrar último estado em localStorage também.
- **Ponderação Q2 ↔ thresholds.** Validação empírica confirmou que thresholds 18/32 seguem válidos pós-ponderação. Caso futuro item (T3.x) mude pesos novamente, refazer `outputs/test-q2-weighting.js` adicionando mais cenários de borda.
- **Sublabel Borderline na demo.** Apenas o Cenário 4 foi atualizado. Os outros 4 cenários da demo não têm Borderline. Cenário 4 agora explicita o raciocínio do sublabel no `gate-trigger`.
- **Decision tree e telemetria.** O SVG é estático em termos de layout. Coordenadas hardcoded. Caso tenha que mudar a rubrica (ex.: adicionar Q16 fechamento crítico no Tier 3), pode ser necessário re-desenhar uma decisão extra.
- **Persona não no co-fill.** Decisão consciente (#2): o destinatário escolhe a própria persona. Caso quisermos transferir wording sugerido (não obrigatório), poderíamos adicionar `state.suggestedPersona` no Tier 3.

---

*Documento gerado durante as entregas Tier 1 e Tier 2 — Calculadora de Escopo SOx · ItGov Insights*
