<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tiago Vilas Boas (Montanha)</title>
    <description>The latest articles on DEV Community by Tiago Vilas Boas (Montanha) (@tiagovilasboas).</description>
    <link>https://dev.to/tiagovilasboas</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F469157%2F64c4bbaf-e836-4c4b-a643-dfef1fa7353e.png</url>
      <title>DEV Community: Tiago Vilas Boas (Montanha)</title>
      <link>https://dev.to/tiagovilasboas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tiagovilasboas"/>
    <language>en</language>
    <item>
      <title>Como o Sentry passou a mostrar o funil. A jornada antes do stacktrace</title>
      <dc:creator>Tiago Vilas Boas (Montanha)</dc:creator>
      <pubDate>Tue, 15 Sep 2026 16:05:39 +0000</pubDate>
      <link>https://dev.to/tiagovilasboas/como-o-sentry-passou-a-mostrar-o-funil-a-jornada-antes-do-stacktrace-1ml6</link>
      <guid>https://dev.to/tiagovilasboas/como-o-sentry-passou-a-mostrar-o-funil-a-jornada-antes-do-stacktrace-1ml6</guid>
      <description>&lt;p&gt;Pessoal, o SDK tava verde. Chegava TypeError. Eu não sabia se era o pagar, o cupom ou o pixel.&lt;/p&gt;

&lt;p&gt;Dashboard cheio. Três stacks iguais. A próxima pessoa abria o evento e ia pro arquivo no bundle.&lt;/p&gt;

&lt;p&gt;O denso do &lt;a href="https://dev.to/tiagovilasboas/como-o-checkout-ganhou-10-de-conversao-no-mes-o-case-que-pesou-na-promocao-a-staff-ii-71"&gt;+10% no checkout&lt;/a&gt; é o dinheiro. Este texto é o recorte que faz o Sentry falar a mesma língua: a ação da pessoa, não o SDK.&lt;/p&gt;

&lt;p&gt;O ponto é: se o filtro não nomeia o passo, você instalou. Não instrumentou.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tabela de Conteúdo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. Sintoma: o tracker gritava, o funil não&lt;/li&gt;
&lt;li&gt;2. Causa: instalar não é instrumentar&lt;/li&gt;
&lt;li&gt;3. O que mudou: jornada no evento, não só a stack&lt;/li&gt;
&lt;li&gt;4. Como eu medi: o filtro da próxima pessoa&lt;/li&gt;
&lt;li&gt;5. Resultado: o filtro passou a ser a jornada&lt;/li&gt;
&lt;li&gt;6. O que copiar no próximo alerta&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Sintoma: o tracker gritava, o funil não
&lt;/h2&gt;

&lt;p&gt;O SDK tava instalado. Volume de evento subia. Ninguém decidia.&lt;/p&gt;

&lt;p&gt;Um TypeError em massa, sem domínio e sem a ação, só prova que o JS quebrou. Não prova se o comprador estava no pagar, no cupom ou no pixel que nem deveria derrubar o checkout. O filtro no Discover era o arquivo minificado. Não era a jornada.&lt;/p&gt;

&lt;p&gt;Produto olhava conversão. Engenharia olhava stack. Os dois boards não se falavam. Timeout na hora de pagar e pixel quebrado depois da venda viravam o mesmo card.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Causa: instalar não é instrumentar
&lt;/h2&gt;

&lt;p&gt;Auto-instrumentation pega HTTP, crash, às vezes replay. Não pega “a pessoa apertou pagar e o request nem saiu”. Esse é o furo do denso: timeout no first load, catch silencioso, UI genérica, back sem request.&lt;/p&gt;

&lt;p&gt;Catch que só faz &lt;code&gt;console.error&lt;/code&gt;: a tela mostra “tente de novo”, o gateway nem foi chamado, o Sentry não vê. Você instalou. Não instrumentou.&lt;/p&gt;

&lt;p&gt;Não é pitch de vendor. Grafana, Sentry, Datadog: a ferramenta é o adapter. O domínio emite o evento. Se a regra de cupom importa &lt;code&gt;Sentry.captureException&lt;/code&gt; no meio do &lt;code&gt;if&lt;/code&gt;, você inverteu. A regra não deveria saber o nome da ferramenta.&lt;/p&gt;

&lt;p&gt;Form incompleto, cupom expirado, cartão recusado: a regra funcionou. Breadcrumb. Não incidente. Se misturar com gateway 500, o board grita e você trata recusa de cartão como queda de conversão.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. O que mudou: jornada no evento, não só a stack
&lt;/h2&gt;

&lt;p&gt;Eu parei de mandar stack nua. Passei a mandar a ação.&lt;/p&gt;

