Artefato gerado. Nesta versão pública, só as referências de contexto foram generalizadas (nome e caminhos do cenário, repositório-base, texto de publicação); o conteúdo técnico do plano é o original.
Evidência equivalente ao “Plan Mode”: este é o plano integral que o orquestrador recebeu do subagente Opus antes de qualquer código, salvo sem edição de conteúdo técnico (ver a nota acima). Os executores (Haiku/Sonnet) receberam as seções correspondentes como especificação.
Executar os blocos 0 a 6 do Lead Scorer em modo autônomo, sem nada mudar fora da pasta do projeto, com um commit por bloco feito pelo orquestrador. O resultado esperado:
clean → features → score → explain → validate → build → docs.solution/app/ que funciona em file://, com data.js gerado, 4 listas, explicações, filtros e modo teste.Desenho técnico, validado num protótipo que rodei só para leitura via python -c (nenhum arquivo criado):
| Probabilidade: P(ganho nos 90 dias seguintes | Engaging, idade) por uma logística monotônica de “platô + queda”, com joelho aprendido. Ela é estimada em fotos mensais de fim de mês (de 31/03/2017 até T−90), sem vazamento. |
PREÂMBULO POWERSHELL (cole no início de cada chamada; variáveis não persistem entre chamadas):
$PM="C:\Users\Info Mania\Documents\Predictive Analytics for CRM Sales Performance"; $PY="$PM\.venv\Scripts\python.exe"; $SUB="$PM\lead-scorer"; $SOL="$SUB\solution"
Comandos do projeto: Set-Location $SOL; & $PY -m <módulo>. PowerShell 5.1: sem &&; use ; e if ($?) {...}. Caminhos têm espaços: sempre entre aspas.
p.chromium.launch(channel="msedge", headless=True)). Não tem scikit-learn. Não instale nada e não use a rede.df[c][m]=x); use .loc.str; datas como datetime64[us].pd.date_range(..., freq="ME").inplace, applymap nem fillna(method=).json.dump, converta numpy para tipos Python e NaN/NaT para None.open/to_csv/read_csv com encoding="utf-8". Todo CLI começa com sys.stdout.reconfigure(encoding="utf-8").now() ou timestamp em arquivo gerado; sementes fixas. Rodar o build duas vezes tem que gerar bytes idênticos.$SUB (exceção única, só na E12: $PM\kit-lead-scorer\PUBLICAR.md);solution/data/raw/*;process-log/00_fase0_exploracao.md e process-log/scripts/*.py;CLAUDE.md, PROMPTS.md, GUIA.md, PROMPT_AUTONOMO.md, docs/briefing.md;process-log/NN_bloco-N.md de blocos anteriores.gitw.py commit: quem commita é o orquestrador. Pode rodar & $PY "$PM\tools\gitw.py" status.process-log/NN_bloco-N.md (NN com 2 dígitos), com as seções:
Registre só o que aconteceu. Nunca atribua ao Rafael algo que não esteja em 00_fase0_exploracao.md.
docs/DECISIONS.md no formato | 2026-10-25 | Bloco N — <decisão> (decisão autônoma do Claude) | <alternativa descartada> | <motivo com número> |. Nunca apague linhas.python -m scorer.docs reescreve os blocos <!-- AUTO:nome -->…<!-- /AUTO:nome -->, e tests/test_docs_numbers.py confere os números da prosa.& $PY -m scorer.docs e & $PY -m pytest (cwd $SOL).solution/{data/processed,scorer,tests,app,briefs,tools} e process-log/{chat-exports,screenshots,outputs} (pasta vazia recebe .gitkeep); solution/requirements.txt, solution/requirements-dev.txt, solution/pytest.ini, process-log/outputs/bloco0_*.txt, process-log/chat-exports/LEIA-ME.md, docs/DECISIONS.md, process-log/00_bloco-0.md.$SUB\CLAUDE.md, docs\briefing.md e PROMPT_AUTONOMO.md.& $PY -c com pandas: sales_pipeline 8800, accounts 85, products 7, sales_teams 35. Se algo divergir, PARE e reporte sem seguir.requirements.txt: pandas>=2.2, numpy>=1.26, scipy>=1.11, pytest>=8 (uma por linha).requirements-dev.txt: playwright>=1.40.pytest.ini: [pytest] / testpaths = tests / pythonpath = . / addopts = -q.& $PY -m pip install -r "$SOL\requirements.txt" (tudo deve aparecer como “already satisfied”; o venv já existe fora do repo). Registre as versões com & $PY -m pip freeze.& $PY -B "$SUB\process-log\scripts\eda.py" "$SOL\data\raw" | Out-File -Encoding utf8 "$SUB\process-log\outputs\bloco0_eda.txt". O mesmo vale para eda2.py e snapshot.py. O planejador já confirmou que os três rodam no pandas 3.1 com exit 0. Confira contra o briefing e liste as divergências (espera-se nenhuma):
pct_won_of_all no snapshot de 30/09: 0.502 / 0.343 / 0.067 / 0 / 0; no de 30/06: 0.529 / 0.445 / 0.216 / 0.O snapshot.py trunca colunas na impressão; a coluna pct_won_of_all aparece. Para os números que os scripts não imprimem, rode um & $PY -c e salve em outputs/bloco0_conferencia.txt: linhas com product==”GTXPro” (esperado 1480) e a tabela completa do snapshot com pd.set_option("display.width",250) e max_columns 30.
docs/DECISIONS.md: cabeçalho | data | decisão | alternativa descartada | motivo | e |---|---|---|---|.
2026-10-25 (fase 0) e texto prefixado com “Fase 0 (kit do Rafael) —”. As alternativas e os motivos saem do CLAUDE.md, do briefing e do 00_fase0; por exemplo, Streamlit foi descartado porque é o default de uma entrega de IA sem supervisão.<pasta-mãe>\.venv, fora do repo;chat-exports/LEIA-ME.md: explique em 3–5 linhas por que não há exports (decisão 6) e onde está o registro (process-log/NN_bloco-N.md, DECISIONS.md, histórico de commits).process-log/00_bloco-0.md: siga o template do bloco comum, incluindo a tabela “número do briefing × número reproduzido × arquivo”.Test-Path é verdadeiro para todas as pastas e arquivos acima;(Select-String -Path "$SUB\docs\DECISIONS.md" -Pattern '^\| 2026').Count -ge 14;process-log\outputs existem e não estão vazios;& $PY "$PM\tools\gitw.py" status termina com “FORA da pasta do projeto: nada”.docs/SCOPE.md (novo), docs/DECISIONS.md (append), process-log/01_bloco-1.md (novo).$SUB\CLAUDE.md, docs\briefing.md, PROMPTS.md (bloco 1), PROMPT_AUTONOMO.md, process-log\00_fase0_exploracao.md;#vendedor=...) e reabre igual. A visão do manager é a mesma tela filtrada por manager ou região, mais uma tabela “Carteira do time” só com contagens (sem taxa de vitória por vendedor; decisão 4 do CLAUDE.md).docs/SCOPE.md (no máximo 2 páginas, cerca de 1.100 palavras), com as 7 seções do PROMPTS bloco 1:
data.js → HTML/CSS/JS sem build, file:// e GitHub Pages) e a tese “frescor > atributos”, com os números do briefing.python -m pytest passa, python -m scorer.build regenera tudo e index.html abre em file:// sem erro de console (verificado por script Playwright no bloco 4);Use só números que já estão no briefing ou no CLAUDE.md (no bloco 2 eles serão conferidos contra facts.json).
process-log/01_bloco-1.md: as 4 perguntas com recomendação, resposta e justificativa; a autoverificação (o PROMPTS mandava esperar o Rafael; aqui o Claude decidiu sozinho, conforme o PROMPT_AUTONOMO); e o que ficou fora do escopo.docs\SCOPE.md existe, com 7 títulos ## numerados;((Get-Content "$SUB\docs\SCOPE.md" -Raw) -split '\s+').Count -le 1300;01_bloco-1.md contém “Q1”, “Q2”, “Q3” e “Q4”;gitw.py status mostra “nada” fora da pasta.solution/scorer/{__init__.py, config.py, fmt.py, clean.py, facts.py, docs.py}solution/tests/{test_clean.py, test_docs_numbers.py}solution/data/processed/{deals.csv, facts.json}docs/DATA_QUALITY.md, docs/SCOPE.md (só para ajustar números, se o teste reclamar), docs/DECISIONS.md, process-log/02_bloco-2.mdprocess-log/scripts/*.py (referência de cálculo; não altere).config.py, com constantes e caminhos baseados em Path(__file__):
DEFAULT_REFERENCE_DATE="2017-12-31", PRODUCT_NAME_MAP={"GTXPro":"GTX Pro"}, SECTOR_MAP={"technolgy":"technology"};HORIZON_DAYS=90, FIRST_SNAPSHOT="2017-03-31", SENSITIVITY_FIRST_SNAPSHOT="2017-02-28";BACKTEST_CUTS=["2017-06-30","2017-07-31","2017-08-31","2017-09-30"], UI_BACKTEST_SNAPSHOTS=["2017-06-30","2017-09-30"];TOP_FRAC=0.20, RANDOM_SEEDS=200, BOOTSTRAP_REPS=1000, KNOT_GRID=range(0,181,5);ALLOWED_CONSTANTS: lista de números estruturais permitidos na prosa (90, 20, 200, 1000, 180, 100, …), cada um com comentário.fmt.py (formato PT-BR): fmt_int(1291)→"1.291", fmt_pct(0.632,1)→"63,2%", fmt_usd(4821)→"US$ 4.821", fmt_usd_compact(3198664)→"US$ 3,2 mi" (e “US$ 393 mil”), fmt_date("2017-12-31")→"31/12/2017".clean.py (funções puras, que nunca modificam a entrada):
load_raw(raw_dir);normalize_products(pipeline, catalog) usa o mapa e faz assert de que todo produto existe no catálogo;normalize_sectors(accounts);join_all(...): left joins pipeline ← products, sales_teams, accounts; assert de len == 8800, nenhum sales_price nulo, nenhum manager nulo;add_derived(df, reference_date):
age_days = (reference_date − engage_date).days só para deals abertos com engage_date; NaN no resto;cycle_days só para Won/Lost;has_account;is_open (Engaging ou Prospecting);account_display = account ou “Conta não informada”.python -m scorer.clean [--reference-date 2017-12-31] grava data/processed/deals.csv (utf-8) e chama facts.write_facts().facts.py gera data/processed/facts.json com TODOS os números de fase 0 que o briefing, o CLAUDE.md e o SCOPE citam:
Estrutura do JSON: chaves descritivas em snake_case, valores numéricos crus (frações como 0.632).
Investigações, com critério de decisão pré-definido (registre números e decisão no DATA_QUALITY, no log e no DECISIONS):
2a) Win rate de 82% em 2017-Q1.
close_date;2b) Vendedores sem deals.
2c) close_value / preço de tabela.
close_value = 0; nenhum Won com 0.sales_price; close_value só serve como desfecho no backtest.Vazamento, registrar já: todos os 1.425 sem conta estão abertos no fim. “Sem conta” equivale a “nunca fechou”, então has_account, ou qualquer categoria “desconhecido” de setor ou porte, é proibido como feature.
test_clean.py):
age_days correto num Engaging escolhido do CSV: o esperado é calculado no teste com datetime.date a partir da string crua;close_value 0 e todos os Lost com 0;engage_date;docs.py:
render_blocks() → dict nome→markdown (a partir de facts.json; no bloco 3 ganha backtest_metrics.json);inject(path) substitui o conteúdo entre <!-- AUTO:x --> e <!-- /AUTO:x --> e dá erro em marcador sem renderizador;python -m scorer.docs (escreve) e --check (exit 1 se algum doc estiver desatualizado).docs/DATA_QUALITY.md, docs/SCOPE.md e, depois, SCORING, MODEL_CARD e README.md. Fora da cobertura: briefing, DECISIONS e process-log.test_docs_numbers.py:
US\$\s?[\d.,]+(\s?(mi|mil))?;[\d.,]+\s?%;facts.json (e depois de backtest_metrics.json), ou de ALLOWED_CONSTANTS, arredondado para a mesma precisão, bater com ele. Frações também valem ×100 para porcentagens. A falha lista o arquivo, a linha e o token.facts.py (se for fato real da fase 0) ou corrija o texto. Registre no log.docs/DATA_QUALITY.md: tabela “problema → evidência (número) → tratamento → impacto”. Linhas:
close_value × tabela;As tabelas de evidência dos itens 2a, 2b e 2c ficam em blocos AUTO (dq-meses, dq-coortes, dq-trimestres, dq-razao-preco, dq-vendedores).
02_bloco-2.md, com a autoverificação: resumo 2a/2b/2c, critério × número × decisão. DECISIONS: linhas 2a, 2b, 2c, artefatos não explorados e a proibição de has_account.Set-Location $SOL; & $PY -m scorer.clean; & $PY -m scorer.docs; & $PY -m pytest; & $PY -m scorer.docs --check com exit 0;(Import-Csv "$SOL\data\processed\deals.csv").Count -eq 8800;facts.json contém as chaves das evidências 2a, 2b e 2c;gitw.py status mostra “nada” fora da pasta.solution/scorer/{features.py, score.py, explain.py}solution/tests/{test_features.py, test_score.py, test_explain.py}process-log/03_bloco-3.md (criar e começar)facts.json.Antes de escrever qualquer código, crie 03_bloco-3.md com:
(a) a seção “Planejamento (Opus) — protótipo exploratório”. O texto é o seguinte (ajuste a forma, não o conteúdo):
“Durante o planejamento, o Claude Opus (somente leitura) rodou protótipos descartáveis via
python -c, sem criar arquivos. (1) Uma logística segmentada sem restrição, treinada a partir de 28/02, gerou para o corte de 30/06 uma curva que SOBE com a idade até 90 dias (não monotônica), o que motivou a restrição monotônica e a regra de fotos ≥ 31/03. Essa regra foi adotada depois de ver o resultado, por isso 28/02 é reportado como sensibilidade. (2) Ordenar por recência é um baseline forte em AUC nos cortes de fim de trimestre. (3) Deals fecham no trimestre do engajamento ou no seguinte (artefato do gerador). (4) Entre 0 e 90 dias, a taxa de ganho em 90 dias fica estável, perto de 40%, e cai depois disso.”
(b) a seção “Checagem de vazamento — escrita antes do código”. Para cada risco, a guarda e o teste:
as_of(T) reconstrói o estado a partir das datas;has_account e a categoria “desconhecido” proibidos; teste de atributos de conta só no subconjunto com conta;close_value como valor → só sales_price;as_of(T);features.py:
month_ends(start, end) usa pd.date_range(start, end, freq="ME").as_of(deals, T):
engage_date não nula e ≤ T (Prospecting fica de fora);close_date nula ou > T): deal_stage="Engaging", close_date=NaT, close_value=NaN.open_at(view, S): engage_date ≤ S e (close_date NaT ou > S), mais a coluna age_days=(S−engage_date).days (int).training_set(view_T, T, horizon_days, first_snapshot):
month_ends(first_snapshot, T) com S+H ≤ T, pega open_at(view_T, S);y = (deal_stage=="Won") & (close_date ≤ S+H);snapshot_date, opportunity_id, age_days, product, sales_price, regional_office, sector, employees, has_account (só para diagnóstico), y;assert max(S)+H ≤ T.score.py.
Logística estável: fit_logistic(X, y, l2=0.0, bounds=None) com scipy.optimize.minimize(method="L-BFGS-B").
sum(logaddexp(0, Xb)) − y·Xb + l2/2·||b[1:]||²Xᵀ(σ(Xb)−y) + l2·[0, b[1:]]Curva de idade, “platô + queda”:
logit P(a) = b0 + b1 · max(0, a − k) / 30, com b1 ≤ 0
KNOT_GRID, ajuste (b0, b1) com o limite b1 ∈ (−∞, 0]. Escolha o k de menor NLL; em empate, o menor k.@dataclass(frozen=True) AgeCurve: knot_days, intercept, slope_per_30d, base_rate, train_rows, train_snapshots.
p_win(age);p_fresh = σ(b0);halving_days = 30·(logit(p_fresh/2) − b0)/b1 (dias depois do joelho até a chance cair à metade).fit_model(deals, T, cfg) → ScoreModel(curve, limpe_after_days, price_min, price_max, as_of_date):
limpe_after_days = maior cycle_days entre os Won com close_date ≤ T na as_of(T) (se for menor que k, use k);price_min e price_max vêm do catálogo products.csv.score_deals(deals, T, model, include_prospecting), para os Engaging em open_at(as_of(T), T):
p_win;expected_value = p_win × sales_price;score = round(100·ln(1+EV/EV_min)/ln(1+EV_max/EV_min)), limitado a [0, 100], com EV_min = p_fresh·price_min e EV_max = p_fresh·price_max;category: Foque se idade ≤ k; Reative se k < idade ≤ limpe_after_days; Limpe acima disso;days_to_knot, days_to_limpe.Prospecting só entra quando include_prospecting (data de referência = fim dos dados): category="Qualifique", p_win, EV e score nulos.
Ordenação (order_for_list, só para UI e briefs; as métricas usam o EV):
opportunity_id;sales_price desc → idade desc;sales_price desc.O vendedor não entra em nenhuma função.
explain.py. explain_deal(row, model) → list[str], de 2 a 4 frases curtas em PT-BR, números via fmt.py, sem a palavra “nan”.
how_it_works(model, headline_metrics) → 5 linhas:
test_features.py:
as_of não tem nenhuma close_date > T nem estágio Won/Lost com fechamento depois de T;training_set respeita S+H ≤ T;close_date > T (T = 2017-06-30), sorteie Won/Lost, close_value e close_date em (T, 2017-12-31] com semente fixa; embaralhe produto e vendedor dos engajados depois de T; apague account de deals aleatórios fechados depois de T. Então fit_model e os scores dos abertos em T ficam IDÊNTICOS aos do original.test_score.py:
sales_agent não muda nenhum score;test_explain.py: de 2 a 4 frases; o percentual citado é igual a round(100·p); sem “nan”; texto de conta ausente presente quando aplicável.Set-Location $SOL; & $PY -m pytest com exit 0;03_bloco-3.md tem as seções “Planejamento (Opus)” e “Checagem de vazamento”;& $PY -c "from scorer.score import *" funciona.solution/scorer/validate.py, solution/scorer/docs.py (estender), solution/scorer/explain.py (linha 5 do how_it_works)solution/tests/test_validate.pysolution/data/processed/{backtest_metrics.json, backtest_tables.md}docs/SCORING.md, docs/DECISIONS.md, process-log/03_bloco-3.md (completar)03_bloco-3.md, score.py, features.py e DATA_QUALITY.auc(score, y): Mann-Whitney com rank médio (scipy.stats.rankdata).top_share(score, gain, frac=0.2): fração de gain no top 20% por contagem, com tratamento fracionário de empates (valor esperado sob desempate aleatório). Algoritmo:
(K − acumulado)/tamanho do grupo que cruza K = frac·n.top_share com gain = y; “receita capturada” é top_share com gain = close_value dos Won.evaluate_cut(deals, T), para cada T em BACKTEST_CUTS:
fit_model(deals, T);open_at(as_of(T), T), incluindo zumbis;y = Won (até 31/12/2017), gain = close_value, y_H = Won & close_date ≤ T+90.Métodos:
sales_price;Calcule por corte:
y_H;as_of(2017-12-31) e avaliado no corte, rotulado VAZADO e nunca reportado como resultado; (ii) AUC de has_account sozinho no corte, mostrando por que é proibido.regional_office): todos os deals;employees calculado na tabela accounts): só no subconjunto com conta, tanto no treino quanto na avaliação, comparando com a curva só de idade ajustada no MESMO subconjunto.y_H e Δ receita no top 20% (P_attr × preço).Valores esperados pelo protótipo, para sanidade:
| Corte | Receita top 20% — score | valor | recência | AUC score | AUC recência |
|---|---|---|---|---|---|
| 30/06 | ≈0,61 | ≈0,49 | ≈0,28 | ≈0,66 | ≈0,73 |
| 31/07 | ≈0,58 | ≈0,49 | ≈0,13 | ≈0,64 | ≈0,58 |
| 31/08 | ≈0,61 | ≈0,48 | ≈0,32 | ≈0,63 | ≈0,66 |
| 30/09 | ≈0,65 | ≈0,47 | ≈0,47 | ≈0,65 | ≈0,83 |
Aleatório ≈ 0,20 em todos os cortes. Limpe teve 0% de ganhos em todos os cortes; Foque, entre 33% e 49%.
run_backtest() grava data/processed/backtest_metrics.json:
config;criterion: regra, passed, adjustment_applied;cuts[]: modelo do corte, métodos, Brier, bootstrap, categorias, resolvidos;media_cortes;sensibilidade_2017-02-28;auditoria;atributos[];hoje: modelo em 2017-12-31 e categorias com n, valor de tabela e valor esperado, incluindo Qualifique.Também grava backtest_tables.md. CLI: python -m scorer.validate imprime a tabela principal e PASSOU/FALHOU.
explain.how_it_works, linha 5: “Testado no passado em {n} datas: o top 20% do score capturou {r}% da receita ganha depois, contra {v}% ordenando por valor e {rec}% pelos mais recentes. Vendedor não entra no score.” Números da média dos cortes.docs/SCORING.md. Seções:
| Resultados: tabela principal (corte × método: AUC | ganhos capturados no top 20% | receita capturada no top 20%), média, categorias, calibração e bootstrap |
Todas as tabelas em AUTO: scoring-hoje, scoring-modelos, scoring-backtest, scoring-media, scoring-categorias, scoring-calibracao, scoring-bootstrap, scoring-sensibilidade, scoring-auditoria, scoring-atributos. Estenda test_docs_numbers para cobrir o SCORING e backtest_metrics.json.
test_validate.py:
auc perfeita = 1, invertida = 0, tudo empatado = 0,5;top_share com tudo empatado = 0,2;passed.03_bloco-3.md: autoverificação (a tabela do backtest copiada do backtest_tables.md), PASSOU/FALHOU, o ajuste (se houve), o que falhou de verdade, e as perguntas de auditoria respondidas com números (vazamento, has_account, 28/02, resolvidos). DECISIONS: H=90; primeira foto; curva; grade de joelhos; limite Limpe; escala log; métrica primária; ajuste único; atributos descartados; tratamento da censura.Set-Location $SOL; & $PY -m scorer.validate; & $PY -m scorer.docs; & $PY -m pytest; & $PY -m scorer.docs --check com exit 0;backtest_metrics.json tem 4 entradas em cuts e a chave criterion.passed;gitw.py status mostra “nada” fora da pasta.data.js, interface e briefssolution/scorer/build.pysolution/app/{index.html, style.css, app.js, data.js (gerado)}solution/briefs/*.md (gerados)solution/data/processed/snapshot_{2017-12-31,2017-09-30,2017-06-30}.csvsolution/tests/test_build.pybacktest_metrics.json, score.py e explain.py.build.py: CLI python -m scorer.build --reference-date 2017-12-31 [--out-root SOL]. “Do zero” significa:
app/data.js, briefs/*.md e data/processed/snapshot_*.csv;deals.csv e facts.json);run_backtest (grava as métricas);UI_BACKTEST_SNAPSHOTS ≤ referência: fit_model do corte, score_deals, explain_deal, e o desfecho real nos snapshots de teste (Ganho com data e valor / Perdido / Ainda aberto em 31/12/2017);gravar os CSVs de snapshot (colunas: id, vendedor, manager, região, conta, produto, preço, engage_date, idade, p, EV, score, categoria, days_to_*, explicação unida por “ |
”, desfecho); |
data.js;docs.inject em todos os docs;data.js (meta: < 4 MB).data.js é igual a backtest_metrics.json["hoje"], e as categorias dos snapshots de teste são iguais às do corte correspondente.data.js: window.LEAD_DATA = {...}; com json.dumps(ensure_ascii=False, separators=(",",":")), em utf-8:
{"meta":{"reference_date","horizon_days","generated_by":"python -m scorer.build --reference-date 2017-12-31"},
"people":{"agents":[{"name","manager","region","has_deals","n_open"}],"managers":[{"name","region"}],"regions":[...]},
"snapshots":[{"date","mode":"hoje"|"teste","model":{knot_days,p_fresh,halving_days,limpe_after_days,...},
"how_it_works":[5 linhas],"summary":{Cat:{n,valor_tabela,valor_esperado}},
"outcome_summary":{Cat:{n,pct_ganhos,receita_ganha}} (só teste),
"deals":[{"id","agent","manager","region","account"(null se ausente),"product","price","engage_date","age","p","ev","score","cat","why":[...],"days_to_knot","days_to_limpe","outcome":null|{"status","close_date","close_value"}}]}],
"backtest":{"rows":[...tabela média e por corte para o painel "Teste no passado"]}}
Nenhum número fica escrito à mão em app.js ou index.html; todo texto com número vem do data.js.
fetch, sem framework). index.html com lang="pt-BR", meta charset utf-8, viewport, e <script src="data.js"></script><script src="app.js"></script> no fim do body. Layout, de cima para baixo, com a AÇÃO antes dos números:
| Barra compacta: “Lead Scorer” + seletor de modo (“Hoje — 31/12/2017” | “Teste: 30/09/2017” | “Teste: 30/06/2017”) + link “Como funciona o score”. |
select com optgroup por manager; os 5 sem deals aparecem com “(sem deals no CRM)”;location.hash (URLSearchParams), o que funciona em file:// e no Pages.| Quatro abas, que também são os KPIs: Foque | Reative | Limpe | Qualifique, cada uma com contagem (data-count) e valor. Cada aba abre com uma linha-instrução com verbo de ação. |
outcome_summary). Selo de desfecho em cada card. A aba Qualifique mostra “Indisponível no modo teste: o CRM não registra quando o deal entrou em Prospecting.”<details> “Como funciona o score”, com as 5 linhas do snapshot ativo.Outros requisitos:
window.LEAD_DATA não existir, mostre a mensagem “Rode python -m scorer.build”.Intl.NumberFormat('pt-BR'); datas dd/mm/aaaa.data-testid obrigatórios: mode-select, filter-agent, filter-manager, filter-region, filter-clear, action-summary, count-Foque, count-Reative, count-Limpe, count-Qualifique (com data-count), tab-<Cat>, deal-card (com data-id), card-why, outcome-badge, backtest-summary-<Cat> (com data-n e data-pct), how-it-works, team-table.briefs/<slug>.md (slug = nome em minúsculas com hífen) para cada vendedor com ≥ 1 deal aberto. Conteúdo:
briefs/README.md traz o índice (vendedor, manager e contagens) e lista os vendedores sem deals ou sem deals abertos.
test_build.py roda o build com --out-root num tmp_path (copiando data/raw). Reduza o bootstrap via parâmetro se ficar lento. Confere:
data.js existe e faz parse (tirando o prefixo window.LEAD_DATA = e o ; final);backtest_metrics["hoje"];data.js idêntico.index.html uma vez com Playwright só para ver se carrega; a verificação completa fica na E7. Comece o 04_bloco-4.md com o que foi feito.Set-Location $SOL; & $PY -m scorer.build --reference-date 2017-12-31; & $PY -m pytest; & $PY -m scorer.docs --check com exit 0;Test-Path "$SOL\app\data.js";(Get-ChildItem "$SOL\briefs\*.md").Count -ge 28 (27 briefs + README; confirme o número real);gitw.py status mostra “nada” fora da pasta.solution/tools/check_app.py, process-log/screenshots/*.png, solution/app/* (correções), process-log/04_bloco-4.md, docs/DECISIONS.md.tools/check_app.py, reutilizável. Uso: & $PY tools\check_app.py --prefix antes|depois|final (cwd $SOL). Exit 1 se qualquer checagem falhar. Comportamento:
Path("app/index.html").resolve().as_uri() com sync_playwright e chromium.launch(channel="msedge", headless=True); se o Edge não existir, cai para o Chromium padrão.console do tipo error, pageerror e requestfailed, e exige zero.window.LEAD_DATA via page.evaluate.data-count de cada lista é igual a backtest_metrics.json["hoje"] e o bloco AUTO:scoring-hoje do SCORING contém essas contagens formatadas;snapshot_2017-12-31.csv), a contagem por lista é igual à do CSV filtrado; o mesmo vale para 1 manager e 1 região;data-n e data-pct de cada lista são iguais às categorias do corte no JSON;$SUB\process-log\screenshots\{prefix}_{vista}_{largura}.png, nas larguras 1280×800 e 390×844, para as vistas inicio, vendedor, card-aberto, modo-teste e como-funciona.--prefix antes e corrija as falhas de checagem. Isso é bug, não atrito; registre com o antes e o depois.antes_* com a ferramenta Read e simule as tarefas, contando cliques e rolagem:
Para cada tarefa, anote o que atrapalhou, com evidência (nome do PNG e o que se vê). Não use atritos pré-escritos. Escolha os 3 atritos reais mais importantes; olhe, por exemplo, para: ação abaixo da dobra, jargão, número sem unidade, texto cortado em 390px, cliques demais, ordem confusa e explicação que não responde ao “por quê”.
--prefix depois e confirme as checagens verdes. Os pares antes_* e depois_* da mesma vista são a evidência.--prefix final para os screenshots finais, em 390 e 1280.04_bloco-4.md: o que foi construído; o resultado das checagens; a tabela de atritos (atrito → evidência antes → correção → evidência depois); os bugs; e a autoverificação do checkpoint. DECISIONS: estrutura do data.js, estado no hash, tabela do time sem taxas, as 3 correções.Set-Location $SOL; & $PY -m scorer.build --reference-date 2017-12-31; & $PY -m pytest; & $PY tools\check_app.py --prefix final com exit 0;final_*_390.png e final_*_1280.png, e ≥ 3 pares antes_/depois_;04_bloco-4.md lista 3 atritos com nomes de arquivo;gitw.py status mostra “nada” fora da pasta.docs/MODEL_CARD.md, README.md (em $SUB), solution/scorer/docs.py (blocos novos), solution/tests/test_readme.py, docs/DECISIONS.md, process-log/05_bloco-5.md.docs/ e de process-log/, o modelo de README do cenário e o backtest_metrics.json.MODEL_CARD.md (1 página):
mc-desempenho com a média dos cortes e o pior corte;README.md seguindo EXATAMENTE o modelo de README do cenário: mesmos títulos, mesma ordem, separadores ---, a tabela de ferramentas e o checklist de evidências. Os placeholders em itálico são substituídos por conteúdo.
# Lead Scorer — Rafael Pereira## Sobre mim: Nome “Rafael Pereira”; LinkedIn https://www.linkedin.com/in/rafael-pereira-30122003-; projeto “Lead Scorer”## Executive Summary## Solução → ### Como rodar (setup) (subseção permitida pelo “use o formato que melhor comunicar”) → ### Abordagem → ### Resultados / Findings → ### Recomendações → ### Limitações## Process Log — Como usei IA → ### Ferramentas usadas → ### Workflow → ### Onde a IA errou e como corrigi → ### O que eu adicionei que a IA sozinha não faria## Evidências_Publicado em: [preencher ao publicar]_https://<seu-usuario>.github.io/analise-officers/solution/app/ [ativar — Rafael];solution/app/index.html;cd solution, pip install -r requirements.txt, python -m pytest, python -m scorer.build --reference-date 2017-12-31;pip install -r requirements-dev.txt e python tools/check_app.py --prefix final.readme-backtest (média e cada corte: score, valor, recência e aleatório na receita no top 20%, mais AUC); readme-hoje (contagens e valores das 4 listas); 2 screenshots final_* linkados.process-log/ e DECISIONS. Os da fase 0 são marcados “(fase 0, com Rafael)”; os desta execução, “(autocorreção na execução autônoma)”;[RASCUNHO — Rafael revisar], baseado só no 00_fase0_exploracao.md: escolha do 003 pelo ângulo de governança; não contornar o bloqueio do Kaggle; exigir backtest temporal; vendedor fora do score; 138 dias não vira regra; model card.docs.py e em test_docs_numbers. test_readme.py:
[.!?](\s|$), que não quebra “1.291”);[RASCUNHO — Rafael revisar];05_bloco-5.md cada divergência encontrada e corrigida. DECISIONS: estrutura do README, setup dentro de Solução, conteúdo do model card.Set-Location $SOL; & $PY -m scorer.build --reference-date 2017-12-31; & $PY -m pytest; & $PY -m scorer.docs --check com exit 0;Select-String -Path "$SUB\README.md" -SimpleMatch "[RASCUNHO — Rafael revisar]" encontra o texto;Test-Path "$SUB\docs\MODEL_CARD.md";gitw.py status mostra “nada” fora da pasta.process-log/06_revisao-independente.md (seção “Revisão 1”, texto integral).O que fazer: dispare o subagente passando SÓ três caminhos: a descrição do cenário, o guia de entrega do cenário e $SUB. Prompt sugerido:
Você é revisor independente e já viu entregas geradas por IA sem supervisão. Avalie este projeto contra cada critério de qualidade (funciona de verdade; o scoring faz sentido e vai além do óbvio; um vendedor não técnico usa e entende; a interface ajuda a decidir; código mantível) e também documentação e process log, com nota de 1 a 5 e justificativa. Siga as instruções de setup do README ao pé da letra e diga se funcionou. O Python já existe em
<PY>: use-o e não crie venv. Abrasolution/app/index.htmlemfile://com Playwright (chromium.launch(channel="msedge", headless=True)) e veja o console. Não edite nenhum arquivo. Aponte os 5 pontos mais fracos e o que pareceria uma entrega de IA sem supervisão.
Salve o relatório sem filtro.
process-log/06_revisao-independente.md (seção “Correções aplicadas”); docs/DECISIONS.md.docs --check e check_app.py --prefix final.& $PY tools\check_app.py --prefix final com exit 0; a seção “Correções aplicadas” lista critério → mudança → arquivo.process-log/06_revisao-independente.md (seção “Revisão 2”, texto integral, mais uma tabela comparando as notas 1 × 2).process-log/06_bloco-6.md, docs/DECISIONS.md; fora do repo, como permitido pelo PROMPT_AUTONOMO §6, $PM\kit-lead-scorer\PUBLICAR.md.$SOL\app\data.js, $SOL\briefs\*.md e $SOL\data\processed\snapshot_*.csv; rode & $PY -m scorer.build --reference-date 2017-12-31; confirme que todos foram recriados. Depois rode & $PY -m pytest, & $PY -m scorer.docs --check e & $PY tools\check_app.py --prefix final.06_bloco-6.md, cada item com o comando e o resultado:
docs/ tem os 5 arquivos;process-log tem os logs 00 a 06, os screenshots e as 2 revisões;gitw.py status não mostra nada fora da pasta.Item que falhar: reporte ao orquestrador, sem consertar.
PUBLICAR.md:
git remote add origin https://github.com/<seu-usuario>/analise-officers.git e git push -u origin <branch-de-trabalho>;/ (root); a URL fica em …/solution/app/.Test-Path "$PM\kit-lead-scorer\PUBLICAR.md"; gitw.py status mostra “nada” fora da pasta.has_account) provam que a guarda funciona.str, freq="ME", datetime64[us], JSON com tipos numpy, encoding UTF-8, caminhos com espaço, URI file:// via Path.as_uri(). Os scripts da fase 0 rodam no pandas 3.1 (verificado).gitw.py commit adiciona tudo o que está na pasta (exceto caches). Se algum artefato carregar timestamp, todo build vira diff, e a revisão independente (que roda o build) suja o status. Os executores não podem criar arquivos temporários dentro de $SUB; o tmp_path do pytest fica fora.$PY. Depois de cada revisão, rode gitw.py status.data.js: cerca de 6.200 deals com explicações dão de 2 a 4 MB. Se passar de 4 MB, use chaves curtas ou explicações só no snapshot ativo. Não troque por fetch.process-log/plano_execucao_opus.md antes do commit do bloco 0 (é a evidência do “Plan Mode”);