&lt;p&gt;Quatro campos. Não uma taxonomia de 40 tags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;domain&lt;/code&gt;: checkout, payment, lead, pixel&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;phase&lt;/code&gt;: a ação (&lt;code&gt;coupon.apply&lt;/code&gt;, &lt;code&gt;payment.updateCard&lt;/code&gt;, &lt;code&gt;checkout.pay.submit&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;tipo: exception vs resultado de negócio&lt;/li&gt;
&lt;li&gt;sem &lt;code&gt;user_id&lt;/code&gt; em label de métrica (explode cardinalidade)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;IDs de pedido e de pessoa: log e trace. Não série do Prometheus.&lt;/p&gt;

&lt;p&gt;Alerta de sintoma de usuário: o pagar não completa. Não cada &lt;code&gt;if&lt;/code&gt; da regra de desconto. Replay, se existir, no caminho curto do dinheiro. Admin longo não precisa da mesma cota.&lt;/p&gt;

&lt;p&gt;Exemplo sintético, de propósito. Dois eventos iguais na stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A  extra.phase = checkout.pay.submit     → o POST de pagar nem saiu
B  extra.phase = pixel.purchase.track    → o pixel quebrou depois da venda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A é receita em risco. B é ruído com a venda já feita. Sem &lt;code&gt;phase&lt;/code&gt;, os dois viram o mesmo card. Com &lt;code&gt;phase&lt;/code&gt;, o Sentry nomeia o passo. Aí dá pra olhar o mesmo funil que o painel.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Como eu medi: o filtro da próxima pessoa
&lt;/h2&gt;

&lt;p&gt;A prova não é “temos Sentry”. É: a próxima pessoa filtra a ação do usuário antes de abrir o stacktrace.&lt;/p&gt;

&lt;p&gt;Cola no Discover. Sintético, de propósito. Sem org, sem issue ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;extra.phase:checkout.pay.submit AND (
  error.type:TimeoutError OR message:*timeout* OR mechanism:*timeout*
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O teste: a próxima pessoa vê o pagar. Não o arquivo minificado. Pixel depois da venda é outro &lt;code&gt;phase&lt;/code&gt; (&lt;code&gt;pixel.purchase.track&lt;/code&gt;). Se os dois caem no mesmo card, você instalou. Não instrumentou.&lt;/p&gt;

&lt;p&gt;No Datadog, se o caminho até o JS saturava. No backoffice, se vendeu. Lighthouse fora dessa conta: lab.&lt;/p&gt;

&lt;p&gt;Se o alerta não cabe numa frase (“o pagar não completa”), ainda é volume. Não é funil.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Resultado: o filtro passou a ser a jornada
&lt;/h2&gt;

&lt;p&gt;Eu não fechei o +10% neste texto. Fechei se a próxima pessoa filtrava &lt;code&gt;checkout.pay.submit&lt;/code&gt; antes de abrir o arquivo minificado.&lt;/p&gt;

&lt;p&gt;Quando dava, timeout de pagar e conversão do mês apontavam pro mesmo lugar. Quando não dava, o painel subia e o Sentry gritava pixel.&lt;/p&gt;

&lt;p&gt;O JS chegar a tempo está no &lt;a href="https://dev.to/tiagovilasboas/como-o-checkout-ganhou-10-de-conversao-no-mes-o-case-que-pesou-na-promocao-a-staff-ii-71"&gt;denso&lt;/a&gt;. Aqui o contrato é só este: o timeout aparece no mesmo dia, no recorte que o passo entende.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. O que copiar no próximo alerta
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. O último evento tem domain + phase, ou só a stack?
2. Dá pra nomear a ação do usuário em uma frase?
3. Exception ou resultado de negócio (cupom, recusa, form)?
4. O alerta é sintoma de usuário (pagar não completa) ou qualquer JS?
5. user_id foi pra log/trace, ou virou label de métrica?
6. Esse recorte conversa com conversão, ou só com o board de erro?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Último alerta que você abriu: consegue nomear a ação do usuário em uma frase? Sim, não, ou só o stack?&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>monitoring</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>+10% de conversão no checkout no mês. A campanha quebrava na hora de pagar</title>
      <dc:creator>Tiago Vilas Boas (Montanha)</dc:creator>
      <pubDate>Tue, 15 Sep 2026 16:05:38 +0000</pubDate>
      <link>https://dev.to/tiagovilasboas/como-o-checkout-ganhou-10-de-conversao-no-mes-o-case-que-pesou-na-promocao-a-staff-ii-71</link>
      <guid>https://dev.to/tiagovilasboas/como-o-checkout-ganhou-10-de-conversao-no-mes-o-case-que-pesou-na-promocao-a-staff-ii-71</guid>
      <description>&lt;p&gt;Pessoal, tinha campanha de influenciador no ar. Eu abria o Sentry e via timeout. O canal falava de Lighthouse.&lt;/p&gt;

&lt;p&gt;Checkout de infoproduto, alto volume, micro frontend. O link saía, o pico chegava junto, e o passo de pagar transbordava. A sessão morria. No dia seguinte o painel caía e a conversa era score. Ou Nuxt.&lt;/p&gt;

&lt;p&gt;Aí o backoffice do cliente fechou o mês. &lt;strong&gt;+10% de conversão.&lt;/strong&gt; Pico nessas campanhas. Métrica de venda, cruzada com o que eu via no Sentry (jornada de pagar) e no Datadog (o caminho até o arquivo). O JS chegou a tempo. O timeout apareceu no mesmo dia.&lt;/p&gt;

&lt;p&gt;O ponto é: o Lighthouse verde não pagou. O POST de pagar, sim.&lt;/p&gt;

&lt;p&gt;Um limite só: não tenho o print do Lighthouse daquela janela no arquivo público. O +10% veio do painel.&lt;/p&gt;

&lt;p&gt;Como etiquetar o Sentry pra isso não virar TypeError órfão fica no &lt;a href="https://dev.to/tiagovilasboas/como-o-sentry-passou-a-mostrar-o-funil-a-jornada-antes-do-stacktrace-1ml6"&gt;complementar&lt;/a&gt;. Aqui é o dinheiro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tabela de Conteúdo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. Sintoma: a campanha quebrava na hora de pagar&lt;/li&gt;
&lt;li&gt;2. Causa: o MFE baixava o time inteiro&lt;/li&gt;
&lt;li&gt;3. O que mudou: SSR, JS sob demanda, CDN&lt;/li&gt;
&lt;li&gt;4. Como eu medi: quatro lentes&lt;/li&gt;
&lt;li&gt;5. Resultado: +10% no mês&lt;/li&gt;
&lt;li&gt;6. O que copiar no próximo PR&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Sintoma: a campanha quebrava na hora de pagar
&lt;/h2&gt;

&lt;p&gt;Checkout de infoproduto no organic é uma fila educada. Na campanha de influenciador não é. O link sai, o pico chega junto, e o passo de pagar ou aguenta o first load ou transborda.&lt;/p&gt;

&lt;p&gt;Timeout, pra mim, era o browser não terminar o trabalho a tempo. A pessoa já tinha escolhido o produto. Muitas vezes o pixel de view já tinha ido. Faltava o POST de pagamento. Se o JS daquele passo ainda tava puxando o mundo, a sessão morria. O backoffice nem via a tentativa. No dia seguinte alguém falava “a campanha foi ruim”.&lt;/p&gt;

&lt;p&gt;Transbordo é o irmão: origem fria, fila, saturação, bundle grande demais pro celular na 4G. Os dois matam a mesma linha do painel.&lt;/p&gt;

&lt;p&gt;No canal a resposta fácil era stack. Vue 3, Nuxt, Lighthouse verde. Eu fiz isso também. Nenhum desses, sozinho, prova que vendeu. O Lighthouse roda no lab, no cabo, num CPU que não é o do comprador no horário da live.&lt;/p&gt;

&lt;p&gt;Se o teu checkout só dói no organic, o bundle inchado passa. Dói quando o influenciador manda o link.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Causa: o MFE baixava o time inteiro
&lt;/h2&gt;

&lt;p&gt;O checkout era um micro frontend. Vários times, várias fatias, um único caminho de pagamento. Isso é bom pra merge. Time de cupom não espera time de pixel. MFE resolve deploy. Não resolve o que o celular baixa nos primeiros dois segundos.&lt;/p&gt;

&lt;p&gt;O que rolava: o host puxava o shell e, se ninguém olhasse o grafo, cada fatia trazia o que era dela e um pedaço do vizinho. Runtime duplicado. Pixel. Upsell. Modal. Às vezes um resto de área logada. O comprador, no passo de pagar, baixava isso antes de ver o botão.&lt;/p&gt;

&lt;p&gt;SPA seca piora. HTML quase vazio, JS inteiro do MFE, parse, hidrata, aí o botão existe. No Wi-Fi do escritório passa. Na live, no celular, é timeout.&lt;/p&gt;

&lt;p&gt;Pra mim a conta virou uma frase só: o passo atual baixa o JS do passo atual. O resto espera o clique.&lt;/p&gt;

&lt;p&gt;“A gente tem MFE” descreve o organograma. Não descreve o Network daquele passo. Se você abre o DevTools no pagar e ainda vê chunk de dashboard, o MFE não te salvou. Ele te faturou o first load.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. O que mudou: SSR, JS sob demanda, CDN
&lt;/h2&gt;

&lt;p&gt;Tinha BFF. Por isso SSR era opção de verdade, não slide. Sem BFF, o browser monta o pagar com vários round-trips e um bundle que hidrata tudo. Com BFF, o servidor já conhece o passo. Dá pra mandar HTML que serve pra pessoa ver preço, meio e botão sem esperar o mundo hidratar.&lt;/p&gt;

&lt;p&gt;Foi isso que eu fiz no first paint do crítico. O JS ainda hidrata o que o POST precisa. Não hidrata o dashboard no meio do caminho.&lt;/p&gt;

&lt;p&gt;Vue 2 pra Vue 3 / Nuxt 3 entrou aqui. Não porque “a lib nova é mais rápida” no vazio. Porque o legado não entregava SSR e split de verdade no MFE com a mesma facilidade. E eu não virei o checkout inteiro num dia. As rotas que a campanha batia foram pro stack novo. O resto ficou no legado. Roteador de borda decide. Sem big bang.&lt;/p&gt;

&lt;p&gt;SSR sozinho não basta. Se a hidratação ainda manda o MFE inteiro, você só adiou o timeout.&lt;/p&gt;

&lt;p&gt;Aí entra JavaScript sob demanda. Code splitting é o bundler gerar mais de um arquivo. Lazy load é o browser só pedir o arquivo quando o passo precisa. Os dois andam juntos. Split sem lazy: você ainda baixa os chunks “por precaução”. Lazy sem split de verdade:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PayStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./PayStep.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se o &lt;code&gt;PayStep.vue&lt;/code&gt; no topo importa upsell, chart e admin, o &lt;code&gt;import()&lt;/code&gt; é teatro. O chunk do pagar ainda é o mundo.&lt;/p&gt;

&lt;p&gt;O teste não é ter &lt;code&gt;import()&lt;/code&gt; no PR. É abrir o Network no passo de pagar e não ver o JS do admin. Order bump, meio extra, modal que quase ninguém abre: depois do clique.&lt;/p&gt;

&lt;p&gt;Tem um furo irmão: cada fatia do MFE traz o Vue de novo. Você splittou o app e duplicou o runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  Onde isso invertia tudo
&lt;/h3&gt;

&lt;p&gt;SSR na tela interna. Bundle único no checkout. O Lighthouse do admin ficava lindo. A campanha caía. A regra que ficou: SSR quando o first paint &lt;em&gt;é&lt;/em&gt; pagar. Lazy quando o código só existe depois do clique.&lt;/p&gt;

&lt;p&gt;Depois do split, o arquivo ainda precisa chegar. Eu já tinha cortado o JS e o timeout continuava em região. O chunk do PayStep saía longe, ou frio, ou sem compressão.&lt;/p&gt;

&lt;p&gt;CDN perto de quem compra. Cache longo no estático com hash no nome. Gzip no que sobrou. Brotli se o edge já tiver, sem religião. Se o lazy sai de origem fria, você não fez lazy load. Fez “espera a origem acordar”. O spinner só mudou de lugar. O POST continua sem sair.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Como eu medi: quatro lentes
&lt;/h2&gt;

&lt;p&gt;Eu parei de tratar ferramenta como tese. Trato como pergunta.&lt;/p&gt;

&lt;p&gt;O hábito que ficou: no pico, eu abria o Network no pagar &lt;em&gt;antes&lt;/em&gt; do Lighthouse. Se tinha chunk de dashboard, o PR de bundle ainda não tinha acabado.&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;Sentry&lt;/strong&gt; pergunta: o timeout foi no pagar, ou foi pixel quebrado depois da venda? Sem a ação do usuário no evento, os dois viram o mesmo TypeError. Aí você trata ruído como incidente, ou trata pagar que não saiu como “JS genérico”. O contrato disso está no &lt;a href="https://dev.to/tiagovilasboas/como-o-sentry-passou-a-mostrar-o-funil-a-jornada-antes-do-stacktrace-1ml6"&gt;outro texto&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;Datadog&lt;/strong&gt; pergunta: o caminho até o arquivo e até o POST tava saturado? Sentry conta o erro da jornada. Datadog conta se a origem ajoelhou. Timeout com origem ok é bundle. Origem ajoelhada com bundle ok é infra. No pico os dois acontecem. Uma lente só te faz otimizar a errada.&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;Lighthouse&lt;/strong&gt; pergunta: no lab, o que está bloqueando o first paint? Eu coletei. Serve pra hipótese. Não fecha receita. Não tem influenciador. Não tem a fila da CDN.&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;backoffice&lt;/strong&gt; pergunta: vendeu? Sem a métrica de venda, bundle menor é hipótese. Essa pergunta fecha o mês. A resposta está no próximo bloco.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recibo (o que fecha, o que não linko)
&lt;/h3&gt;

&lt;p&gt;PRs e dashboards reais ficam atrás de NDA. O que o leitor consegue checar é este recorte. O +10% é o número do painel. O resto é o hábito + o filtro. Sem print forjado.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lente&lt;/th&gt;
&lt;th&gt;O que eu olhava&lt;/th&gt;
&lt;th&gt;O que este post pode mostrar&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Backoffice&lt;/td&gt;
&lt;td&gt;conversão do mês, pico na campanha&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;+10%&lt;/strong&gt; (número do cliente)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sentry&lt;/td&gt;
&lt;td&gt;timeout no passo de pagar, não TypeError órfão&lt;/td&gt;
&lt;td&gt;query abaixo (sintético)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datadog&lt;/td&gt;
&lt;td&gt;TTFB / saturação / região no chunk do pagar&lt;/td&gt;
&lt;td&gt;o que eu perguntava&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lighthouse&lt;/td&gt;
&lt;td&gt;first paint no lab&lt;/td&gt;
&lt;td&gt;limite: print não arquivado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Código&lt;/td&gt;
&lt;td&gt;split, SSR, CDN&lt;/td&gt;
&lt;td&gt;PRs internos. Não linko&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Network no &lt;code&gt;/pagar&lt;/code&gt;. Sintético, de propósito. Sem kB inventado. O furo é a &lt;em&gt;presença&lt;/em&gt; do arquivo, não um score:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;antes (furo)
  PayStep.js
  vue.runtime.js
  dashboard.chunk.js   ← não deveria estar no pagar
  admin-charts.js      ← idem

depois (o teste)
  PayStep.js
  vue.runtime.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se o dashboard ainda aparece, o &lt;code&gt;import()&lt;/code&gt; é teatro. Não importa o Lighthouse.&lt;/p&gt;

&lt;p&gt;Sentry. Mesmo recorte do &lt;a href="https://dev.to/tiagovilasboas/como-o-sentry-passou-a-mostrar-o-funil-a-jornada-antes-do-stacktrace-1ml6"&gt;complementar&lt;/a&gt;. Cola no Discover:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;extra.phase:checkout.pay.submit AND (
  error.type:TimeoutError OR message:*timeout* OR mechanism:*timeout*
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pixel quebrado depois da venda &lt;strong&gt;não&lt;/strong&gt; entra nesse filtro. É outro &lt;code&gt;phase&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Datadog, a pergunta que eu fazia no pico: o TTFB do chunk do PayStep subiu na região da campanha, ou a origem tava ok e o bundle que era grande? Uma lente só te faz otimizar a errada.&lt;/p&gt;

&lt;p&gt;Cupom inválido, cartão recusado, form incompleto: a regra funcionou. Breadcrumb. Não exception. Exception é gateway 500, timeout de rede, SDK que estourou no submit. Se misturar, o board grita e você trata recusa de cartão como queda de conversão.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Resultado: +10% no mês
&lt;/h2&gt;

&lt;p&gt;O cliente leu o painel. &lt;strong&gt;+10% de conversão no mês.&lt;/strong&gt; Pico nas campanhas de influenciador.&lt;/p&gt;

&lt;p&gt;Esse número não veio de uma ferramenta só. Veio de três camadas juntas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Métrica de venda:&lt;/strong&gt; backoffice do cliente. Quem vende lê isso.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jornada:&lt;/strong&gt; Sentry com timeout/transbordo no pagar, não TypeError órfão.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caminho:&lt;/strong&gt; Datadog com TTFB, saturação, região.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lighthouse ficou de hipótese de lab. Não fecha o mês.&lt;/p&gt;

&lt;p&gt;O +10% não veio de silenciar recusa de cartão. Veio de o JS do pagar chegar a tempo, e de eu ver o timeout no mesmo dia.&lt;/p&gt;

&lt;p&gt;Eu toquei esse recorte sozinho: o caminho do pagar, o split, essas lentes. Depois pesou numa promoção. O que ficou copiável foi a ordem: painel primeiro, lab por último. Bundle menor sem esse fechamento era só PR bonito.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. O que copiar no próximo PR
&lt;/h2&gt;

&lt;p&gt;Antes de mergear “otimizar o bundle”:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Qual jornada perde dinheiro se o JS atrasar? (pagar, não o admin)
2. No Network do passo de pagar, aparece JS de dashboard?
3. SSR no first paint do pagar, ou SPA seca esperando hidratar?
4. O import() puxa o passo, ou o passo importa o mundo no topo?
5. O chunk lazy está no edge, com hash e gzip, ou sai frio?
6. Venda (backoffice) e obs (Sentry/Datadog) olham o mesmo número, ou só o Lighthouse?
7. Timeout/transbordo alertam sintoma de usuário, ou qualquer JS?

Ordem: backoffice → Sentry → Datadog → Lighthouse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se o PR não responde 1 e 6, pode ser um PR bom. Não é este case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O número que paga é a conversão.&lt;/strong&gt; Vue 3, MFE e Lighthouse entram se ajudam esse número. Se não ajudam, são logo.&lt;/p&gt;

&lt;p&gt;Última campanha que você pegou: olhou primeiro o timeout no Sentry, o TTFB no Datadog, ou o backoffice?&lt;/p&gt;

</description>
      <category>career</category>
      <category>frontend</category>
      <category>monitoring</category>
      <category>performance</category>
    </item>
    <item>
      <title>Observabilidade no frontend: o HTTP 200 esconde ~900 catch vazios</title>
      <dc:creator>Tiago Vilas Boas (Montanha)</dc:creator>
      <pubDate>Sun, 13 Sep 2026 17:51:10 +0000</pubDate>
      <link>https://dev.to/tiagovilasboas/observabilidade-no-frontend-o-http-200-esconde-900-catch-vazios-53jd</link>
      <guid>https://dev.to/tiagovilasboas/observabilidade-no-frontend-o-http-200-esconde-900-catch-vazios-53jd</guid>
      <description>&lt;p&gt;Pessoal, abri o checkout. O BFF de pagamento devolveu HTTP 200. No botão, TypeError. Quem vendia e quem comprava falhavam sem stack, sem release, sem rota. Infra já tinha métrica. O navegador, onde o dinheiro vive, era ponto cego.&lt;/p&gt;

&lt;p&gt;Eu contei da ordem de &lt;strong&gt;~900&lt;/strong&gt; &lt;code&gt;catch&lt;/code&gt; vazios ou logs que engoliam a exceção. Quatro fronts. Sem dono. Se eu ligasse o SDK no default do wizard, ia queimar quota e ainda assim não ter sinal.&lt;/p&gt;

&lt;p&gt;O ponto é: HTTP 200 no BFF não prova que o clique viveu. &lt;code&gt;catch {}&lt;/code&gt; também não.&lt;/p&gt;

&lt;p&gt;A conta foi grep no repo, não runtime. Se você rodar as duas buscas, já vê a classe. Não é o regex que fechou o &lt;code&gt;~900&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rg -n --glob '*.{js,ts,tsx,vue}' 'catch\s*\([^)]*\)\s*\{\s*\}'
rg -n --glob '*.{js,ts,tsx,vue}' 'catch\s*\([^)]*\)\s*\{\s*console\.(log|error|warn)'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como etiquetar a jornada no evento (pagar vs pixel) fica no &lt;a href="https://dev.to/tiagovilasboas/como-o-sentry-passou-a-mostrar-o-funil-a-jornada-antes-do-stacktrace-1ml6"&gt;complementar&lt;/a&gt;. O &lt;a href="https://dev.to/tiagovilasboas/como-o-checkout-ganhou-10-de-conversao-no-mes-o-case-que-pesou-na-promocao-a-staff-ii-71"&gt;denso do +10%&lt;/a&gt; é o dinheiro. Aqui é o buraco que os dois encontram: o front engoliu o erro e o BFF disse 200.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tabela de Conteúdo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. HTTP 200 não é o sinal do usuário&lt;/li&gt;
&lt;li&gt;2. Empty catch não é telemetria&lt;/li&gt;
&lt;li&gt;3. O que entrou nos quatro fronts&lt;/li&gt;
&lt;li&gt;4. Depois da adoção: talk, triagem, o tempo até a task&lt;/li&gt;
&lt;li&gt;5. Dois clients no mesmo origin. Shell sem SDK&lt;/li&gt;
&lt;li&gt;6. PII, replay, sample&lt;/li&gt;
&lt;li&gt;7. O irmão de 30 dias&lt;/li&gt;
&lt;li&gt;8. O que copiar no próximo PR&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. HTTP 200 não é o sinal do usuário
&lt;/h2&gt;

&lt;p&gt;O servidor pode estar verde e o clique morto. Métrica de API responde tráfego e saturação. Ela não vê o &lt;code&gt;TypeError&lt;/code&gt; no &lt;code&gt;onClick&lt;/code&gt; do pagar.&lt;/p&gt;

&lt;p&gt;Quatro repos, stacks distintas. Calendário de checkout não parava pra um “projeto de observabilidade”. A premissa era complementar Grafana e APM, não substituir.&lt;/p&gt;

&lt;p&gt;Google SRE lista quatro sinais: Latency, Traffic, Errors, Saturation. No client, o pedaço que um SDK de erro carrega barato é Errors e um pouco de Latency. Traffic e saturação de fila ficam no backend. Se o setup promete os quatro sinais num projeto de front, a premissa está errada.&lt;/p&gt;

&lt;p&gt;Eu recusei três coisas no mesmo dia: só APM (não vê o botão), &lt;code&gt;captureException&lt;/code&gt; nu (sem dono) e sampling &lt;code&gt;1.0&lt;/code&gt; (custo + ruído). Sem essas recusas, o dashboard mentia com mais volume.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Empty catch não é telemetria
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;catch&lt;/code&gt; vazio é o oposto de sinal. A exceção existe, a pessoa sente, o time descobre no ticket dias depois.&lt;/p&gt;

&lt;p&gt;Sem tag de domínio, erro de PIX misturava com erro de upload. Sem máscara, qualquer SDK virava risco de PII.&lt;/p&gt;

&lt;p&gt;A cadeia de pagamento piora isso. O mesmo erro sobe &lt;code&gt;processPayment&lt;/code&gt; → &lt;code&gt;executePayment&lt;/code&gt; → o handler. Sem dedup, você manda o mesmo TypeError três vezes e acha que o volume é incidente.&lt;/p&gt;

&lt;p&gt;O que eu cobrei depois que o SDK entrou (formato, não issue de produção):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;release:  checkout@git-a1b2c3
title:    TypeError: Cannot read properties of undefined (reading 'pix')
culprit:  PayButton.tsx:onClick
tags:     domain=checkout flow=pay error_type=timeout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se falta &lt;code&gt;release&lt;/code&gt; ou &lt;code&gt;domain&lt;/code&gt;, a issue ainda é ticket com stack. Não é sinal.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. O que entrou nos quatro fronts
&lt;/h2&gt;

&lt;p&gt;Não foi um &lt;code&gt;Sentry.init&lt;/code&gt; colado quatro vezes. Foram quatro jeitos, um contrato.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Front&lt;/th&gt;
&lt;th&gt;Stack&lt;/th&gt;
&lt;th&gt;O que o SDK (ou a ausência dele) faz&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Checkout&lt;/td&gt;
&lt;td&gt;Nuxt 3 / Vue&lt;/td&gt;
&lt;td&gt;Plugin client. Só produção. Domínio no composable (&lt;code&gt;payment&lt;/code&gt;, &lt;code&gt;checkout_load&lt;/code&gt;, pixel, lead).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Admin novo&lt;/td&gt;
&lt;td&gt;React 19, MFE&lt;/td&gt;
&lt;td&gt;Init no boot. Error boundary. Interceptor do HTTP mapeia a URL da API pro domínio. Hook de rota seta &lt;code&gt;flow&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Admin legado&lt;/td&gt;
&lt;td&gt;Vue&lt;/td&gt;
&lt;td&gt;Projeto separado. Mesmo contrato de tag. Sem misturar evento com o MFE novo.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shell de rota&lt;/td&gt;
&lt;td&gt;Go + JS inline&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Sem SDK no &lt;code&gt;window&lt;/code&gt;.&lt;/strong&gt; Envelope na mão, &lt;code&gt;fetch&lt;/code&gt; + &lt;code&gt;keepalive&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Init só em produção. Hostname de PRD &lt;strong&gt;e&lt;/strong&gt; flag de build. Localhost com bundle de prod não dispara. Sem DSN no env, return. Guard contra double-init (HMR / remount do MFE).&lt;/p&gt;

&lt;p&gt;A API que o time chama não é &lt;code&gt;Sentry.captureException&lt;/code&gt;. É o módulo de domínio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capturePaymentError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pix&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;captureFinanceError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;withdraw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;captureApiError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O interceptor lê o path (&lt;code&gt;/withdraw&lt;/code&gt;, &lt;code&gt;/pay&lt;/code&gt;, &lt;code&gt;/auth&lt;/code&gt;). Classifica &lt;code&gt;timeout&lt;/code&gt; | &lt;code&gt;network_error&lt;/code&gt; | &lt;code&gt;server_error&lt;/code&gt; | &lt;code&gt;client_error&lt;/code&gt;. Coloca &lt;code&gt;http_status&lt;/code&gt; e &lt;code&gt;error_type&lt;/code&gt; na tag. Fingerprint: domínio + flow + tipo. Assim PIX e boleto com o mesmo TypeError não viram o mesmo card.&lt;/p&gt;

&lt;p&gt;Dedup: chave &lt;code&gt;domain:message&lt;/code&gt;, TTL &lt;strong&gt;5s&lt;/strong&gt;. Cobre o boundary, o &lt;code&gt;captureException&lt;/code&gt; nu e o handler do Vue. Sem isso o clique duplo vira storm.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ignoreErrors&lt;/code&gt; não é “esconder bug”. É não pagar quota com ResizeObserver, chunk que o retry resolve, navegação cancelada, pixel de terceiro, WebView (&lt;code&gt;Java object is gone&lt;/code&gt;, &lt;code&gt;webkit.messageHandlers&lt;/code&gt;). &lt;code&gt;Failed to fetch&lt;/code&gt; no admin eu &lt;strong&gt;não&lt;/strong&gt; ignorei: quero falha de API. No checkout ignorei rede do comprador. O ignore é contrato por app, não lista copiada.&lt;/p&gt;

&lt;p&gt;Replay no checkout só se &lt;code&gt;domain&lt;/code&gt; é &lt;code&gt;payment&lt;/code&gt; ou &lt;code&gt;checkout_load&lt;/code&gt;. Pixel e lead resolvem no stack. Cota de sessão não é pra gravar o admin inteiro.&lt;/p&gt;

&lt;p&gt;Tinha um &lt;code&gt;ReferenceError&lt;/code&gt; em tela de detalhe. Centenas de eventos. Dezenas de contas. Silencioso até o projeto certo existir. Isolar o client não foi estética. Foi conseguir achar o arquivo.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Depois da adoção: talk, triagem, o tempo até a task
&lt;/h2&gt;

&lt;p&gt;Ligar o SDK não era o fim. Sem cultura, vira dashboard que ninguém abre.&lt;/p&gt;

&lt;p&gt;Eu apresentei. Chapter de front: dezenove slides, demo ao vivo, guia escrito pra qualquer squad copiar o contrato (domínio, produção-only, máscara). Checkpoint no meio: o que o APM via vs o que o browser via. Depois, reunião pra estender o mesmo contrato pro fluxo de identidade, com client isolado. Não foi pitch de vendor. Foi “assim a gente investiga”.&lt;/p&gt;

&lt;p&gt;Nas primeiras 24h no admin, sample a 10%: &lt;strong&gt;98 eventos&lt;/strong&gt;. Volume real, se o sample vale, na casa de mil erros de client. Vinte issues. Poucas causas raiz. Um crash de login no mobile saiu com &lt;strong&gt;um caractere&lt;/strong&gt; (&lt;code&gt;?.&lt;/code&gt; no reset do captcha). Navegação com &lt;code&gt;router-link&lt;/code&gt; pra &lt;code&gt;null&lt;/code&gt; antes do dado. &lt;code&gt;RangeError: Invalid Date&lt;/code&gt; no Safari. Isso existia. Só não tinha dono.&lt;/p&gt;

&lt;p&gt;Em ~25 dias de instrumentação ativa: &lt;strong&gt;15 bugs&lt;/strong&gt; de verdade (sem E2E, sem teste), da ordem de &lt;strong&gt;300 contas&lt;/strong&gt; no total, dezenas de PRs (setup + fix + tag). Custo do ciclo: frações de dólar. Só replay passou da cota.&lt;/p&gt;

&lt;p&gt;Aí entra o que o volume faz com a cabeça. O board de erro &lt;strong&gt;aumenta&lt;/strong&gt;. Antes o front era zero. Zero não é saúde. É cegueira. Quem olha só a fila acha que o Sentry piorou o MTTR porque agora tem issue. O que caiu foi o tempo até agir.&lt;/p&gt;

&lt;p&gt;Antes: chamado vago, horas ou dias depois, “não consigo acessar”. Depois: o primeiro erro e a task na mesma janela. Nos casos que eu fechei no doc de produto, &lt;strong&gt;menos de 24h&lt;/strong&gt; do evento até o card. A meta escrita de investigação era 4-6h no escuro para 15-30min com stack e breadcrumb. O cronômetro de cada fix eu &lt;strong&gt;não&lt;/strong&gt; publico aqui. O que eu medi foi o tempo até a task, e o fato de achar dezenas de contas &lt;strong&gt;antes&lt;/strong&gt; do chamado (o login travado depois da senha errada).&lt;/p&gt;

&lt;p&gt;Triagem virou rito. Todo sprint: o que é novo, o que é regressão, o que é E2E fingindo usuário. Filtro de ambiente sintético. Issue que “resolveu” e voltou no &lt;code&gt;lastSeen&lt;/code&gt; depois do deploy. Sem isso o SDK mente duas vezes: no silêncio, e no falso positivo.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Dois clients no mesmo origin. Shell sem SDK
&lt;/h2&gt;

&lt;p&gt;Dois SPAs no mesmo origin. Um Vue legado (SDK velho no CDN, &lt;code&gt;window.Sentry&lt;/code&gt;). Um React novo (SDK 9, namespace versionado). Se os dois escrevem no mesmo global, o evento do filho cai no projeto do pai. Ou some.&lt;/p&gt;

&lt;p&gt;O shell de roteamento (Go + JS inline) &lt;strong&gt;não carrega o SDK&lt;/strong&gt;. Monta o envelope, posta no ingest, &lt;code&gt;keepalive: true&lt;/code&gt; pra sobreviver ao unload. Expõe &lt;code&gt;captureError&lt;/code&gt; / &lt;code&gt;captureWarning&lt;/code&gt; pro interceptor de rota. Breadcrumb no shell é no-op: sem SDK, não inventa timeline.&lt;/p&gt;

&lt;p&gt;No MFE novo, o fluxo de identidade/documentos ganhou &lt;strong&gt;client isolado&lt;/strong&gt;. &lt;code&gt;BrowserClient&lt;/code&gt; próprio, &lt;code&gt;Scope&lt;/code&gt; próprio. Client isolado &lt;strong&gt;não herda&lt;/strong&gt; breadcrumb do scope global. Se você captura no client B e o &lt;code&gt;addBreadcrumb&lt;/code&gt; foi no A, a timeline some. Por isso o scope dedicado: o passo (&lt;code&gt;upload_document&lt;/code&gt;, &lt;code&gt;submit_data&lt;/code&gt;) acompanha o evento no projeto certo.&lt;/p&gt;

&lt;p&gt;Diagnóstico em PRD: um &lt;code&gt;status()&lt;/code&gt; no &lt;code&gt;window&lt;/code&gt; (namespace do app, não &lt;code&gt;window.Sentry&lt;/code&gt;). DSN não se loga. Só: inicializou? env? o segundo client subiu?&lt;/p&gt;

&lt;p&gt;O guia público do padrão está no &lt;a href="https://github.com/tiagovilasboas/sentry-golden-path" rel="noopener noreferrer"&gt;sentry-golden-path&lt;/a&gt;. Ele prova o contrato. Não prova rate de uma marca.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. PII, replay, sample
&lt;/h2&gt;

&lt;p&gt;LGPD: documento, cartão, telefone, senha não vão pro vendor.&lt;/p&gt;

&lt;p&gt;Máscara em duas camadas, no &lt;code&gt;beforeSend&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Por &lt;strong&gt;nome de campo&lt;/strong&gt;: &lt;code&gt;cpf&lt;/code&gt;, &lt;code&gt;document&lt;/code&gt;, &lt;code&gt;card_number&lt;/code&gt;, &lt;code&gt;cvv&lt;/code&gt;, &lt;code&gt;password&lt;/code&gt;. Substitui por &lt;code&gt;[PII_MASKED]&lt;/code&gt;. Não pega &lt;code&gt;order_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Por &lt;strong&gt;regex em texto livre&lt;/strong&gt;: CPF &lt;em&gt;formatado&lt;/em&gt; (&lt;code&gt;000.000.000-00&lt;/code&gt;), cartão, telefone. CPF sem pontuação eu não mascaro: pega id numérico.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Replay: &lt;code&gt;maskAllInputs: true&lt;/code&gt;. Sessão aleatória: &lt;strong&gt;0&lt;/strong&gt;. Replay no erro: fração baixa, e ainda filtrada por domínio.&lt;/p&gt;

&lt;p&gt;Sample: o wizard empurra &lt;code&gt;tracesSampleRate: 1.0&lt;/code&gt; e replay em 10% das sessões. Isso é demo.&lt;/p&gt;

&lt;p&gt;No checkout eu subi sample de erro na casa de &lt;strong&gt;um terço&lt;/strong&gt;, trace em &lt;strong&gt;5%&lt;/strong&gt;, replay de sessão &lt;strong&gt;zero&lt;/strong&gt;. Replay no erro só no domínio que perde venda. No admin, no rollout, o erro foi mais alto que o 10% do guia. Trace igual: 5%. O número mora no PR. Se você não consegue escrever por que subiu, ele não sobe.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sampleRate&lt;/code&gt; é evento de erro. &lt;code&gt;tracesSampleRate&lt;/code&gt; é span de pageload. São independentes. Cortar trace não esconde 100% das exceptions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ordem de grandeza do contrato. não é o wizard.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TRACES_SAMPLE_RATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;REPLAY_SESSION_SAMPLE_RATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. O irmão de 30 dias
&lt;/h2&gt;

&lt;p&gt;No mesmo tipo de buraco, por outro canal: da ordem de quatro mil contas ficaram &lt;strong&gt;~30 dias&lt;/strong&gt; sem push. HTTP aparentava sucesso. API de terceiro deprecada, client antigo sem header, catch otimista, zero alerta de queda de entrega.&lt;/p&gt;

&lt;p&gt;Achei no dashboard, não na fila de ticket. Happy path mente. &lt;code&gt;$response-&amp;gt;failed()&lt;/code&gt; (ou o equivalente) é o teste de sanidade de toda integração. Fallback gracioso sem alerta é o mesmo silêncio com nome bonito.&lt;/p&gt;

&lt;p&gt;O que eu &lt;strong&gt;não&lt;/strong&gt; publico neste texto: “60% dos bugs antes do suporte”, crash-free, dinheiro projetado. Contei os catches na estática. Vi o padrão entrar nos quatro fronts. PII deixou de ser risco aberto no &lt;code&gt;beforeSend&lt;/code&gt;. Ainda ficou catch financeiro no legado e uma dezena no checkout sem fechar aqui.&lt;/p&gt;

&lt;p&gt;Se o sample estiver errado, o blast é quota + alerta mutado + PII no vendor. Por isso o número começa escrito e só sobe com motivo.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. O que copiar no próximo PR
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. grep do catch vazio / console.error no glob do front. Qual o número da classe?
2. Init só em PRD (hostname + flag de build). Sem DSN, return.
3. O time chama captureXError, ou Sentry.captureException no if?
4. Interceptor mapeia URL → domain, ou cada hook captura de novo?
5. Dedup 5s por domain:message? A cadeia de pagamento dispara uma vez ou três?
6. Dois SPAs no mesmo origin: cada um tem client/projeto, ou compartilham window.Sentry?
7. Shell de rota usa SDK no window, ou envelope na mão?
8. beforeSend mascara campo + CPF formatado? Replay de sessão está 0?
9. ignoreErrors é contrato do app, ou lista copiada do wizard?
10. $response-&amp;gt;failed() (ou equivalente) no client HTTP. 200 não é entrega.
11. Tem rito de triagem (novo vs regressão vs E2E), ou só o SDK?

Kit: https://github.com/tiagovilasboas/sentry-golden-path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Premissa, target sample, dono. Depois o golden signal. Sem isso, você tem dashboard.&lt;/p&gt;

&lt;p&gt;Qual &lt;code&gt;catch&lt;/code&gt; vazio ou integração “200” o seu time ainda trata como silêncio? Catch no botão, client HTTP, ou os dois?&lt;/p&gt;

</description>
      <category>observability</category>
      <category>sentry</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>IA generativa e agentic: o README diz agent. O git não mudou</title>
      <dc:creator>Tiago Vilas Boas (Montanha)</dc:creator>
      <pubDate>Sat, 12 Sep 2026 20:17:09 +0000</pubDate>
      <link>https://dev.to/tiagovilasboas/ia-generativa-gera-ia-agentic-age-1jik</link>
      <guid>https://dev.to/tiagovilasboas/ia-generativa-gera-ia-agentic-age-1jik</guid>
      <description>&lt;p&gt;Você já teve aquele chat. O “agente” resolveu tudo em Markdown. &lt;code&gt;git status&lt;/code&gt; limpo. Billing igual. Os dois usam o mesmo modelo. A diferença não está no vocabulário. Está no que aconteceu depois do prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  O histórico decide, não o README
&lt;/h2&gt;

&lt;p&gt;Cursor, Claude Code, Codex, um &lt;code&gt;StateGraph&lt;/code&gt; na API: o mesmo produto pode ser generativo numa rodada e agentic na outra. O nome da caixa não decide. O histórico decide.&lt;/p&gt;

&lt;p&gt;Só completou Markdown? Generativo. Chamou busca, &lt;code&gt;Read&lt;/code&gt;, &lt;code&gt;gh&lt;/code&gt;? Agentic de leitura. Já tem blast (segredo no arquivo, dado que não devia sair). Chamou merge, migrate, deploy, pagar? Agentic que grava. Sem humano no meio, incidente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uma rodada é só o próximo passo
&lt;/h2&gt;

&lt;p&gt;Você manda uma mensagem. O modelo recebe o histórico, escolhe o próximo passo e devolve alguma coisa. Isso é uma rodada.&lt;/p&gt;

&lt;p&gt;Nada no git. Nada no banco. Nada no gateway. Generativo bem feito já vale. Classificar issue. Esboçar teste. Explicar um diff. Isso não é menor. É outro risco.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agir devolve string e decide de novo
&lt;/h2&gt;

&lt;p&gt;No agentic, o próximo passo pode ser uma ferramenta. O ciclo é curto e sempre o mesmo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;O modelo para e monta a chamada (nome da tool, argumentos).&lt;/li&gt;
&lt;li&gt;O runtime executa no mundo.&lt;/li&gt;
&lt;li&gt;O resultado volta como mais texto no histórico.&lt;/li&gt;
&lt;li&gt;Ele decide de novo: responde pra você, ou chama outra tool.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pode fazer isso várias vezes antes da frase final. Se a string mente, ele segue. Se a ferramenta é larga demais, ele age largo. Agentic sem política não é produto. É um &lt;code&gt;invoke&lt;/code&gt; com efeito colateral.&lt;/p&gt;

&lt;h2&gt;
  
  
  O modelo não sabe que pagou
&lt;/h2&gt;

&lt;p&gt;Ele só vê a string que a ferramenta devolveu. Não “sabe” que a assinatura caiu, que o PR abriu, que o deploy passou. Confiança alta no texto não muda isso.&lt;/p&gt;

&lt;p&gt;Isso aqui não é log de produção. É o exemplo que eu uso pra não misturar as duas:&lt;/p&gt;

&lt;h2&gt;
  
  
  Empty invoke não é agente
&lt;/h2&gt;

&lt;p&gt;O erro é chamar de agente um &lt;code&gt;invoke&lt;/code&gt; sem loop. Agente, neste texto, é o ciclo: observa o estado, decide, usa ferramenta, lê o resultado, decide de novo.&lt;/p&gt;

&lt;p&gt;Teste de bolso: se o git não mudou, o banco não mudou e nenhuma API foi chamada, aquela rodada foi generativa. Por mais que o README diga agent. Sem &lt;code&gt;arquivo:linha&lt;/code&gt; no review, a gente já aprendeu a não misturar finding com palpite. Aqui é o mesmo músculo: texto não é efeito.&lt;/p&gt;

&lt;p&gt;No último chat, 20 segundos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. tem tool call?            nao: gerou
2. a tool so le (Read, rg)?  sim: agir, leitura
3. a tool grava, paga, push? sim: humano antes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se você não abre o transcript, o README decide por você.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leitura e escrita não andam no mesmo cano
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Confiança do modelo não é autorização
&lt;/h2&gt;

&lt;p&gt;O modelo pode ter certeza alta e ainda assim chamar o path errado. Se ele errar, o pior é uma frase ruim, ou um deploy? Se for frase, gera. Se for deploy, cobra humano antes da tool que grava.&lt;/p&gt;

&lt;p&gt;Cadeia, grafo e harness entram no runtime. O post irmão é &lt;a href="https://dev.to/tiagovilasboas/langgraph-versus-langchain-a-conta-e-cadeia-grafo-ou-harness-57cn"&gt;LangGraph versus LangChain&lt;/a&gt;. RAG, MCP e fine-tuning são outro texto. Aqui o knob é um: você deixou o modelo agir?&lt;/p&gt;

&lt;h2&gt;
  
  
  O que eu não medi
&lt;/h2&gt;

&lt;p&gt;Isso não é transcript de produção. Não contei quantos READMEs da sua org mentem. Não tenho taxa de agente de mentira. Tenho o teste: histórico, não vocabulário. Se eu estiver errado e a tool de escrita for larga, o blast é PR, deploy ou cobrança. Por isso a chave não fica com o modelo.&lt;/p&gt;

&lt;p&gt;Gera é texto. Age é ferramenta. Olha o git.&lt;/p&gt;

&lt;p&gt;No último chat: ele só escreveu, leu alguma ferramenta, ou gravou no mundo?&lt;br&gt;
Ler README é uma ferramenta. &lt;code&gt;git push&lt;/code&gt; é outra. As duas não andam no mesmo cano. A de leitura pode ser larga. A de escrita, não.&lt;/p&gt;

&lt;p&gt;Humano no meio, aqui, não é revisar o Markdown depois. É confirmar antes da tool que grava, apaga, publica ou cobra. O modelo não segura essa chave. Sandbox, allowlist e audit trail existem pra essa rodada. A que só gera não precisa disso.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;você:  cancela a assinatura 441
gerar: o path costuma ser DELETE /v1/subscriptions/{id}
agir:  tool HTTP, status 204, a assinatura caiu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O primeiro é hipótese. Pode estar certo. Pode ter alucinado o path. O git não mudou. O billing não mudou. O segundo é o mundo. Alguém de verdade perdeu a assinatura.&lt;br&gt;
Não é “turno mágico”. Não é autonomia. É uma decisão, um retorno, e a chance de decidir de novo. Se você não consegue apontar onde a rodada acabou, você não sabe se gerou ou agiu.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gerar acaba no buffer
&lt;/h2&gt;

&lt;p&gt;No generativo, o próximo passo é só token. Prompt entra, texto sai. Traduz, resume, sugere um patch em Markdown. A rodada acaba no buffer do chat.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>discuss</category>
      <category>beginners</category>
    </item>
    <item>
      <title>LangGraph versus LangChain: a conta é cadeia, grafo ou harness</title>
      <dc:creator>Tiago Vilas Boas (Montanha)</dc:creator>
      <pubDate>Sat, 12 Sep 2026 20:17:08 +0000</pubDate>
      <link>https://dev.to/tiagovilasboas/langgraph-versus-langchain-a-conta-e-cadeia-grafo-ou-harness-57cn</link>
      <guid>https://dev.to/tiagovilasboas/langgraph-versus-langchain-a-conta-e-cadeia-grafo-ou-harness-57cn</guid>
      <description>&lt;p&gt;Pessoal, eu ia responder LangGraph ou LangChain. Tipo React ou Vue.&lt;/p&gt;

&lt;p&gt;Terminei o &lt;a href="https://www.coursera.org/learn/agentic-ai-with-langchain-and-langgraph" rel="noopener noreferrer"&gt;Agentic AI with LangChain and LangGraph&lt;/a&gt;, no Coursera (IBM). O curso ainda empurra um pouco pra escolher um logo. A pergunta que ficou depois do certificado não era o logo. Era: este fluxo é uma &lt;strong&gt;cadeia&lt;/strong&gt; (entra, sai, acabou)? É um &lt;strong&gt;grafo&lt;/strong&gt; (estado, ciclo, pausa pra gente)? Ou o cérebro nem está na lib: está no Cursor, no Kiro, no Claude Code?&lt;/p&gt;

&lt;p&gt;O que você leva daqui: &lt;strong&gt;nomeia o fluxo antes do logo.&lt;/strong&gt; Cadeia, grafo ou o desk. LangChain e LangGraph se encaixam. Nenhum substitui o harness.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://dev.to/tiagovilasboas/ia-generativa-gera-ia-agentic-age-1jik"&gt;nota gerar vs agir&lt;/a&gt; para na rodada. O &lt;a href="https://dev.to/tiagovilasboas/rag-ou-mcp-a-pergunta-errada-cole-o-prompt-e-monte-os-dois-57kj"&gt;post de RAG e MCP&lt;/a&gt; é outra camada. Aqui é orquestração.&lt;/p&gt;

&lt;p&gt;Um limite só: eu não rodei benchmark de latência Chain versus Graph neste laboratório. A conta abaixo é de forma, não de milissegundo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nomeia o fluxo antes do logo
&lt;/h2&gt;

&lt;p&gt;Três formas. Não são três produtos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cadeia.&lt;/strong&gt; Prompt, modelo, parser. Um sentido. Sem loop. LCEL: &lt;code&gt;prompt | model | parser&lt;/code&gt;. Traduz, classifica, extrai. Acabou.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grafo.&lt;/strong&gt; Nós, arestas, estado. Pode voltar. Pode pausar. Pode retomar depois de falha. Docs oficiais: execução durável, memória de curto e longo prazo, &lt;code&gt;interrupt&lt;/code&gt; com gente no meio. Isso é &lt;a href="https://github.com/langchain-ai/langgraph" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Harness.&lt;/strong&gt; O IDE chama tool, lê arquivo, espera você. Não precisa do grafo Python pra existir. Precisa de política: o que lê, o que grava, quem aprova.&lt;/p&gt;

&lt;p&gt;No grafo, uma pergunta extra: &lt;strong&gt;quem escolhe o próximo passo?&lt;/strong&gt; Se o código escolhe (aresta que você escreveu), é workflow. Se o modelo escolhe (ReAct, &lt;code&gt;create_react_agent&lt;/code&gt;), é agent. LangGraph faz os dois. Por isso “coloquei LangGraph” não diz se você tem agente.&lt;/p&gt;

&lt;p&gt;Se o time discute logo e o fluxo ainda é um &lt;code&gt;invoke&lt;/code&gt; sem estado, vocês estão no item 1 fingindo que é o 2. Se no teu time já abriu PR de &lt;code&gt;StateGraph&lt;/code&gt; pra um classificador, você já viu isso.&lt;/p&gt;

&lt;h2&gt;
  
  
  LangChain: a cadeia
&lt;/h2&gt;

&lt;p&gt;A forma mínima, da &lt;a href="https://docs.langchain.com/oss/python/integrations/chat/amazon_nova" rel="noopener noreferrer"&gt;documentação LCEL&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;StrOutputParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_language&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;English&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output_language&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Spanish&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Entra dict. Sai string. Não tem “tenta de novo se a tool falhar”. Não tem “espera o humano”. Não tem checkpoint.&lt;/p&gt;

&lt;p&gt;LangChain também entrega atalho de agente (&lt;code&gt;create_agent&lt;/code&gt; no pacote atual). O detalhe está no H3 abaixo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O que você ganha:&lt;/strong&gt; integração, LCEL, um &lt;code&gt;invoke&lt;/code&gt; que o time lê em dez linhas.&lt;br&gt;
&lt;strong&gt;O que você paga:&lt;/strong&gt; quando precisa de ciclo, persistência e pausa de verdade, a cadeia não chega. Você sobe de knob. Ou finge que subiu.&lt;/p&gt;
&lt;h3&gt;
  
  
  atalho de agente versus cadeia
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;create_agent&lt;/code&gt; no pacote atual não apaga o grafo. Por baixo, o runtime de agente da casa ainda é grafo. Você não desenhou os nós. O ciclo está lá. Se o pedido é traduzir e não há tool, o atalho é cadeia cara.&lt;/p&gt;
&lt;h2&gt;
  
  
  LangGraph: estado, ciclo, pausa
&lt;/h2&gt;

&lt;p&gt;O README lista o que a cadeia não promete: execução que sobrevive a falha, HITL, memória de trabalho e entre sessões.&lt;/p&gt;

&lt;p&gt;Ciclo: aresta condicional. O nó &lt;code&gt;agent&lt;/code&gt; decide se chama &lt;code&gt;tools&lt;/code&gt; ou &lt;code&gt;END&lt;/code&gt;. Tool volta pro agent. ReAct não é marketing. É esse loop.&lt;/p&gt;

&lt;p&gt;Pausa: &lt;code&gt;interrupt()&lt;/code&gt;. Sobe valor pro cliente. Retoma com &lt;code&gt;Command&lt;/code&gt;. Sem checkpointer, a pausa não existe: o estado não grava.&lt;/p&gt;
&lt;h3&gt;
  
  
  interrupt versus print("confirma?")
&lt;/h3&gt;

&lt;p&gt;HITL que é &lt;code&gt;print("confirma?")&lt;/code&gt; no log &lt;strong&gt;não&lt;/strong&gt; é o &lt;code&gt;interrupt&lt;/code&gt; da lib. Sem checkpointer, a pausa some no crash.&lt;/p&gt;

&lt;p&gt;Cena pedagógica (não é log de produção):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;você:     faz deploy da branch feature/pix
cadeia:   gera um YAML de pipeline. ninguém chamou o CI
grafo:    nó agent → nó tools → interrupt (HITL)
humano:   aprova
grafo:    retoma do checkpoint, dispara o workflow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A cadeia “fez o texto do deploy”. O grafo “quase publicou”. São contas diferentes. Least privilege e HITL no que grava. Sem isso o grafo é force push com estado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O que você ganha:&lt;/strong&gt; estado explícito, ciclo, pausa retomável.&lt;br&gt;
&lt;strong&gt;O que você paga:&lt;/strong&gt; desenho. Cada nó a mais é um jeito de errar no mundo. Blast radius da tool.&lt;/p&gt;
&lt;h2&gt;
  
  
  O grafo que ainda é uma cadeia
&lt;/h2&gt;

&lt;p&gt;Atalho:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langgraph.prebuilt&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_react_agent&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_react_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;human&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traduz isto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Isso &lt;strong&gt;compila&lt;/strong&gt; um &lt;code&gt;StateGraph&lt;/code&gt; e devolve um Runnable do LangChain. Você pode &lt;code&gt;invoke&lt;/code&gt; como cadeia. Por dentro tem ciclo se houver tool.&lt;/p&gt;

&lt;p&gt;Se a tool não existe e o pedido é “traduz”, você pagou grafo pra fazer LCEL. Estado vazio. Sem &lt;code&gt;interrupt&lt;/code&gt;. Sem checkpointer. Logo Graph no &lt;code&gt;requirements.txt&lt;/code&gt;. Fluxo de cadeia.&lt;/p&gt;

&lt;p&gt;O teste: o transcript tem mais de um nó, ou tem um &lt;code&gt;invoke&lt;/code&gt; e acabou?&lt;/p&gt;

&lt;p&gt;Não usei ChargePaidHandler aqui. Essa cena é do &lt;a href="https://dev.to/tiagovilasboas/indexar-o-codigo-fora-do-repo-como-economizar-tokens-sem-jogar-o-projeto-no-contexto-48c7"&gt;post do grafo de código&lt;/a&gt;. Aqui o knob é orquestração, não dump do repo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Quatro perguntas antes de adicionar o grafo
&lt;/h2&gt;

&lt;p&gt;Copia. Cola no PR do agente.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Cadeia, grafo ou harness&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; O fluxo é um sentido só (gera, classifica, extrai)? Cadeia. LCEL chega.
&lt;span class="p"&gt;2.&lt;/span&gt; Precisa voltar (tool falhou, tenta de novo) ou parar pra gente (push, pagar, deploy)? Grafo. Checkpointer. &lt;span class="sb"&gt;`interrupt`&lt;/span&gt; no que grava. Aí: o &lt;span class="gs"&gt;**código**&lt;/span&gt; escolhe o próximo nó (workflow) ou o &lt;span class="gs"&gt;**modelo**&lt;/span&gt; (agent)?
&lt;span class="p"&gt;3.&lt;/span&gt; O “agente” é o Cursor/Kiro neste pedido, não o Python? Harness. Não invente um StateGraph pra duplicar o que o IDE já faz.
&lt;span class="p"&gt;4.&lt;/span&gt; Se o servidor do grafo cair, o time ainda trabalha? Se não, você acoplou o desk num processo.

HITL, least privilege, blast radius: a tool que grava não anda no mesmo cano da que só lê.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Se a resposta da 1 for sim e você mesmo assim abriu &lt;code&gt;StateGraph&lt;/code&gt;, você comprou ciclo pra um &lt;code&gt;invoke&lt;/code&gt;. O prompt da seção seguinte monta a arquitetura no teu repo. Sem pular pra &lt;code&gt;StateGraph&lt;/code&gt; no escuro.&lt;/p&gt;
&lt;h2&gt;
  
  
  O contrato cola depois das quatro perguntas
&lt;/h2&gt;

&lt;p&gt;Isto não é receita de instalar LangChain e LangGraph. É o contrato das três formas, pra colar numa conversa nova. Arquiteto, não gerador de código. Se você não tem nada, ele assume cadeia (LCEL) e semana 1 no README. Se tem tool que grava, pergunta HITL e espera.&lt;/p&gt;

&lt;p&gt;Conversa &lt;strong&gt;nova&lt;/strong&gt;. Cinco linhas do seu repo. Ele lê README e o que já existe (LCEL, StateGraph, &lt;code&gt;create_agent&lt;/code&gt;). Sem stack: default cadeia. Stack complexa: até 8 perguntas, aí para.&lt;/p&gt;

&lt;p&gt;Se devolver um &lt;code&gt;StateGraph&lt;/code&gt; pra traduzir texto, você pulou as perguntas. Manda o trecho no comentário.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an architect, not a code generator.

I am designing a LangChain + LangGraph setup for a software team. I may paste repo context, a stack description, or a messy Slack thread. Do not assume Cursor, LangSmith Cloud, or any host. Talk in terms of three shapes: chain, graph, harness.

Do not write application code in the first reply. Do not invent tools, models, or vendors I did not mention. Do not open a StateGraph because the hype asked.

First pass, before any questionnaire: inspect what I pasted. If the harness can see the repo, look only at README, existing chains/graphs (LCEL, StateGraph, create_agent / create_react_agent), tools, checkpointer config. A handful of Reads. Not ten. Not dump. Cite what you found. Then classify from that evidence: chain-only vs needs-graph vs harness-already-does-it. Do not pick 3 or 8 questions before this pass.

If the pass shows nothing (no LangChain, no LangGraph, no tools): ask at most 3 short questions: (1) one-way generate/classify/extract, or a loop with tools, (2) does anything write (push, pay, deploy, email), (3) is the “agent” this Python process or an IDE chat. If I skip them, assume: one-way chain; no writes; one IDE for daily work; no StateGraph this week. Then output the sections below in this same reply. Week-1 bullets must be doable with LCEL in that repo. They are the first correct setup, not a shopping list of integrations.

If the pass already has enough, skip questions and output. If the pass shows a complex stack (tools that mutate, existing graph, checkpointer, or I proposed “add LangGraph for everything”) and something material is still missing, ask up to 8 short questions, then stop and wait. The 8 are for that case. Not for the nothing-case.

Mental model (non-negotiable):
- LangChain vs LangGraph is the wrong fight. Chain = one-way compose (LCEL: prompt | model | parser). Graph = state, cycles, durable execution, interrupt for humans. They fit: a compiled StateGraph is a LangChain Runnable. create_react_agent / create_agent is a shortcut that still compiles a graph. A shortcut with no tools and no interrupt is a chain you overpaid for.
- Harness (Cursor, Claude Code, Codex, Kiro) is a third shape. Do not duplicate the IDE with a Python graph if this request already lives in the chat with tools.
- interrupt() without a checkpointer is not HITL. print("confirm?") is not interrupt.
- Read tools ≠ write tools. Push, pay, deploy, email: HITL. Least privilege. Blast radius. No unbounded shell “to make the demo work”.
- Checkpointer memory is not a knowledge-base contract. Retrieval/RAG is another layer. Do not stuff the repo or OpenAPI into graph state.
- Start with the smallest shape that matches the flow. Chain first. Graph when you need to come back or pause. Harness policy always.

Also respect:
- If I said LangGraph or LangChain as alternatives, correct me and classify my flow as chain, graph, or harness.
- If I asked to “add LangGraph” to a translate/classify pipeline, refuse the graph and specify the LCEL chain.
- LangSmith is optional observability. Not a layer that replaces chain/graph/harness.

After you have enough answers, output exactly these sections in my language (pt-BR unless I write in English):

1. Diagnosis — for MY flow: chain vs graph vs harness. One paragraph. If I treated LangGraph as a competitor to LangChain, say so.
2. Chain (LangChain / LCEL) — what stays a one-way invoke. What must never become a node.
3. Graph (LangGraph) — “not now”, or nodes / edges / state fields / checkpointer / interrupt points. Table: node, read vs write, blast radius, HITL yes/no.
4. Shortcut — create_react_agent / create_agent: allowed only if the table needs a tool cycle. Otherwise forbid.
5. Harness — what stays in the IDE and must not be copied into Python this week.
6. Week-1 plan — 5 bullets in the repo. No new logo unless I already have it. If I had nothing, bullets are LCEL only.

End with 3 risks if I skip the diagnosis and jump to StateGraph.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;O prompt já dá pra colar. O gist vive sem o artigo:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h2&gt;
  
  
  Se o server cai, o desk segue
&lt;/h2&gt;

&lt;p&gt;No meu setup o cérebro do servidor defaultou pra Goose. LangGraph ficou legado. Não li isso num slide. Olhei o stack.&lt;/p&gt;

&lt;p&gt;Quando o server está down, o Cursor continua: rules, vault, MCP. Doctor reclama. Você não para. A lib do grafo não era o harness. Era um cérebro atrás de um HTTP.&lt;/p&gt;

&lt;p&gt;Isso não mata LangGraph. Mata a ideia de que escolher Graph ou Chain &lt;strong&gt;é&lt;/strong&gt; montar o agente. Montar o agente é política no desk +, se você precisa de processo longo no servidor, um runtime com estado. Goose, Graph, o que for: o knob 3 (harness) continua existindo.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://dev.to/tiagovilasboas/mesmo-com-graphrag-o-agent-se-perde-sem-contrato-de-memoria-hc8"&gt;memória&lt;/a&gt; é outra porta. Retrieve não é o checkpointer do Graph. Não misture as duas.&lt;/p&gt;

&lt;p&gt;No último fluxo que você subiu: foi &lt;code&gt;prompt | model | parser&lt;/code&gt;, foi grafo com &lt;code&gt;interrupt&lt;/code&gt;, ou foi o chat do IDE sem Python no meio?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>python</category>
      <category>discuss</category>
    </item>
    <item>
      <title>13 findings no Juice Shop. Nenhuma virou issue.</title>
      <dc:creator>Tiago Vilas Boas (Montanha)</dc:creator>
      <pubDate>Sun, 06 Sep 2026 01:54:22 +0000</pubDate>
      <link>https://dev.to/tiagovilasboas/13-findings-no-juice-shop-nenhuma-virou-issue-340p</link>
      <guid>https://dev.to/tiagovilasboas/13-findings-no-juice-shop-nenhuma-virou-issue-340p</guid>
      <description>&lt;p&gt;Pessoal, eu ia colar o &lt;a href="https://github.com/juice-shop/juice-shop" rel="noopener noreferrer"&gt;Juice Shop&lt;/a&gt; na tabela do &lt;a href="https://dev.to/tiagovilasboas/prompt-appsec-4-gaps-autorizacao-1k77"&gt;post das três portas&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Tinha 13 findings com requisito ASVS e &lt;code&gt;arquivo:linha&lt;/code&gt;. SQL concatenado, &lt;code&gt;eval&lt;/code&gt; no username, CORS &lt;code&gt;*&lt;/code&gt;, cesto de outro usuário. O modelo tinha trabalhado. A tabela ia ficar “mais completa”.&lt;/p&gt;

&lt;p&gt;Aí li o &lt;code&gt;package.json&lt;/code&gt;: &lt;em&gt;probably the most modern and sophisticated insecure web application&lt;/em&gt;. Não é gap. É o produto.&lt;/p&gt;

&lt;p&gt;O que você leva daqui: lab de propósito inseguro calibra o procedimento. Não entra nas três portas. Silêncio aqui não é “invariante manteve”. É não abrir issue no scoreboard dos outros.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. A tabela errada
&lt;/h2&gt;

&lt;p&gt;O post pai é sobre OSS que alguém usa de verdade. Formbricks, Dub, Cal.com, Magnitude. Issue de hardening, e-mail quando for exploitable, nada quando o denominador fecha.&lt;/p&gt;

&lt;p&gt;Juice Shop e &lt;a href="https://github.com/WebGoat/WebGoat" rel="noopener noreferrer"&gt;WebGoat&lt;/a&gt; não são essa classe. O README do WebGoat avisa: a máquina fica vulnerável de propósito; o default escuta em localhost. O &lt;a href="https://github.com/juice-shop/juice-shop/blob/master/SECURITY.md" rel="noopener noreferrer"&gt;SECURITY.md do Juice Shop&lt;/a&gt; pede contato só para o que &lt;strong&gt;não&lt;/strong&gt; faz parte de nenhum hacking challenge. O resto já está no placar.&lt;/p&gt;

&lt;p&gt;Somar 13 linhas naquela tabela seria o mesmo reflexo dos 23 findings que eu quase mandei. Volume. A pergunta de segurança some: disto aqui, o que vira issue?&lt;/p&gt;

&lt;p&gt;Nada. Porque o sink já tem nome de challenge.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Round 1: o que a varredura achou
&lt;/h2&gt;

&lt;p&gt;Estático. Sem PoC. Sem tráfego. Fatia estreita, não o monorepo. YAML de challenges &lt;strong&gt;depois&lt;/strong&gt;, só pra recall. Issue: nenhuma.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/juice-shop/juice-shop" rel="noopener noreferrer"&gt;Juice Shop&lt;/a&gt; v20.2.0. 13 findings (10 L1, 3 L2). 10 chaves do catálogo bateram depois, de 116.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Problema&lt;/th&gt;
&lt;th&gt;ASVS&lt;/th&gt;
&lt;th&gt;Onde&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Busca concatena &lt;code&gt;q&lt;/code&gt; em SQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V1.2.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;routes/search.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Login concatena email em SQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V1.2.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;routes/login.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Username via &lt;code&gt;eval&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V1.3.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;routes/userProfile.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Search query com &lt;code&gt;bypassSecurityTrustHtml&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V3.2.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;search-result.component.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Description HTML sem encoding&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V3.2.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;search-result.component.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;CORS default &lt;code&gt;*&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V3.4.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;server.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Cesto por id sem dono&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V8.2.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;routes/basket.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Senha em MD5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V11.4.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lib/insecurity.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Admin seed &lt;code&gt;admin123&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V6.3.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;data/static/users.yml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;FTP: allowlist de extensão depois null-byte&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V5.3.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;routes/fileServer.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fetch(imageUrl)&lt;/code&gt; sem allowlist&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V1.3.6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;routes/profileImageUrlUpload.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Redirect com &lt;code&gt;url.includes&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V3.7.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lib/insecurity.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;Chave RSA do JWT no source&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V13.3.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lib/insecurity.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/WebGoat/WebGoat" rel="noopener noreferrer"&gt;WebGoat&lt;/a&gt;. 11 findings. Uma lição de SSRF não abre URL: não entrei como SSRF.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Problema&lt;/th&gt;
&lt;th&gt;ASVS&lt;/th&gt;
&lt;th&gt;Onde&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;SQL concatena params&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V1.2.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SqlInjectionLesson5a.java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;XML parser com entidade externa&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V1.5.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CommentsCache.java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Upload usa &lt;code&gt;fullName&lt;/code&gt; no path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V5.3.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ProfileUploadBase.java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;XSS refletido em HTML&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V1.2.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CrossSiteScriptingLesson5a.java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Comment stored sem encoding&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V3.2.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StoredXssComments.java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Profile por &lt;code&gt;userId&lt;/code&gt; sem dono&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V8.2.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IDORViewOtherProfile.java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;JWT &lt;code&gt;jku&lt;/code&gt; busca chave na URL do token&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V9.1.3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;JWTHeaderJKUEndpoint.java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Default &lt;code&gt;admin&lt;/code&gt; / &lt;code&gt;admin&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V6.3.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DefaultCredentialsTask.java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Lista de users sem role (Needs-runtime)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V8.2.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MissingFunctionACUsers.java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;redirect:&lt;/code&gt; sem allowlist&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V3.7.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OpenRedirectRealRedirect.java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;HMAC JWT com palavra do source&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V13.3.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;JWTSecretKeyEndpoint.java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Round 1 responde se inventa ID, inventa finding ou vaza PoC. Não responde se cobriu o placar.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. A academia não é o jogo
&lt;/h2&gt;

&lt;p&gt;Essa tabela não é troféu. É o &lt;a href="https://github.com/juice-shop/juice-shop" rel="noopener noreferrer"&gt;Juice Shop&lt;/a&gt; fazendo o trabalho que a OWASP se propôs: um lugar onde a gente pode errar de propósito, ver o sink com nome, e voltar pro código que paga boleto com o olho mais treinado.&lt;/p&gt;

&lt;p&gt;Quem mantém isso (e o &lt;a href="https://github.com/WebGoat/WebGoat" rel="noopener noreferrer"&gt;WebGoat&lt;/a&gt; no mesmo espírito) está doando academia pra uma indústria que, no dia a dia, mistura pressão de entrega com risco de verdade. Sem esse tipo de lab, o atalho vira “rodei o scanner, achei 23 coisas, mandei pro tracker”. Com ele, dá pra ensaiar o reflexo certo: isto aqui é aula. Aquilo ali é produto. A porta não é a mesma.&lt;/p&gt;

&lt;p&gt;Ferramenta ajuda. Prompt ajuda. Modelo de raciocínio alto ajuda. Nada disso substitui a pessoa no time que já viu o suficiente de segurança pra não confundir volume com cuidado.&lt;/p&gt;

&lt;p&gt;O profissional avançado em AppSec não é o que gera a lista mais longa. É o que segura a mão do time quando a lista pede 13 issues num CTF. É o que olha um &lt;code&gt;eval&lt;/code&gt; no username e pergunta: isso é challenge ou é o checkout de amanhã? É o que sabe que silêncio também é ofício. Não o silêncio de quem não olhou. O de quem olhou e escolheu não fazer barulho no lugar errado.&lt;/p&gt;

&lt;p&gt;Time sem essa voz vira fábrica de card. Time com essa voz usa o Juice Shop como aquecimento e reserva o tracker pra quem realmente vai ler a issue no sábado.&lt;/p&gt;

&lt;p&gt;O checklist de lab vs produto, se você quiser copiar: &lt;a href="https://gist.github.com/tiagovilasboas/685b8a720fc7d6db48a57f7629f1faf5" rel="noopener noreferrer"&gt;está no gist&lt;/a&gt;. O &lt;a href="https://dev.to/tiagovilasboas/prompt-appsec-4-gaps-autorizacao-1k77"&gt;post das três portas&lt;/a&gt; continua sendo o mapa quando o alvo é OSS de verdade.&lt;/p&gt;

&lt;p&gt;Takeaway: 13 sinks, zero issues, porque academia não se reporta como incidente. O que se leva pro time é o julgamento.&lt;/p&gt;

&lt;p&gt;No seu time, quem é a pessoa que olha uma tabela dessas e pergunta a porta, em vez de abrir treze cards?&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>github</category>
      <category>opensource</category>
      <category>security</category>
    </item>
    <item>
      <title>RAG e MCP, na prática. Cole o prompt e monte os dois</title>
      <dc:creator>Tiago Vilas Boas (Montanha)</dc:creator>
      <pubDate>Sat, 05 Sep 2026 23:43:57 +0000</pubDate>
      <link>https://dev.to/tiagovilasboas/rag-ou-mcp-a-pergunta-errada-cole-o-prompt-e-monte-os-dois-57kj</link>
      <guid>https://dev.to/tiagovilasboas/rag-ou-mcp-a-pergunta-errada-cole-o-prompt-e-monte-os-dois-57kj</guid>
      <description>&lt;p&gt;Pessoal, hoje todo mundo sabe falar de RAG, MCP e fine-tuning. Pouca gente tem isso no harness. Menos ainda com arquitetura: o que busca o doc, o que conecta, o que só muda o jeito.&lt;/p&gt;

&lt;p&gt;Não é falta de artigo. É falta de prática.&lt;/p&gt;

&lt;p&gt;Na prática: você pergunta o TTL do cache de idempotência. O agente lê o runbook: 60s, a janela. Você abre o código: 120. Ele não alucinou. O doc documenta a janela. O TTL nunca esteve no índice. Colar a nota no chat também não resolve.&lt;/p&gt;

&lt;p&gt;O modelo não ficou mais esperto. Ele só prevê a próxima palavra com o que está no contexto agora. RAG colocou a janela nesse contexto. Sem o trecho, ele chuta. Com o runbook pela metade, cita certo o número que estava lá: 60. A pergunta era 120. Fine-tuning não coloca o TTL nos pesos. Muda o jeito da frase, se o fato já estiver no turno.&lt;/p&gt;

&lt;p&gt;Como o trecho chega neste desk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pergunta → harness → MCP → RAG busca o doc → contexto → resposta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;MCP no meio não porque MCP “é pedir trecho”. MCP é protocolo: tool, resource, API, arquivo, banco. Neste fluxo a busca entra como tool. Fine-tuning fica fora do caminho.&lt;/p&gt;

&lt;p&gt;O que você leva daqui: cole o prompt e monte os dois. RAG busca o doc. MCP conecta o harness ao que o servidor expõe. Fine-tuning muda o jeito. Se a primeira ferramenta do último chat não casa com a camada, você ainda está na teoria.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Camada&lt;/th&gt;
&lt;th&gt;Papel&lt;/th&gt;
&lt;th&gt;No desk&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RAG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Busca o doc&lt;/td&gt;
&lt;td&gt;Logseq, Notion, Confluence. Devolve o trecho certo.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Protocolo&lt;/td&gt;
&lt;td&gt;Tool, resource, API, arquivo, banco. Neste fluxo: o harness alcança a busca por ele. Um host, duas tools: function calling nativo resolve.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fine-tuning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Muda o jeito&lt;/td&gt;
&lt;td&gt;Não guarda o doc. Só o padrão da resposta. Fora do runtime da pergunta.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Neste desk: monta a busca, depois o conector até ela. Se o formato ainda vazar, aí sim fine-tuning muda o jeito.&lt;/p&gt;

&lt;p&gt;Depois de montar, o fato que faltava entra no doc. TTL 120: uma linha no runbook e no índice. A próxima pergunta pega o 120. O modelo não retreina. Se o código mudou e o runbook não, volta a mesma pergunta: retrieve certo, índice pela metade. MCP muda o conector, não o corpus. Fine-tuning não recebe o 120. Só entra de novo se o &lt;em&gt;jeito&lt;/em&gt; mudou, e ainda assim depois de prompt e schema.&lt;/p&gt;

&lt;p&gt;O erro é pular etapa. Colar a nota no chat não é MCP. Um conector que além de buscar também grava, sem gente no meio, vira incidente.&lt;/p&gt;

&lt;p&gt;Abaixo: quatro perguntas. Se o último chat já responde, pode parar. Quem monta segue no prompt.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tabela de Conteúdo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. Nomeia o problema antes da ferramenta&lt;/li&gt;
&lt;li&gt;2. RAG: busca o doc, não o peso&lt;/li&gt;
&lt;li&gt;3. MCP: protocolo, não a busca&lt;/li&gt;
&lt;li&gt;4. Fine-tuning: muda o jeito, não o catálogo&lt;/li&gt;
&lt;li&gt;5. Cole isto no seu agente&lt;/li&gt;
&lt;li&gt;6. Context window grande não é desculpa pra jogar o repositório inteiro&lt;/li&gt;
&lt;li&gt;7. Não use o mesmo modelo (LLM) para tudo&lt;/li&gt;
&lt;li&gt;8. O modelo é o mesmo. O harness muda&lt;/li&gt;
&lt;li&gt;9. Se for implementar RAG, MCP ou fine-tuning de verdade, lê isto depois&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  1. Nomeia o problema antes da ferramenta
&lt;/h2&gt;

&lt;p&gt;Antes de ligar mais uma ferramenta, pergunta:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Você precisa de um &lt;strong&gt;texto que já existe&lt;/strong&gt; (política no Notion, ADR, README, o retry da API) e só quer o trecho certo? Isso muda em dias. É &lt;strong&gt;RAG&lt;/strong&gt;. O harness alcança essa busca por &lt;strong&gt;MCP&lt;/strong&gt; ou por function calling. Ou você precisa do &lt;strong&gt;agora&lt;/strong&gt;: o pipeline passou, o PR está aberto, o teste quebrou, o banco tem o registro? Isso não é RAG. Está no CI, no git, no banco: outra tool, no mesmo protocolo ou nativo.&lt;/li&gt;
&lt;li&gt;Se o agente errar, o pior é uma &lt;strong&gt;frase ruim&lt;/strong&gt; no chat, ou ele pode &lt;strong&gt;gravar de verdade&lt;/strong&gt;: merge, apagar arquivo, disparar pagamento, fazer deploy? Consultar e gravar podem ir no mesmo conector. Gravar precisa de gente no meio.&lt;/li&gt;
&lt;li&gt;O trecho certo &lt;strong&gt;já está na conversa&lt;/strong&gt; (você vê o arquivo, a regra, o JSON) e o que sai torto é só o &lt;strong&gt;formato&lt;/strong&gt;: mistura finding com palpite, tom errado, label errada? Aí não falta fato. Falta jeito. &lt;strong&gt;Fine-tuning&lt;/strong&gt;, depois de prompt e schema.&lt;/li&gt;
&lt;li&gt;Vocês estão &lt;strong&gt;colando o repositório inteiro&lt;/strong&gt; no chat para achar dois arquivos? Isso não é busca. É dump. Janela grande não acha o handler. Acha o meio.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Se o time grita fine-tuning e o fato nem entrou na conversa, vocês estão no item 1 fingindo que é o 4.&lt;/p&gt;

&lt;p&gt;Quem constrói confere no histórico da conversa. &lt;strong&gt;Qual foi a primeira ferramenta que o agente usou nesta rodada?&lt;/strong&gt; A tabela abaixo é o mesmo diagnóstico, em código.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;O que você perguntou&lt;/th&gt;
&lt;th&gt;Primeiro tool caro&lt;/th&gt;
&lt;th&gt;Primeiro tool barato&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Timeout, retry, contrato deste serviço&lt;/td&gt;
&lt;td&gt;Busca no README de janeiro&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Read&lt;/code&gt; do código agora. Reindex se o doc mentir.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O pipeline da &lt;code&gt;main&lt;/code&gt; passou?&lt;/td&gt;
&lt;td&gt;XML / JUnit no vetor&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gh run&lt;/code&gt; agora&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quem chama este handler?&lt;/td&gt;
&lt;td&gt;Dez &lt;code&gt;Read&lt;/code&gt; “só pra garantir”&lt;/td&gt;
&lt;td&gt;Mapa (grafo / LSP), depois 1–2 arquivos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON do review mistura finding e palpite&lt;/td&gt;
&lt;td&gt;LoRA no OpenAPI&lt;/td&gt;
&lt;td&gt;Schema no prompt. Finding ≠ hipótese.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Abre o último chat. O de ontem, não o demo. Primeira ferramenta: busca no doc, &lt;code&gt;Read&lt;/code&gt;/&lt;code&gt;gh&lt;/code&gt;, algo que grava, ou “vamos treinar”? Se não foi o barato da tabela, a camada estava errada.&lt;/p&gt;

&lt;p&gt;Duas perguntas que eu fiz no chat. Histórico anonimizado. A primeira ferramenta foi busca no vault, via MCP, nas duas. O que muda é o índice.&lt;/p&gt;

&lt;p&gt;Número de negócio. Doc e código bateram.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;você:     qual é a janela de idempotência neste serviço?
primeira: MCP read no vault  (hub)
depois:   MCP read  (runbook.md → 60s, ADR datado)
          grep      (middleware.php → 60)
doc:      60s
código:   60s
camada:   índice vivo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Detalhe de implementação. O runbook documenta a janela. Não o TTL do cache. Grep no vault: vazio. O número estava no código.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;você:     qual é o TTL do cache de idempotência?
primeira: MCP read no vault  (hub)
depois:   MCP read  (runbook.md → 60s, janela)
          grep no vault  (nada sobre TTL)
          Read  (middleware.php → 120)
doc:      vazio no TTL
código:   120s
camada:   índice pela metade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;O vault não mentiu. Documentou o fato de negócio. Quem debuga “por que a chave ainda está no cache depois da janela?” não acha resposta no índice. Vai pro &lt;code&gt;Read&lt;/code&gt;. A correção é uma linha no runbook, não LoRA. Dez &lt;code&gt;Read&lt;/code&gt; vs mapa: &lt;a href="https://dev.to/tiagovilasboas/indexar-o-codigo-fora-do-repo-como-economizar-tokens-sem-jogar-o-projeto-no-contexto-48c7"&gt;neste post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cartão + prompt: &lt;a href="https://gist.github.com/tiagovilasboas/083f155f527914353f2b4f6b75ccefcf" rel="noopener noreferrer"&gt;Antes de ligar RAG, MCP ou LoRA: vê a primeira ferramenta que o agente usou&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. RAG: busca o doc, não o peso
&lt;/h2&gt;

&lt;p&gt;Logseq, Notion, Confluence, o runbook. Mesma conta da abertura: o fato está no doc. RAG busca o trecho. Não treina o doc de novo.&lt;/p&gt;

&lt;p&gt;Isso mora no ADR, no runbook, no comentário do handler. Não mora no &lt;code&gt;gh pr view&lt;/code&gt; de agora. Busca a regra no doc. Consulta o PR aberto na ferramenta. Inverter é inventar política ou procurar ADR na API do GitHub.&lt;/p&gt;

&lt;p&gt;A janela estava no runbook. O TTL, não. Fine-tuning não escreve essa linha. Tool de GitHub também não. Se o fato mudou, você fornece o fato agora, na fonte. Treinar de novo é o caminho rígido e caro pra número que vive no runbook.&lt;/p&gt;

&lt;p&gt;O modelo sabe HTTP. Não sabe o contrato &lt;em&gt;deste&lt;/em&gt; serviço, a não ser que o trecho esteja no turno. RAG é a busca na pergunta, com fonte. Não é treinar de novo cada vez que o time muda o retry.&lt;/p&gt;

&lt;p&gt;O que RAG &lt;strong&gt;não&lt;/strong&gt; é: o status do Actions agora. Se muda a cada push, o índice mente educado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O que você ganha:&lt;/strong&gt; fato que atualiza sem treinar de novo, e dá pra apontar o arquivo. No trabalho: o modelo busca menos pra fora. Decide no contexto do time.&lt;br&gt;
&lt;strong&gt;O que você paga:&lt;/strong&gt; alguém tem que cuidar da base. Nota de janeiro no índice, política de março no jurídico: citação bonita, regra morta.&lt;/p&gt;

&lt;p&gt;O doc ainda não chegou no harness. Isso é o próximo passo.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. MCP: protocolo, não a busca
&lt;/h2&gt;

&lt;p&gt;MCP não é a busca. É o protocolo: tool, resource, API, arquivo, banco. O harness chama o que o servidor expõe.&lt;/p&gt;

&lt;p&gt;O fluxo da abertura é uma composição, não a definição. MCP no meio não porque “MCP pede trecho” seja o que MCP é. Porque neste desk a busca entra como tool.&lt;/p&gt;

&lt;p&gt;O Logseq não anda sozinho até o chat. Sem conector, você cola a nota. Com o conector, a tool de retrieve entra no turno. RAG continua sendo a busca. MCP, neste desk, é como o harness chega nela.&lt;/p&gt;

&lt;p&gt;Se o servidor está verde e o transcript ainda começa em dez &lt;code&gt;Read&lt;/code&gt;, você não ligou o RAG. Só instalou o protocolo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Um host, duas tools?&lt;/strong&gt; Function calling nativo resolve. MCP começa a valer quando a mesma busca precisa aparecer em mais de um IDE, ou quando o time não quer reinventar o conector a cada harness.&lt;/p&gt;

&lt;p&gt;O mesmo servidor pode expor &lt;em&gt;outra&lt;/em&gt; coisa: git, CI, banco, pagamento. Isso não vira RAG. Leitura da busca ≠ &lt;code&gt;push&lt;/code&gt;. HITL no que grava. Sem isso, o servidor “só de busca” vira force push. A sessão que consulta o manual não é a sessão que dispara o pagamento.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O que você ganha:&lt;/strong&gt; o harness chega no doc (Logseq, Notion, Confluence) sem colar a nota no chat. E, se precisar, chega em sistema vivo pelo mesmo protocolo, em outro cano.&lt;br&gt;
&lt;strong&gt;O que você paga:&lt;/strong&gt; superfície. Cada tool a mais no mesmo servidor é um jeito de errar no mundo real.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Fine-tuning: muda o jeito, não o catálogo
&lt;/h2&gt;

&lt;p&gt;Aqui o fine-tuning muda o jeito. Não busca o doc. Se a busca já devolve o trecho certo (RAG) e o harness já alcança essa busca (MCP ou function calling), e mesmo assim o formato vaza, aí conversa treino.&lt;/p&gt;

&lt;p&gt;Fine-tuning não é o próximo passo porque o hype pediu. Prompt e schema são o degrau do meio. Treino é o último. Ele muda &lt;em&gt;como&lt;/em&gt; o modelo responde: estilo, formato, padrão que se repete. Não é o jeito de ensinar as 400 rotas da API.&lt;/p&gt;

&lt;p&gt;Um limite só: eu não treinei um modelo grande neste laboratório. O passo barato que eu uso é schema no prompt. Finding, observação e hipótese em campos separados. Sem arquivo:linha não é finding. Fine-tuning seria treinar &lt;em&gt;esse&lt;/em&gt; jeito, mil vezes, se o schema não segurasse. Não seria gravar o OpenAPI nos pesos.&lt;/p&gt;
&lt;h3&gt;
  
  
  “Vamos treinar com o OpenAPI inteiro”
&lt;/h3&gt;

&lt;p&gt;Amanhã o endpoint muda. O modelo, não. O spec de terça ficou preso nos pesos. Cena pedagógica:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;você:     qual o path de cancelamento?
agente:   POST /v1/subscriptions/cancel  (spec de junho)
código:   POST /v2/billing/cancel        (desde agosto)
alguém:   "alucinou"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Não alucinou. Você pediu pra ele &lt;strong&gt;lembrar rota&lt;/strong&gt;. Rota é fato. Fato vai pra busca no spec, ou pra tool que lê o OpenAPI no repo. Nunca pro peso, se muda.&lt;/p&gt;

&lt;p&gt;Indexar o código-fonte no dataset de treino é o mesmo erro. O time muda o handler. Você ficou com um &lt;code&gt;Readme.bak&lt;/code&gt; caro, que não dá grep.&lt;/p&gt;
&lt;h3&gt;
  
  
  O JSON do review sai torto
&lt;/h3&gt;

&lt;p&gt;O arquivo certo já está no contexto. O modelo mistura “acho que é CSRF” com finding, quebra o JSON, escreve como blog.&lt;/p&gt;

&lt;p&gt;Aí &lt;em&gt;pode&lt;/em&gt; ser comportamento. Ainda não é abrir o painel de treino.&lt;/p&gt;

&lt;p&gt;Ordem: prompt claro, dois exemplos bons, &lt;strong&gt;schema&lt;/strong&gt;. Finding só com &lt;code&gt;arquivo:linha&lt;/code&gt;. Hipótese em outro campo.&lt;/p&gt;

&lt;p&gt;Se depois disso o formato &lt;em&gt;ainda&lt;/em&gt; vaza em mil reviews por dia, aí sim: exemplos do &lt;strong&gt;jeito certo&lt;/strong&gt; de reportar. Não o repositório no JSONL.&lt;/p&gt;
&lt;h3&gt;
  
  
  Quando fine-tuning vale
&lt;/h3&gt;

&lt;p&gt;Classificar issue o dia inteiro: bug, dívida, feature. O rótulo quase não muda. Um modelo menor sai mais barato que o modelo caro lendo prosa.&lt;/p&gt;

&lt;p&gt;Tom do time no review em escala, sem system prompt de oito mil tokens. O &lt;em&gt;conteúdo&lt;/em&gt; (qual handler, qual timeout) continua de fora: busca ou tool.&lt;/p&gt;

&lt;p&gt;“Não gera PoC, não abre PR nesta sessão” automático, quando o prompt já não segura.&lt;/p&gt;

&lt;p&gt;Regra: se a correção é “atualiza o README”, não é fine-tuning. Se ele &lt;em&gt;viu&lt;/em&gt; o código e mesmo assim escreve o review errado do mesmo jeito, mil vezes, aí conversa.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Cole isto no seu agente
&lt;/h2&gt;

&lt;p&gt;Isto não é receita de instalar RAG, MCP e LoRA. É o contrato das camadas, pra colar numa conversa nova. Se você não tem nada, o agente &lt;strong&gt;monta a semana 1 do jeito certo&lt;/strong&gt;: README do repo, tools nativas, fine-tuning depois. Arquiteto, não gerador de código. O bloco é longo de propósito.&lt;/p&gt;

&lt;p&gt;Conversa &lt;strong&gt;nova&lt;/strong&gt;. Cinco linhas do seu repo. Ele vasculha o que você colou (e o README/docs/MCP do tree, se o IDE deixar). Só então escolhe: 3 perguntas, 8, ou já o plano. Sem stack: default e semana 1. Stack complexa: até 8, aí espera. Não manda já commitar.&lt;/p&gt;

&lt;p&gt;Se devolver “vamos treinar o OpenAPI” ou “um MCP com as 40 APIs”, você pulou as perguntas. Manda o trecho no comentário.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/tiagovilasboas/083f155f527914353f2b4f6b75ccefcf" rel="noopener noreferrer"&gt;Antes de ligar RAG, MCP ou LoRA: vê a primeira ferramenta que o agente usou (prompt + cartão)&lt;/a&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an architect, not a code generator.

I am designing an agentic setup for a software team. I may paste repo context, a stack description, or a messy Slack thread. Do not assume Cursor, Claude, Copilot, or any other host. Talk in terms of layers.

Do not write application code in the first reply. Do not invent tools, datasets, or vendors I did not mention.

First pass, before any questionnaire: inspect what I pasted. If the harness can see the repo, look only at README, docs/ (or where docs are pointed), connector/MCP config, any existing index. A handful of Reads. Not ten. Not dump. Cite what you found. Then classify from that evidence: nothing vs complex. Do not pick 3 or 8 before this pass.

If the pass shows nothing (no RAG, no MCP, no LoRA): ask at most 3 short questions: (1) where do docs live today, (2) one IDE or several, (3) does anything need to write (push, pay, deploy). If I skip them or say I have nothing, assume: corpus = README/ADR in the repo; one host; native function calling (no MCP yet); fine-tune = not now; do not dump the repo. Then output the sections below in this same reply. Week-1 bullets must be doable with only that repo and that IDE. They are the first correct setup, not a shopping list.

If the pass already has enough, skip questions and output. If the pass shows a complex stack (several hosts, writes, existing MCP, index, or I proposed fine-tuning) and something material is still missing, ask up to 8 short questions, then stop and wait. The 8 are for that case. Not for the nothing-case.

Mental model (non-negotiable):
- This stack does not make the model smarter. It is harness discipline: directed context at query time, so next-token prediction is better grounded. At work, retrieve in-corpus first: less fishing on the public web, fewer exploratory Reads. Tokens drop vs dump or ten Reads, not vs an empty chat.
- RAG / retrieval = the knowledge base (Logseq, Notion, Confluence, ADRs, READMEs) plus search at query time. Cite sources. Not a substitute for live CI, git, or DB.
- MCP = a protocol (tools, resources, APIs, files, DBs). Not the corpus. Not RAG. This article's happy path is a composition: the harness reaches retrieval through MCP, or through native function calling. Native function calling is enough if I have one host and two tools.
- Tools that mutate (push, pay, email) may ride the same connector. They are not RAG. Read ≠ write. HITL on writes.
- Fine-tuning = behavior (format, tone, labels) AFTER retrieval is reachable and facts are right. Try system prompt, few-shot, and strict JSON schema first. Never fine-tune to inject an OpenAPI spec, a codebase, or API paths that change.
- Context budget = do not dump the monorepo. Prefer a code map / graph / LSP query, then 1–2 files. A large context window still loses the middle.
- Model role = pick by trained skill and claimed capability, not the IDE logo. The turn only tells you which of those skills you need now: daily craft, hard architecture, code review, security review.

Also respect:
- Least privilege: read tools ≠ write tools. git push, merge, migrate, deploy need a human in the loop. No unbounded shell “to make the demo work”.
- Exposing remember()/recall() as a tool does not make a memory policy.
- Exposing RAG as one MCP tool is allowed, but the agent must still know WHEN to search docs vs WHEN to call git/CI.

After you have enough answers, output exactly these sections in my language (pt-BR unless I write in English):

1. Diagnosis — for MY repo/workflow, what is knowledge vs action vs behavior vs budget vs model role. One paragraph.
2. RAG — what corpus (docs vs code), what must NEVER go in the index, how we know a hit is stale vs the tree.
3. MCP / protocol — what the harness can call. Table: server, what it exposes (retrieve vs git vs mutate), blast radius, HITL, MCP vs native function calling and why. If I treated MCP as a synonym of RAG, or as “the thing that asks for the snippet”, correct me: that is a composition, not the definition.
4. Fine-tune — “not now” or a precise behavior to learn, with the prompt/schema alternative we will try first. If I proposed fine-tuning OpenAPI or the repo, refuse and reroute to RAG or a tool that reads the spec from git.
5. Context budget — dump vs map. One indexer max.
6. Model posts — map daily craft / hard architecture / code review / security review onto models by trained skill and claimed capability, using whatever I already pay for. If I named none, propose the jobs without vendor lock-in.
7. Week-1 plan — 5 bullets I can do in the repo without buying a new course. No new logo unless I already have it. If I had nothing, these bullets are how I mount week 1 the right way.

End with 3 risks if I ignore the diagnosis.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O prompt já dá pra colar. As duas seções seguintes não são RAG nem MCP. São o que ainda quebra na prática: jogar o repo no context window, e trocar de modelo achando que montou camada.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Context window grande não é desculpa pra jogar o repositório inteiro
&lt;/h2&gt;

&lt;p&gt;“O context window é enorme, manda o monorepo.”&lt;/p&gt;

&lt;p&gt;Você não economizou. Enterrou o handler certo no meio.&lt;/p&gt;

&lt;p&gt;Na prática (cena pedagógica):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;você:     quem chama applyTimeout neste serviço?
dump:     monorepo no context window
agente:   cita vendor/retry/README.md
          "30s, 3 tentativas"  (achou o que parecia)
mapa:     applyTimeout ← PaymentHttpClient ← WebhookController
código:   src/http/client.ts, 5s, um caller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O modelo citou. Citou o arquivo errado. Context window grande entrega o meio. Não o handler.&lt;/p&gt;

&lt;p&gt;Indexar o repo no vetor e chamar de RAG também não resolve. Mapa responde &lt;em&gt;quem chama&lt;/em&gt;. Vetor de 40 mil chunks responde &lt;em&gt;parece com&lt;/em&gt;. Se o histórico começa em dez &lt;code&gt;Read&lt;/code&gt;, não tem mágica. Tem pasta no disco.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Não use o mesmo modelo (LLM) para tudo
&lt;/h2&gt;

&lt;p&gt;Isso não é a quarta camada. É o seletor. Trocar o logo não busca o doc, não conecta o harness e não muda o jeito.&lt;/p&gt;

&lt;p&gt;“Arruma o auth.”&lt;/p&gt;

&lt;p&gt;Pode ser typo no middleware. Um &lt;code&gt;if&lt;/code&gt;, um import, o token caiu no header errado. Um arquivo. Coding no fluxo. O modelo que se propõe a isso resolve e segue.&lt;/p&gt;

&lt;p&gt;Pode ser recorte de seis módulos. Gateway, sessão, cookie, refresh, o middleware que ainda deixa passar o path antigo. Vira desenho. ADR. Troca de contrato. Vários arquivos, horizonte longo. O pedido é o mesmo. O skill não é.&lt;/p&gt;

&lt;p&gt;Pode ser code review. O patch passou. Quem implementou quer terminar. Quem revisa quer discordar. Isso pede o teto, não o modelo que acabou de escrever o diff.&lt;/p&gt;

&lt;p&gt;Pode ser security review no source. O furo de autorização ficou no PR. Não é pentest. É ler o diff contra o contrato de auth.&lt;/p&gt;

&lt;p&gt;O texto do pedido sozinho não separa. “Arruma o auth” não diz se você precisa de um patch, de um recorte ou de um review. Quem escolhe modelo sem isso cola o skill errado no seletor.&lt;/p&gt;

&lt;p&gt;Não escolhe pela IDE. Escolhe pelo skill / capability que o modelo &lt;strong&gt;declara&lt;/strong&gt;. Sem ranking. Não rodei o leaderboard deles.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill / capability&lt;/th&gt;
&lt;th&gt;Vendor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Coding in the loop, ship&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://docs.x.ai/developers/grok-4-6" rel="noopener noreferrer"&gt;Grok 4.6 High&lt;/a&gt;: coding, agentic, knowledge work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-file recorte, ADR&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://www.anthropic.com/research/claude-opus-5" rel="noopener noreferrer"&gt;Claude Opus 5&lt;/a&gt;: complex agentic coding, long refactor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code review&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://www.anthropic.com/news/claude-fable-5-mythos-5" rel="noopener noreferrer"&gt;Claude Fable 5&lt;/a&gt;: highest capability, long SWE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security review (source)&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://www.anthropic.com/news/claude-fable-5-mythos-5" rel="noopener noreferrer"&gt;Claude Fable 5&lt;/a&gt;: Mythos-class. Cyber no GA, com safeguard&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;O modelo do dia a dia revisando o diff de auth é viés do próprio patch. Quem escreveu o patch não é quem caça o furo. Fable no seletor é o teto GA: &lt;a href="https://www.anthropic.com/news/claude-fable-5-mythos-5" rel="noopener noreferrer"&gt;Mythos-class com safeguard&lt;/a&gt;. Mythos 5 é o mesmo peso, acesso restrito. Code review e security review de source no PR. Não é pentest.&lt;/p&gt;

&lt;p&gt;Trocar de modelo não substitui RAG, MCP ou fine-tuning. Fine-tuning não substitui o README que faltava.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. O modelo é o mesmo. O harness muda
&lt;/h2&gt;

&lt;p&gt;Não ganhamos um modelo mais inteligente. Ganhamos um harness que para de chute: o trecho certo entra no turno, o context window não vira dump. Predição mais acertada. O cérebro é o mesmo.&lt;/p&gt;

&lt;p&gt;No trabalho isso pesa. A política já está no doc. O modelo para de completar com o treino público, para de abrir dez arquivos “só pra garantir”. Token cai quando a alternativa era dump ou dez &lt;code&gt;Read&lt;/code&gt;. Retrieve ainda gasta contexto. Gasta o certo.&lt;/p&gt;

&lt;p&gt;O que ganhamos não é “ter RAG, MCP e LoRA” no vocabulário. É ter busca, protocolo e jeito no harness, cada um no papel certo. Página velha não se resolve treinando. Colar no chat não é protocolo. Trocar o logo no seletor não atualiza o README.&lt;/p&gt;

&lt;p&gt;Dá pra usar no Kiro, no Cursor, no Claude Code, no Codex. A conta não é do logo. Knowledge base, conector, jeito, context window: isso mora no harness que você estiver. Um host, duas tools: function calling nativo resolve. Vários hosts, a mesma base: aí o MCP vale, pra não reinventar o conector em cada um. O histórico da primeira ferramenta funciona em todos. Se o primeiro movimento muda quando você troca de IDE, você não montou camada. Montou atalho daquele app.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Se for implementar RAG, MCP ou fine-tuning de verdade, lê isto depois
&lt;/h2&gt;

&lt;p&gt;Até aqui a gente só fechou a conta: o que é cada camada, o que não é, o que olhar no histórico. Chunk, servidor MCP, memória entre sessões, mapa do repo: isso não cabia neste texto. Se você for implementar isso no IDE, esses cinco links são o próximo passo. Na ordem. Cada um cobre uma coisa que eu deixei de fora de propósito.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://dev.to/tiagovilasboas/mesmo-com-graphrag-o-agent-se-perde-sem-contrato-de-memoria-hc8"&gt;Mesmo com GraphRAG, o agent se perde sem contrato de memória&lt;/a&gt;. Retrieve não é memória. Hub, o que gravar, o que não.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/tiagovilasboas/indexar-o-codigo-fora-do-repo-como-economizar-tokens-sem-jogar-o-projeto-no-contexto-48c7"&gt;Indexar o código fora do repo&lt;/a&gt;. Mapa vs dump. Context window grande não acha o handler. O histórico da primeira ferramenta mora lá, não aqui.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro" rel="noopener noreferrer"&gt;MCP: intro oficial&lt;/a&gt;. Host, client, server. Tools e resources. Como o protocolo existe de fato.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.anthropic.com/news/contextual-retrieval" rel="noopener noreferrer"&gt;Contextual retrieval (Anthropic)&lt;/a&gt;. Chunk, embedding, o que a busca devolve quando o índice é mais do que “joga no vetor”.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://platform.openai.com/docs/guides/fine-tuning" rel="noopener noreferrer"&gt;Fine-tuning (OpenAI)&lt;/a&gt;. Quando é comportamento. Quando não é fato. Dataset, não OpenAPI nos pesos.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Os cinco links são o passo seguinte, se for montar de verdade. O que este texto pede é não pular a camada. O &lt;a href="https://gist.github.com/tiagovilasboas/083f155f527914353f2b4f6b75ccefcf" rel="noopener noreferrer"&gt;prompt + cartão&lt;/a&gt; não instala a stack. Diagnostica a sua. Cola no Kiro, no Cursor, no Claude Code, no Codex.&lt;/p&gt;

&lt;p&gt;No seu último chat: a primeira ferramenta foi a busca do RAG (MCP ou function calling), outra tool no mesmo protocolo, ou ele tentou treinar o OpenAPI?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>mcp</category>
      <category>discuss</category>
    </item>
    <item>
      <title>O prompt de AppSec que eu criei achou 4 gaps de segurança</title>
      <dc:creator>Tiago Vilas Boas (Montanha)</dc:creator>
      <pubDate>Wed, 02 Sep 2026 03:58:17 +0000</pubDate>
      <link>https://dev.to/tiagovilasboas/prompt-appsec-4-gaps-autorizacao-1k77</link>
      <guid>https://dev.to/tiagovilasboas/prompt-appsec-4-gaps-autorizacao-1k77</guid>
      <description>&lt;p&gt;Pessoal, eu quase mandei uma lista de 23 "vulnerabilidades" pra mantenedores de código aberto.&lt;/p&gt;

&lt;p&gt;XSS, CORS, CSP, um SVG suspeito. O modelo tinha devolvido volume. Eu sentia que tinha trabalhado. Aí parei. Se eu fosse o cara do outro lado da issue, eu levaria isso a sério?&lt;/p&gt;

&lt;p&gt;A conta virou quando eu parei de pedir "audita meu código". Passei a escrever o contrato &lt;em&gt;antes&lt;/em&gt; de abrir o repo. Direção (o que o modelo pode afirmar) e disciplina (o que ele não pode). Isso não deixa o LLM mais inteligente. Controla o comportamento. Em segurança, isso é o que separa auditoria de lista.&lt;/p&gt;

&lt;p&gt;O que você leva daqui: o contrato (esqueleto), por que o scanner não substitui hunt, e as três portas pra não colar relatório no tracker. Os repos no meio são prova. Não são o ponto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tabela de Conteúdo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. Direção e disciplina não deixam o modelo mais inteligente&lt;/li&gt;
&lt;li&gt;2. O scanner vê o que está. O hunt vê o que falta&lt;/li&gt;
&lt;li&gt;3. Silêncio também é resultado&lt;/li&gt;
&lt;li&gt;4. As três portas&lt;/li&gt;
&lt;li&gt;5. O que você monta amanhã&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Direção e disciplina não deixam o modelo mais inteligente
&lt;/h2&gt;

&lt;p&gt;Esse é o ponto que eu demorei pra aceitar.&lt;/p&gt;

&lt;p&gt;O modelo já conhece OWASP. O que muda é o que ele tem permissão de afirmar. Sem isso, "audita meu código" devolve XSS fantasma, CORS, CSP, um SVG. Volume. A pergunta de segurança some: disto aqui, o que vira issue, o que vai pro e-mail privado, o que é silêncio?&lt;/p&gt;

&lt;p&gt;Direção amarra três coisas &lt;strong&gt;antes&lt;/strong&gt; da leitura:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Padrão externo. OWASP ASVS 5.0, com o ID do requisito. "Boas práticas" não conta.&lt;/li&gt;
&lt;li&gt;Invariante em uma frase, falsificável. "Webhooks deveriam ser autenticados" é opinião. "Todo handler de webhook de entrada verifica autenticidade do remetente antes de mutar estado" é hunt.&lt;/li&gt;
&lt;li&gt;Formato único. Finding, observação e hipótese não misturam.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Disciplina é o que corta o teatro. O texto que o modelo vê antes de abrir arquivo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Padrão: OWASP ASVS 5.0, com o ID do requisito. "Boas práticas" não conta.
Invariante: uma frase, falsificável.
Relatório: finding, observação e hipótese não misturam.

Sem arquivo:linha → não é finding.
CWE inventado → encerra a sessão.
Needs-runtime → não vira card.
Sem X de Y → não é hunt.
Estático. Sem PoC. Sem request no ambiente deles.
A sessão que encontra não é a sessão que corrige.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Esse último item parece burocracia até o modelo achar um gap e, empolgado, reescrever o &lt;code&gt;where&lt;/code&gt; do Prisma no mesmo fôlego, sem teste. Finding de um lado. Fix de outro.&lt;/p&gt;

&lt;p&gt;O prompt completo é &lt;strong&gt;privado&lt;/strong&gt;. Por trás dele tem regras numa knowledge base (quando hunt, quando varredura, o que é finding, as três portas) e skills do harness que amarram o modelo nisso. Sem a KB o modelo improvisa. Sem a skill ele lê o bloco e segue viagem. O esqueleto acima é o que cabe num artigo.&lt;/p&gt;

&lt;p&gt;Um limite honesto: isso só rende em modelo de raciocínio alto. Num modelo fraco, prompt bom organiza melhor o erro.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. O scanner vê o que está. O hunt vê o que falta
&lt;/h2&gt;

&lt;p&gt;Scanner estático (Semgrep, CodeQL, Bandit) acha padrão que existe. Chamada perigosa, query concatenada, header ausente. É rápido, determinístico, barato. Roda a cada push. Eu não troco isso por LLM.&lt;/p&gt;

&lt;p&gt;O que ele estruturalmente não vê é um controle ausente. "Esta rota deveria checar o dono do recurso antes de retornar, e não checa" não é regex. É propriedade semântica: o que é um tenant, que recurso pertence a quem, qual rota é pública de propósito.&lt;/p&gt;

&lt;p&gt;Por isso o Semgrep entra neste texto. Não porque ele tenha achado os gaps. Porque, no mesmo commit do caso das 174 rotas, ele foi o baseline de CI. Sete rulesets, 25 segundos:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;semgrep &lt;span class="nt"&gt;--config&lt;/span&gt; p/default &lt;span class="nt"&gt;--config&lt;/span&gt; p/typescript &lt;span class="nt"&gt;--config&lt;/span&gt; p/react &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--config&lt;/span&gt; p/nextjs &lt;span class="nt"&gt;--config&lt;/span&gt; p/owasp-top-ten &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--config&lt;/span&gt; p/javascript &lt;span class="nt"&gt;--config&lt;/span&gt; p/secrets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;23 findings. 17 eram &lt;code&gt;unsafe-formatstring&lt;/code&gt; em template. Zero de authorization.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Broken authorization (BOLA, OWASP API #1) não tem regra sintática estável: o padrão certo e o errado usam o mesmo &lt;code&gt;findUnique&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;O hunt começa invertido. Você escreve a invariante antes de abrir o arquivo. Enumera caminhos. Classifica: &lt;code&gt;enforced-explicitly&lt;/code&gt;, &lt;code&gt;inherited&lt;/code&gt;, &lt;code&gt;gap&lt;/code&gt;, &lt;code&gt;out-of-scope&lt;/code&gt;. No fim tem denominador. "Achei um IDOR" sem "1 de 174" não é hunt. É vibe.&lt;/p&gt;

&lt;p&gt;Naquele commit: &lt;strong&gt;173 de 174&lt;/strong&gt; rotas seguravam. Uma não. Esse é 1 dos 4 authorization gaps da rodada. Os outros 3 estão num alvo cujo detalhe de rota não entra neste post. Formbricks e Dub são hardening, não entram nos 4.&lt;/p&gt;

&lt;p&gt;Não é um contra o outro. O scanner pega injeção, XSS, secret, header. O hunt pega o caminho que usa o mesmo &lt;code&gt;findUnique&lt;/code&gt; e esquece o dono. Um é CI. O outro é pergunta. Verde no pipeline nunca respondeu se a invariante ainda vale.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Silêncio também é resultado
&lt;/h2&gt;

&lt;p&gt;A falha mais comum de IA em segurança é fabricar achado para não devolver silêncio. Disciplina se mede nisso.&lt;/p&gt;

&lt;p&gt;Busca ingênua por &lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt; em um app Next: oito hits, XSS crítico fantasma. Seguindo o dado até a função, é DOMPurify. Em SSR o React escapa. Zero finding. O que virou issue foi CORS contraditório (&lt;code&gt;*&lt;/code&gt; + credentials), que o navegador rejeita. &lt;a href="https://github.com/formbricks/formbricks/issues/9081" rel="noopener noreferrer"&gt;formbricks#9081&lt;/a&gt;. Tinha um SVG &lt;code&gt;Needs-runtime&lt;/code&gt;. Não virou card. Prompt solto teria aberto dois. O dirigido abriu um.&lt;/p&gt;

&lt;p&gt;Webhook de entrada: 7 de 7 autenticam o remetente. Semgrep no escopo: 0. Autenticidade não é padrão sintático. Dois HMAC com &lt;code&gt;!==&lt;/code&gt; no mesmo repo que já usa &lt;code&gt;timingSafeEqual&lt;/code&gt;. Observação, não gap da invariante. &lt;a href="https://github.com/dubinc/dub/issues/4415" rel="noopener noreferrer"&gt;dubinc/dub#4415&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Mesma classe, outro alvo. No &lt;a href="https://github.com/calcom/cal.com" rel="noopener noreferrer"&gt;Cal.com&lt;/a&gt;, 8 de 8 webhooks de entrada verificam o remetente antes de mutar. HitPay, o token do Google Calendar e o secret de credential-sync ainda comparam com &lt;code&gt;!==&lt;/code&gt;. O BTCPay, no mesmo monorepo, já usa &lt;code&gt;timingSafeEqual&lt;/code&gt;. &lt;a href="https://github.com/calcom/cal.diy/issues/30100" rel="noopener noreferrer"&gt;cal.com#30100&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Axios, gin, Guzzle, Medusa, Documenso: invariante manteve. 3/3, 4/4, 11/11. Não abri issue. Abrir card pra dizer "olhei e está ok" é o mesmo ruído que o modelo solto fabrica.&lt;/p&gt;

&lt;p&gt;Daemon local é o mesmo teste com outra forma. No &lt;a href="https://github.com/magnitudedev/magnitude" rel="noopener noreferrer"&gt;Magnitude&lt;/a&gt; o ACN já escuta em loopback. Isso é controle. CORS na porta pública ainda refletia &lt;code&gt;Origin: null&lt;/code&gt;. Markdown passava &lt;code&gt;href&lt;/code&gt; sem allowlist de protocolo. Três issues no tracker que o README aponta: &lt;a href="https://github.com/magnitudedev/magnitude/issues/78" rel="noopener noreferrer"&gt;#78&lt;/a&gt;, &lt;a href="https://github.com/magnitudedev/magnitude/issues/79" rel="noopener noreferrer"&gt;#79&lt;/a&gt;, &lt;a href="https://github.com/magnitudedev/magnitude/issues/80" rel="noopener noreferrer"&gt;#80&lt;/a&gt;. Shell do agent (&lt;code&gt;-c&lt;/code&gt;) não virou finding: é a ferramenta do produto. Dashboard de debug: o maintainer já tinha fechado essa classe no tracker. Não abri mais três cards no dia seguinte. Volume aqui também seria ruído.&lt;/p&gt;

&lt;p&gt;A prova compacta, estático, sem PoC:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Alvo&lt;/th&gt;
&lt;th&gt;O que a disciplina fez&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/formbricks/formbricks" rel="noopener noreferrer"&gt;Formbricks&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;XSS não confirmado. Hardening CORS → &lt;a href="https://github.com/formbricks/formbricks/issues/9081" rel="noopener noreferrer"&gt;#9081&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://github.com/axios/axios" rel="noopener noreferrer"&gt;axios&lt;/a&gt;, &lt;a href="https://github.com/gin-gonic/gin" rel="noopener noreferrer"&gt;gin&lt;/a&gt;, &lt;a href="https://github.com/guzzle/guzzle" rel="noopener noreferrer"&gt;Guzzle&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;invariante manteve. Silêncio.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/dubinc/dub" rel="noopener noreferrer"&gt;Dub&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;7/7 autenticam. Timing → &lt;a href="https://github.com/dubinc/dub/issues/4415" rel="noopener noreferrer"&gt;#4415&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/calcom/cal.com" rel="noopener noreferrer"&gt;Cal.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;8/8 autenticam. Timing → &lt;a href="https://github.com/calcom/cal.diy/issues/30100" rel="noopener noreferrer"&gt;#30100&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt;, &lt;a href="https://github.com/documenso/documenso" rel="noopener noreferrer"&gt;Documenso&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;payment idempotency e file namespace: denominador cheio, 0 gap. Silêncio.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/magnitudedev/magnitude" rel="noopener noreferrer"&gt;Magnitude&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;daemon local. &lt;a href="https://github.com/magnitudedev/magnitude/issues/78" rel="noopener noreferrer"&gt;#78&lt;/a&gt; &lt;a href="https://github.com/magnitudedev/magnitude/issues/79" rel="noopener noreferrer"&gt;#79&lt;/a&gt; &lt;a href="https://github.com/magnitudedev/magnitude/issues/80" rel="noopener noreferrer"&gt;#80&lt;/a&gt;. Dashboard de debug: silêncio.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plane&lt;/td&gt;
&lt;td&gt;3 gaps de object-level authorization. Canal privado. Sem path aqui.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/juice-shop/juice-shop" rel="noopener noreferrer"&gt;Juice Shop&lt;/a&gt; e &lt;a href="https://github.com/WebGoat/WebGoat" rel="noopener noreferrer"&gt;WebGoat&lt;/a&gt; não estão nesta tabela. São lab de propósito inseguro: o SQL concatenado já é o produto. Abrir issue ali seria o volume que este post recusa. O complemento deste fio: &lt;a href="https://dev.to/tiagovilasboas/13-findings-no-juice-shop-nenhuma-virou-issue-340p"&gt;13 findings no Juice Shop. Nenhuma virou issue.&lt;/a&gt; Lab calibra precisão. Não prova as três portas.&lt;/p&gt;

&lt;p&gt;Se o seu critério de sucesso for volume, metade dessa tabela parece desperdício. Pra mim as linhas em silêncio são as que sustentam as outras. Impacto verificável não é o mesmo que "issue aceita": a #9081 ganhou &lt;code&gt;security&lt;/code&gt; e &lt;code&gt;agent-ready&lt;/code&gt;; a #4415 entrou no Linear deles como ENG-1732. As duas continuam abertas. A &lt;a href="https://github.com/calcom/cal.diy/issues/30100" rel="noopener noreferrer"&gt;#30100&lt;/a&gt; é a mesma porta: hardening, sem PoC.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. As três portas
&lt;/h2&gt;

&lt;p&gt;Disciplina de divulgação é a mesma disciplina de finding. Least privilege aqui é o blast radius do texto. HITL é você escolhendo a porta, não o modelo. Audit trail é a issue ou o e-mail, nunca o transcript.&lt;/p&gt;

&lt;p&gt;Issue pública: hardening. Bypass, injeção, secret, authorization gap: privado primeiro. Sem &lt;code&gt;SECURITY.md&lt;/code&gt;, o README ainda pode apontar o tracker: aí hardening vai pra issue, como no Magnitude. O resto continua e-mail ou advisory, não thread de exploit.&lt;/p&gt;

&lt;p&gt;Copia. Cola no fim da sessão, antes de abrir o browser.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;O e-mail da trilha, com os links do que foi aberto e o porquê do silêncio:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  5. O que você monta amanhã
&lt;/h2&gt;

&lt;p&gt;Pega o repo que você mantém. Escreve uma invariante falsificável antes de abrir arquivo. "Todo webhook de entrada verifica assinatura antes de mutar estado" serve.&lt;/p&gt;

&lt;p&gt;Roda o scanner primeiro. Depois pede ao modelo o passe só naquela invariante, com denominador e &lt;code&gt;arquivo:linha&lt;/code&gt;, sem PoC, sem fix na mesma sessão.&lt;/p&gt;

&lt;p&gt;Se voltar "23 findings" sem &lt;code&gt;X&lt;/code&gt; de &lt;code&gt;Y&lt;/code&gt; e sem citação, você não tem auditoria. Tem lista. Faltou direção. Faltou disciplina.&lt;/p&gt;

&lt;p&gt;No último output de "audit" que um modelo te deu: você teria aberto issue pública, mandado e-mail privado, ou deixado nada? Se a resposta honesta for "ia colar o relatório inteiro no tracker", a porta ainda está solta.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sobre o produto&lt;/strong&gt;: o prompt é privado. Neste post vai o esqueleto. Por trás dele tem regras numa knowledge base e skills no harness. O pack (metodologia, cases, templates) está sendo refinado. Se você quer ser avisado quando sair mais, ou tem caso de uso específico, me chama no &lt;a href="https://linkedin.com/in/tiagovilasboas" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; ou &lt;a href="https://github.com/tiagovilasboas" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>opensource</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Mesmo com GraphRAG, o agent se perde sem contrato de memória</title>
      <dc:creator>Tiago Vilas Boas (Montanha)</dc:creator>
      <pubDate>Fri, 28 Aug 2026 17:50:21 +0000</pubDate>
      <link>https://dev.to/tiagovilasboas/mesmo-com-graphrag-o-agent-se-perde-sem-contrato-de-memoria-hc8</link>
      <guid>https://dev.to/tiagovilasboas/mesmo-com-graphrag-o-agent-se-perde-sem-contrato-de-memoria-hc8</guid>
      <description>&lt;p&gt;Pessoal, o agent puxou um trecho bonito. Similaridade alta. Confiança alta. Estava no stub errado.&lt;/p&gt;

&lt;p&gt;Não foi “RAG fraco”. Foi porta errada. Índice caro, lixo na entrada.&lt;/p&gt;

&lt;p&gt;O post anterior foi sobre &lt;a href="https://dev.to/tiagovilasboas/indexar-o-codigo-fora-do-repo-como-economizar-tokens-sem-jogar-o-projeto-no-contexto-48c7"&gt;consultar o grafo de código&lt;/a&gt;: token cai quando o agent pergunta ao mapa, não quando o gitignore está certo. Aqui é a outra conta. Vector, GraphRAG, híbrido: &lt;strong&gt;como recuperar&lt;/strong&gt;. Contrato: &lt;strong&gt;o que pode ser lembrado e por onde se entra&lt;/strong&gt;. Sem a segunda, o índice só erra mais rápido.&lt;/p&gt;

&lt;p&gt;A prova também não é o vendor. É o transcript. Primeiro passo foi abrir o hub canônico, ou foi grep no cemitério?&lt;/p&gt;

&lt;h2&gt;
  
  
  Tabela de Conteúdo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. O índice acertou. O mundo, não&lt;/li&gt;
&lt;li&gt;2. O que o mercado está vendendo&lt;/li&gt;
&lt;li&gt;3. A prova está na porta&lt;/li&gt;
&lt;li&gt;4. O contrato em quatro peças&lt;/li&gt;
&lt;li&gt;5. O que eu corto amanhã&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. O índice acertou. O mundo, não
&lt;/h2&gt;

&lt;p&gt;A pergunta clássica era &lt;em&gt;qual banco de vetor?&lt;/em&gt; A de 2026 é &lt;em&gt;vector, grafo ou os dois?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A minha ficou outra: quando o agent pergunta “onde está X?”, ele entra pelo hub ou grepa o bak?&lt;/p&gt;

&lt;p&gt;Se for a segunda, tanto faz o logo. Você indexou o labirinto.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. O que o mercado está vendendo
&lt;/h2&gt;

&lt;p&gt;O debate público é retrieval. Lookup (“qual o prazo neste contrato?”) vs síntese (“quais os riscos em todos?”). Vector costuma ganhar no primeiro. GraphRAG no segundo, caro de manter. Híbrido roteia. Um recorte com data: &lt;a href="https://cruxdigits.nl/blog/rag-vs-graphrag-2026/" rel="noopener noreferrer"&gt;vector vs GraphRAG, 2026&lt;/a&gt;. Snapshot. Não ranking.&lt;/p&gt;

&lt;p&gt;Isso importa. Não decide se &lt;code&gt;legado/&lt;/code&gt; pode ser porta. Não decide se o agent mistura diário com runbook no mesmo contexto. Embedding “certo” em guia morto: política ausente.&lt;/p&gt;

&lt;p&gt;No meu setup o contrato veio antes do vetor. Não estou dizendo que vetor é inútil. Estou dizendo que porta entrega valor no dia zero. Eval de recall é outra conta. Um limite só: contrato não substitui GraphRAG quando a pergunta é síntese em grafo de entidades, e não é AppSec de prompt sozinho.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. A prova está na porta
&lt;/h2&gt;

&lt;p&gt;Mesma lógica do grafo de código. Lá eu olho se o primeiro tool foi query ou o décimo &lt;code&gt;Read&lt;/code&gt;. Aqui eu olho se o agent &lt;strong&gt;cita o hub&lt;/strong&gt; ou só a similaridade.&lt;/p&gt;

&lt;p&gt;Pergunta sintético: &lt;em&gt;qual a regra de entrada da memória do agent?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Turno caro (reconstruído, não é log de produção):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grep "memória" (em tudo)
Read README.bak
Read (mais três hits no lixo)
→ responde com confiança, regra invertida
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Turno barato:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scope = meta / ops
Read o hub
segue o canônico
grep só depois, se precisar
→ a resposta diz por qual hub entrou
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se o agent não consegue dizer &lt;strong&gt;por qual porta entrou&lt;/strong&gt;, você não tem contrato. Tem índice.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. O contrato em quatro peças
&lt;/h2&gt;

&lt;p&gt;Cabem em qualquer vault. Os nomes das caixas são seus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fonte única.&lt;/strong&gt; Um canônico. O resto é espelho ou lixo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scopes.&lt;/strong&gt; Pessoal, carreira, ops, meta, o que for. Página nova declara a caixa. Sem isso o search mistura incidente com diário.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Porta.&lt;/strong&gt; Escolhe o scope, &lt;strong&gt;abre o hub&lt;/strong&gt;, segue canônicos. Search é complemento. “Onde está X?” só com grep é o anti-padrão.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hop.&lt;/strong&gt; Redirect fino. Um pulo. Labirinto de stub é indexar lixo de propósito.&lt;/p&gt;

&lt;p&gt;O que nunca vai pro contexto: segredo, PII, case que identifica empresa. Least privilege de memória. Não é feature do Pinecone.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. O que eu corto amanhã
&lt;/h2&gt;

&lt;p&gt;Copia. Cola no vault.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Memory contract (agente)&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; Fonte canônica: um vault, não cinco?
&lt;span class="p"&gt;2.&lt;/span&gt; Scopes: o que cada caixa pode conter?
&lt;span class="p"&gt;3.&lt;/span&gt; Página nova declara scope?
&lt;span class="p"&gt;4.&lt;/span&gt; Porta: hub-first por tipo de pergunta?
&lt;span class="p"&gt;5.&lt;/span&gt; Redirect: hop máximo?
&lt;span class="p"&gt;6.&lt;/span&gt; Proibido como entrada: legado, lixeira, bak?
&lt;span class="p"&gt;7.&lt;/span&gt; Search é complemento, nunca substituto do hub?
&lt;span class="p"&gt;8.&lt;/span&gt; Nunca no contexto: segredo, PII, case identificável?
&lt;span class="p"&gt;9.&lt;/span&gt; Como você sabe que errou? (cita o hub usado)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No seu último “RAG”: o agent entrou por &lt;strong&gt;porta&lt;/strong&gt;, por &lt;strong&gt;similaridade&lt;/strong&gt;, ou por &lt;strong&gt;grep no bak&lt;/strong&gt;? O que você proibiria como entrada amanhã?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Consultar o grafo economiza token. Tirar o índice do git, não</title>
      <dc:creator>Tiago Vilas Boas (Montanha)</dc:creator>
      <pubDate>Thu, 27 Aug 2026 18:41:59 +0000</pubDate>
      <link>https://dev.to/tiagovilasboas/indexar-o-codigo-fora-do-repo-como-economizar-tokens-sem-jogar-o-projeto-no-contexto-48c7</link>
      <guid>https://dev.to/tiagovilasboas/indexar-o-codigo-fora-do-repo-como-economizar-tokens-sem-jogar-o-projeto-no-contexto-48c7</guid>
      <description>&lt;p&gt;Pessoal, eu ia vender a tese errada.&lt;/p&gt;

&lt;p&gt;“Tirei o índice do git, então a sessão ficou barata.” Mentira educada. O modelo não lê gitignore. Se o agent abre dez arquivos, você pagou dez arquivos. A pasta do índice pode estar no lixo do disco que a fatura não muda.&lt;/p&gt;

&lt;p&gt;O que mudou na minha cabeça: &lt;strong&gt;a prova não é a tool instalada. É o transcript.&lt;/strong&gt; Primeiro tool foi query/neighbors/go-to-def, ou foi &lt;code&gt;Read&lt;/code&gt; de um arquivo “suspeito”?&lt;/p&gt;

&lt;p&gt;Token cai quando o agent pergunta ao mapa com orçamento. Fora do git só impede o mapa de ir no PR. São contas diferentes. Graphify, Serena, LSP, índice do editor: o logo não importa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tabela de Conteúdo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. Eu misturava três coisas&lt;/li&gt;
&lt;li&gt;2. A prova está no transcript&lt;/li&gt;
&lt;li&gt;3. Se o seu indexer não é o meu&lt;/li&gt;
&lt;li&gt;4. O que eu corto (e o que eu não vendo como economia)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Eu misturava três coisas
&lt;/h2&gt;

&lt;p&gt;Uma é política: o que pode ser lembrado, por qual porta se entra. Isso é memória de domínio. Outro post.&lt;/p&gt;

&lt;p&gt;Outra é o turno: “quem chama este handler?” sem pagar o monorepo. Isso é mapa de código + budget.&lt;/p&gt;

&lt;p&gt;A terceira é onde o arquivo do mapa mora. Disco, gitignore, cache fora da árvore. Blast radius. Quase zero na fatura.&lt;/p&gt;

&lt;p&gt;Eu vendi a terceira como se fosse a segunda. Por isso o texto antigo parecia raso.&lt;/p&gt;

&lt;p&gt;Se a pergunta é “o que a gente decidiu sobre retry?”, grep no repo é porta errada. Se é “quem chama?”, despejar dez arquivos “pra garantir” também é.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. A prova está no transcript
&lt;/h2&gt;

&lt;p&gt;Não tenho % de token pra te vender. Tenho um hábito que qualquer um reproduz: &lt;strong&gt;abre a sessão e conta os tools.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pergunta sintético (de propósito): &lt;em&gt;quem chama o handler de &lt;code&gt;charge.paid&lt;/code&gt;, e o que quebra se o payload mudar?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Turno que eu classifico caro (reconstruído, não é log de produção):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grep charge.paid
Read WebhookController.php
Read ChargePaidHandler.php
Read PayloadDto.php
Read tests/ChargePaidTest.php
Read (mais quatro “só pra garantir”)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Você pagou o repo pra uma pergunta de vizinho.&lt;/p&gt;

&lt;p&gt;Turno que eu classifico barato:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;neighbors / path / go-to-def  →  ChargePaidHandler
Read ChargePaidHandler.php
Read (no máximo mais um caller)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;O mapa já existia. O agent perguntou. Aí leu pouco.&lt;/p&gt;

&lt;p&gt;Como eu sei que “o Graphify está ajudando”? Não é porque o MCP está verde. É porque o &lt;strong&gt;primeiro&lt;/strong&gt; tool não é um &lt;code&gt;Read&lt;/code&gt; de arquivo aleatório. Se o indexer está instalado e o transcript ainda começa com dez &lt;code&gt;Read&lt;/code&gt;, você não economizou. Só gerou pasta de índice.&lt;/p&gt;

&lt;p&gt;Índice velho tem cara própria: o agent cita função que você apagou ontem, com confiança. Aí reindexa. CLI (&lt;code&gt;test&lt;/code&gt;, &lt;code&gt;git status&lt;/code&gt;, build) é outra conta. Comprime ou lê o trecho. Não mistura com o grafo.&lt;/p&gt;

&lt;p&gt;E o gitignore: vários indexers despejam output na raiz. Ignore. Isso não é a economia. É pra o mapa não ir no PR.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Se o seu indexer não é o meu
&lt;/h2&gt;

&lt;p&gt;No desk o mapa é Graphify. A conta não precisa do nome.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Você tem&lt;/th&gt;
&lt;th&gt;Sinal caro no transcript&lt;/th&gt;
&lt;th&gt;Sinal barato&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grafo / symbol index&lt;/td&gt;
&lt;td&gt;Instalar e começar em &lt;code&gt;Read&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Query / neighbors / go-to-def, depois 1–2 arquivos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Só grep&lt;/td&gt;
&lt;td&gt;Dez arquivos “pra garantir”&lt;/td&gt;
&lt;td&gt;Grep estreito + 1–2 arquivos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dois indexers&lt;/td&gt;
&lt;td&gt;Dois mapas, zero consulta&lt;/td&gt;
&lt;td&gt;Um por sessão&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Dois indexers na mesma conversa dilui o hábito que importa: &lt;strong&gt;consultar&lt;/strong&gt;. Mede pelo transcript, não pelo setup.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. O que eu corto (e o que eu não vendo como economia)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/tiagovilasboas/ac7fd45bcda942c850a70f3a3596d967" rel="noopener noreferrer"&gt;Gist&lt;/a&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Corta contexto: perguntar ao mapa com budget; Read cirúrgico; CLI resumido; um indexer.&lt;/p&gt;

&lt;p&gt;Não corta contexto (e mesmo assim faça): output fora do git; saber quem lê o mapa; reindex quando ele mentir.&lt;/p&gt;

&lt;p&gt;Fora do git protege o mapa. &lt;strong&gt;Consulta com budget&lt;/strong&gt; é o que corta a janela.&lt;/p&gt;

&lt;p&gt;No seu último turno: o primeiro tool foi &lt;strong&gt;query&lt;/strong&gt;, &lt;strong&gt;Read em massa&lt;/strong&gt;, ou &lt;strong&gt;dump de CLI&lt;/strong&gt;? O que você cortaria na próxima?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Ownership não é o PR. É não deixar o problema só na sua cabeça</title>
      <dc:creator>Tiago Vilas Boas (Montanha)</dc:creator>
      <pubDate>Thu, 27 Aug 2026 18:01:31 +0000</pubDate>
      <link>https://dev.to/tiagovilasboas/ownership-nao-e-o-pr-e-nao-deixar-o-problema-so-na-sua-cabeca-57gg</link>
      <guid>https://dev.to/tiagovilasboas/ownership-nao-e-o-pr-e-nao-deixar-o-problema-so-na-sua-cabeca-57gg</guid>
      <description>&lt;p&gt;Pessoal, tem um padrão que eu vi demais em time que já sabe entregar.&lt;/p&gt;

&lt;p&gt;O erro está no board de observabilidade há meses. Alguém já comentou no canal. Suporte eventualmente reclama. Nunca virou card. Um dia vira incidente.&lt;/p&gt;

&lt;p&gt;Aí a pergunta aparece, desconfortável: &lt;strong&gt;de quem era a responsabilidade?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Eu costumava responder errado. Ownership, pra mim, era pegar o código. Se eu vi, eu conserto. Ou o contrário: se não tem task, não é comigo. Os dois lados quebram o time.&lt;/p&gt;

&lt;p&gt;O que você leva daqui: uma distinção de uma frase e um ritual de três perguntas. &lt;strong&gt;Ver um problema não te obriga a implementá-lo. Obriga você a não fingir que não viu.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tabela de Conteúdo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. Todo mundo via. Ninguém possuía&lt;/li&gt;
&lt;li&gt;2. Quatro posturas: a pergunta muda&lt;/li&gt;
&lt;li&gt;3. See it, own it. OWN não é CODE&lt;/li&gt;
&lt;li&gt;4. Evidência antes de feeling&lt;/li&gt;
&lt;li&gt;5. Três perguntas, três saídas&lt;/li&gt;
&lt;li&gt;6. Onde o seu time para?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Todo mundo via. Ninguém possuía
&lt;/h2&gt;

&lt;p&gt;O salto não é de júnior pra Staff. É de pergunta.&lt;/p&gt;

&lt;p&gt;Enquanto a unidade for o card, o time otimiza “done”. O problema que não virou card continua existindo. Só mudou de lugar: saiu do board e foi morar na cabeça de três pessoas.&lt;/p&gt;

&lt;p&gt;Eu fiquei um tempo nos dois extremos. Herói (vi, então eu implemento). Ou espectador (não tem task, não é comigo). Os dois normalizam o mesmo buraco: o sistema não tem dono do &lt;em&gt;problema&lt;/em&gt;, só dono do &lt;em&gt;ticket&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Quatro posturas: a pergunta muda
&lt;/h2&gt;

&lt;p&gt;Nenhuma é “errada” sozinha. O problema é o time inteiro parar no primeiro degrau.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Postura&lt;/th&gt;
&lt;th&gt;Pergunta que guia&lt;/th&gt;
&lt;th&gt;Sinal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Task&lt;/td&gt;
&lt;td&gt;“Está done?”&lt;/td&gt;
&lt;td&gt;Fecha o card sem olhar comportamento em produção&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delivery&lt;/td&gt;
&lt;td&gt;“Está em produção?”&lt;/td&gt;
&lt;td&gt;Acompanha o deploy, não o resultado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outcome&lt;/td&gt;
&lt;td&gt;“Resolveu o problema do usuário?”&lt;/td&gt;
&lt;td&gt;Olha métrica depois do merge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ownership&lt;/td&gt;
&lt;td&gt;“O que mais está errado aqui?”&lt;/td&gt;
&lt;td&gt;Percebe o adjacente &lt;strong&gt;sem&lt;/strong&gt; esperar task&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Task e delivery
&lt;/h3&gt;

&lt;p&gt;Task pergunta pelo card. Delivery pergunta pelo deploy. As duas são necessárias. Nenhuma das duas pergunta se o usuário parou de sofrer, nem se o modo de falha do lado ainda está aberto.&lt;/p&gt;

&lt;h3&gt;
  
  
  Outcome e ownership
&lt;/h3&gt;

&lt;p&gt;Outcome pergunta pelo resultado. Ownership pergunta pelo sistema: o que mais está errado &lt;em&gt;aqui&lt;/em&gt;, mesmo sem task.&lt;/p&gt;

&lt;p&gt;Ownership não substitui as outras. Ela impede que o time chame de “resolvido” o que só foi &lt;em&gt;movido&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. See it, own it. OWN não é CODE
&lt;/h2&gt;

&lt;p&gt;Na prática eu uso quatro passos. Nomes feios de propósito, pra não virar poster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SEE          percebi algo estranho
UNDERSTAND   isso importa? recorrente? quem se ferrar?
EXPOSE       o problema precisa de dado, não de feeling
OWN          existe decisão ou dono. Inclusive “aceitamos o risco”.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;OWN não significa CODE.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pode ser um card com contexto de verdade. Uma conversa com quem prioriza. Um parágrafo escrito. Uma investigação com prazo. Inclusive: “vamos conviver com isso por enquanto”, desde que isso seja &lt;strong&gt;decisão&lt;/strong&gt;, não esquecimento.&lt;/p&gt;

&lt;p&gt;Se ownership virar “quem viu implementa”, o Senior/TL vira o único adulto da sala. Isso não escala. E queima gente boa.&lt;/p&gt;
&lt;h3&gt;
  
  
  O que isso não é
&lt;/h3&gt;

&lt;p&gt;Ownership não é resolver tudo que passar na timeline, furar priorização porque você ficou inquieto, trabalhar fora do horário pra “provar” que é dono, nem caçar culpado no post-mortem.&lt;/p&gt;

&lt;p&gt;Se o framework criar ansiedade, ele falhou. O objetivo é sistema mais saudável, não indivíduo exausto.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Evidência antes de feeling
&lt;/h2&gt;

&lt;p&gt;“Corrigimos um bug” é output.&lt;/p&gt;

&lt;p&gt;O que costuma destravar priorização é outra frase:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Problema → evidência → impacto → ação → (depois) resultado.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Exemplo sintético, de propósito:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Uma API crítica seguia respondendo 200. Em três semanas o P95 foi de ~400 ms para quase 2 s. Ninguém tinha aberto incidente porque “ainda funciona”. Alguém colocou o gráfico no canal e perguntou o que tinha mudado. Aí deixou de ser feeling.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sem número, o problema compete com o backlog e perde. Com número, ele vira escolha consciente.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dora.dev/research/2023/dora-report/" rel="noopener noreferrer"&gt;DORA&lt;/a&gt; coloca o dedo nisso há anos: time de alto desempenho não é só o que mergeia rápido. É o que &lt;strong&gt;detecta e responde&lt;/strong&gt;. Achar tarde custa mais. A indústria de qualidade, incluindo trabalhos associados ao &lt;a href="https://www.nist.gov/publications/economic-impacts-inadequate-infrastructure-software-testing" rel="noopener noreferrer"&gt;NIST&lt;/a&gt;, repete isso em variações. O ponto prático pra mim não é o fator exato. É que “ainda está 200” não é saúde.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Três perguntas, três saídas
&lt;/h2&gt;

&lt;p&gt;Dá pra usar numa retro, num 1:1 ou sozinho no fim da semana. Cópia solta: &lt;a href="https://gist.github.com/tiagovilasboas/f6d644849f63a8b02187ac2e3c50304d" rel="noopener noreferrer"&gt;gist do radar&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Que problema você viu nas últimas duas semanas que &lt;strong&gt;ninguém está tratando&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;Se continuar um mês, &lt;strong&gt;o que piora&lt;/strong&gt; (usuário, on-call, lead time)?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quem deveria ser o owner&lt;/strong&gt;, ou quem decide que não tem owner agora?&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Ignorar, investigar ou abrir card
&lt;/h3&gt;

&lt;p&gt;Cada item sai com um status, sem teatro:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ignorar com consciência&lt;/strong&gt; (risco aceito, data pra rever);&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;investigar&lt;/strong&gt; (hipótese + evidência);&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;abrir card&lt;/strong&gt; (com as duas linhas de cima, não “olhar depois”).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quinze minutos de retro bastam: cada pessoa traz &lt;strong&gt;um&lt;/strong&gt; item. Sem ranking de herói. Se ninguém trouxer nada, o dado também vale: ou o sistema está saudável, ou o time ainda não se sente seguro pra expor.&lt;/p&gt;

&lt;p&gt;Se a mesma coisa aparecer toda quinzena, o problema não é o card. É o sistema que ensinou o time a esperar o card.&lt;/p&gt;

&lt;p&gt;A frase que eu ainda seguro em público:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Seu trabalho não termina quando o código funciona. Termina quando o problema deixa de existir, ou quando você garante que alguém é responsável por fazê-lo deixar de existir.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  6. Onde o seu time para?
&lt;/h2&gt;

&lt;p&gt;Não preciso do seu pior incidente. Quero a postura.&lt;/p&gt;

&lt;p&gt;No último problema chato que &lt;strong&gt;todo mundo já via&lt;/strong&gt;: vocês &lt;strong&gt;ignoraram com consciência&lt;/strong&gt;, &lt;strong&gt;investigaram&lt;/strong&gt; ou &lt;strong&gt;abriram card&lt;/strong&gt;? E o que impediu a opção que vocês &lt;em&gt;não&lt;/em&gt; escolheram?&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;p&gt;O framework SEE → UNDERSTAND → EXPOSE → OWN e a distinção OWN ≠ CODE são síntese minha. Os links abaixo são o contexto público que usei pra amarrar “detectar tarde custa” e “entrega ≠ resultado”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dora.dev/research/2023/dora-report/" rel="noopener noreferrer"&gt;Accelerate State of DevOps Report 2023 (DORA)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dora.dev/" rel="noopener noreferrer"&gt;DORA: capacidades e four keys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.nist.gov/publications/economic-impacts-inadequate-infrastructure-software-testing" rel="noopener noreferrer"&gt;NIST: The Economic Impacts of Inadequate Infrastructure for Software Testing&lt;/a&gt; (2002; o “quanto mais tarde, mais caro” que o mercado cita)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stripe.com/files/reports/the-developer-coefficient.pdf" rel="noopener noreferrer"&gt;Stripe: The Developer Coefficient&lt;/a&gt; (tempo gasto em legado / débito; leitura complementar)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>career</category>
      <category>architecture</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Por que um Staff Engineer está estudando AI Security</title>
      <dc:creator>Tiago Vilas Boas (Montanha)</dc:creator>
      <pubDate>Mon, 03 Aug 2026 07:15:17 +0000</pubDate>
      <link>https://dev.to/tiagovilasboas/por-que-um-staff-engineer-esta-estudando-ai-security-37d2</link>
      <guid>https://dev.to/tiagovilasboas/por-que-um-staff-engineer-esta-estudando-ai-security-37d2</guid>
      <description>&lt;p&gt;Pessoal, a pergunta do meu trabalho mudou.&lt;/p&gt;

&lt;p&gt;Antes: &lt;em&gt;como eu faço esse agente entregar mais rápido?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Agora: &lt;em&gt;se esse agente tem ferramenta, o que ele pode quebrar, e quem assume o risco?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A trava que você cola no PR amanhã: &lt;strong&gt;sete perguntas antes de dar tool access a um agente&lt;/strong&gt;. Se alguma resposta ficar vaga, a tool não entra.&lt;/p&gt;

&lt;p&gt;Não escrevo como expert. Em agosto de 2026 comecei Defesa Cibernética na Impacta. Este post é o marco zero público.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tabela de Conteúdo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. A pergunta mudou&lt;/li&gt;
&lt;li&gt;2. Texto errado é uma coisa. Ação indevida é outra&lt;/li&gt;
&lt;li&gt;3. O caso que eu já rodei. O rm é o desenho&lt;/li&gt;
&lt;li&gt;4. Sete perguntas antes da tool entrar no agente&lt;/li&gt;
&lt;li&gt;5. Qual trava você colocaria primeiro?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. A pergunta mudou
&lt;/h2&gt;

&lt;p&gt;Eu passo o dia em cima de sistema sob pressão: confiabilidade, observabilidade, incidente, decisão que precisa continuar fazendo sentido quando eu saio da sala. Nos últimos tempos, passei a operar de verdade com LLM, agente, MCP e memória/RAG.&lt;/p&gt;

&lt;p&gt;Staff, pra mim, nunca foi aparecer em tudo. Foi construir condições para que as squads tomem boas decisões sem depender da minha memória.&lt;/p&gt;

&lt;p&gt;Isso cria um reflexo meio chato: olhar pra demo bonita e perguntar pelo &lt;strong&gt;modo de falha&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O webhook falha como?&lt;/li&gt;
&lt;li&gt;O retry duplica o quê?&lt;/li&gt;
&lt;li&gt;Essa métrica explica impacto ou só joga stacktrace na tela?&lt;/li&gt;
&lt;li&gt;Tem kill switch ou a gente só torce?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agora esse mesmo reflexo precisa olhar pra prompt, ferramenta, memória e permissão de agente. Não porque eu mudei de carreira da noite pro dia. Porque a unidade de risco mudou.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Texto errado é uma coisa. Ação indevida é outra
&lt;/h2&gt;

&lt;p&gt;Quando o modelo só gera texto, o estrago costuma ser contido: chato, caro às vezes, mas contido. Alucinação, vazamento por descuido, resposta ruim.&lt;/p&gt;

&lt;p&gt;Quando o mesmo modelo ganha ferramenta (ler arquivo, chamar API, abrir PR, mexer em dado, acionar automation), o jogo muda.&lt;/p&gt;

&lt;p&gt;Aí o risco deixa de ser só “falou besteira”. Passa a ser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fez uma ação irreversível com confiança alta demais;&lt;/li&gt;
&lt;li&gt;tinha permissão bem maior do que o trabalho pedia;&lt;/li&gt;
&lt;li&gt;puxou pro contexto uma informação que não deveria sair dali;&lt;/li&gt;
&lt;li&gt;conectou um MCP/skill poderoso sem auditoria decente.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso não é paranoia de blog. A &lt;a href="https://genai.owasp.org/llm-top-10/" rel="noopener noreferrer"&gt;OWASP&lt;/a&gt; chama o desenho de &lt;a href="https://genai.owasp.org/llm-top-10/" rel="noopener noreferrer"&gt;Excessive Agency (LLM06:2025)&lt;/a&gt;: tool demais, permissão demais, autonomia demais. O &lt;a href="https://www.nist.gov/itl/ai-risk-management-framework" rel="noopener noreferrer"&gt;NIST AI RMF&lt;/a&gt; fala a mesma coisa em linguagem de governança. Eu estudo pelos dois. O gate mais abaixo é o dia 1 no desk.&lt;/p&gt;

&lt;p&gt;A leitura de Staff que eu carrego é bem direta:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Acelerar agente sem olhar permissão é otimizar velocidade com o cinto solto.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. O caso que eu já rodei. O &lt;code&gt;rm&lt;/code&gt; é o desenho
&lt;/h2&gt;

&lt;p&gt;O caso meu no ar não é shell em prod. É texto.&lt;/p&gt;

&lt;p&gt;No &lt;a href="https://dev.to/tiagovilasboas/prompt-appsec-4-gaps-autorizacao-1k77"&gt;09&lt;/a&gt; eu quase mandei 23 “vulnerabilidades” pra maintainer. XSS fantasma, CORS, um SVG. Volume. Se eu fosse o cara do outro lado da issue, eu levaria isso a sério? Parei. O que vingou foi hardening público e silêncio quando a invariante segurou. O denominador está lá. Aqui não reconto os findings.&lt;/p&gt;

&lt;p&gt;No gate, isso é pergunta 1 e pergunta 7. A ação irreversível era abrir issue com ruído. O que impediu o dano fui eu na porta, não o modelo. HITL. Blast radius do texto.&lt;/p&gt;

&lt;p&gt;Este post é a outra ponta. Não o que o agente afirma. O que ele pode executar.&lt;/p&gt;

&lt;p&gt;Desenho do gate. Pedagógico. Não é o caso acima.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;você:     limpa os temporários da pasta de build
agente:   rm -rf ./build ../tmp
          (prestativo. path saiu da pasta. token ainda chama API)
você:     quase dá enter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Sem allowlist de path, sem dry-run, sem humano no &lt;code&gt;rm&lt;/code&gt;, o modelo não “alucinou um ensaio”. Montou o &lt;code&gt;rm&lt;/code&gt; na leitura generosa de “temporários”.&lt;/p&gt;

&lt;p&gt;O gate, preenchido neste desenho. Resposta vaga = a tool não entra.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Pergunta&lt;/th&gt;
&lt;th&gt;Neste desenho&lt;/th&gt;
&lt;th&gt;Entra?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ação irreversível mais cara&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;rm&lt;/code&gt; no tree. Token que chama API.&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Menor escopo que ainda resolve&lt;/td&gt;
&lt;td&gt;Token amplo “pra não travar”.&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Precisa escrever, ou leitura basta?&lt;/td&gt;
&lt;td&gt;Escrita porque senão não demo.&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;O que no contexto não deveria sair&lt;/td&gt;
&lt;td&gt;Nota interna misturada no ticket.&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Como detecta abuso ou erro&lt;/td&gt;
&lt;td&gt;Sem dry-run. Sem log do comando.&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Kill switch, com dono&lt;/td&gt;
&lt;td&gt;Ninguém nomeado pra revogar o token.&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Se alucinar a intenção, o que impede o dano&lt;/td&gt;
&lt;td&gt;Sem allowlist de path. Sem HITL no &lt;code&gt;rm&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sete vagos. A tool não entra. &lt;a href="https://genai.owasp.org/llm-top-10/" rel="noopener noreferrer"&gt;LLM06&lt;/a&gt; neste caso não é paper. É essa tabela. O caso meu, com issue, já está no &lt;a href="https://dev.to/tiagovilasboas/prompt-appsec-4-gaps-autorizacao-1k77"&gt;09&lt;/a&gt;: mesma disciplina, outra porta.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Sete perguntas antes da tool entrar no agente
&lt;/h2&gt;

&lt;p&gt;Copia, cola no PR da automação, e não segue em frente sem responder. Cópia solta: &lt;a href="https://gist.github.com/tiagovilasboas/31a649d043e8b1ebf3bb504f05ab94ef" rel="noopener noreferrer"&gt;gist do gate&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Tool access gate (agente)&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; Qual é a ação irreversível mais cara que essa tool permite?
   (apagar, pagar, publicar, exfiltrar, merge, deploy)
&lt;span class="p"&gt;2.&lt;/span&gt; Qual é o menor escopo de permissão que ainda resolve o trabalho?
&lt;span class="p"&gt;3.&lt;/span&gt; O agente precisa de escrita, ou leitura bastaria na maior parte do tempo?
&lt;span class="p"&gt;4.&lt;/span&gt; O que entra no contexto (RAG/memória) que não deveria sair dali?
&lt;span class="p"&gt;5.&lt;/span&gt; Como eu detecto abuso ou erro? (log, audit trail, alerta, dry-run)
&lt;span class="p"&gt;6.&lt;/span&gt; Qual é o kill switch? (revogar token, desligar tool, pausar automation)
&lt;span class="p"&gt;7.&lt;/span&gt; Se o modelo alucinar a intenção, o que ainda impede o dano?
   (allowlist, HITL, confirmação humana, sandbox)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No fundo, as sete perguntas tentam aplicar princípios antigos (menor privilégio, redução de blast radius, auditabilidade, defesa em profundidade) a uma interface nova: o agente. Capability boundaries e human-in-the-loop entram aí sem drama.&lt;/p&gt;

&lt;h3&gt;
  
  
  Se alguma resposta ficar vaga
&lt;/h3&gt;

&lt;p&gt;Regra de bolso:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Se alguma resposta ficar vaga, a tool &lt;strong&gt;não&lt;/strong&gt; entra no agente.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Leitura vs escrita é a ramificação que mais gente pula. “Precisa escrever porque senão não demo” não é menor escopo. É conveniência. Kill switch sem dono também não conta: se ninguém sabe &lt;em&gt;quem&lt;/em&gt; revoga o token, você não tem kill switch.&lt;/p&gt;

&lt;p&gt;É a trava mínima pra gente não confundir produtividade com negligência.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Qual trava você colocaria primeiro?
&lt;/h2&gt;

&lt;p&gt;Este texto para no gate. O dia 1. O caso que eu já rodei está no H3, no &lt;a href="https://dev.to/tiagovilasboas/prompt-appsec-4-gaps-autorizacao-1k77"&gt;09&lt;/a&gt;. RAG, MCP e fine-tuning como camada é o &lt;a href="https://dev.to/tiagovilasboas/rag-ou-mcp-a-pergunta-errada-cole-o-prompt-e-monte-os-dois-57kj"&gt;10&lt;/a&gt;. Sem calendário.&lt;/p&gt;

&lt;p&gt;Filtro que eu uso, pra não trocar de hobby toda semana:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Isso me deixa mais confiável com agente, ou só me distrai com logo novo?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Você já viu um agente receber mais permissão do que precisava? O que colocaria como primeira trava: &lt;strong&gt;allowlist&lt;/strong&gt;, &lt;strong&gt;sandbox&lt;/strong&gt;, &lt;strong&gt;aprovação humana&lt;/strong&gt; ou &lt;strong&gt;token restrito&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Caso concreto fica opcional. O gate preenchido no &lt;code&gt;rm&lt;/code&gt; já dá pra responder. O 09 também.&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>architecture</category>
      <category>career</category>
    </item>
  </channel>
</rss>
