<?xml version="1.0"?>
<rss version="2.0"
     xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>JBake</title>
        <link >https://cheroliv.com/</link>
        <atom:link href="https://cheroliv.com//feed.xml" rel="self" type="application/rss+xml" />
        <description>JBake Bootstrap Template</description>
        <language>en-gb</language>
        <pubDate>Fri, 25 Sep 2026 01:41:36 +0000</pubDate>
        <lastBuildDate>Fri, 25 Sep 2026 01:41:36 +0000</lastBuildDate>

        <item>
            <title>HyperFrames-Gradle: Transformar AsciiDoc em vídeo MP4</title>
            <link >https://cheroliv.com//blog/2026/0126_hyperframes_asciidoc_video_post.html</link>
            <pubDate>Sun, 31 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0126_hyperframes_asciidoc_video_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_buraco_no_pipeline_de_vídeo&amp;quot;&amp;gt;O Buraco no Pipeline de Vídeo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_hyperframes_o_motor&amp;quot;&amp;gt;HyperFrames : O Motor&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_arquitetura_do_plugin&amp;quot;&amp;gt;A Arquitetura do Plugin&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_pipeline_em_quatro_etapas&amp;quot;&amp;gt;O Pipeline em Quatro Etapas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_dsl_asciidoc&amp;quot;&amp;gt;O DSL AsciiDoc&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_bridge_node_js&amp;quot;&amp;gt;O Bridge Node.js&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_dois_plugins_de_vídeo_dois_usos&amp;quot;&amp;gt;Dois plugins de vídeo, dois usos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_os_6_epics_da_roadmap&amp;quot;&amp;gt;Os 6 EPICs da Roadmap&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_funciona&amp;quot;&amp;gt;Por que funciona&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão_o_elo_faltante&amp;quot;&amp;gt;Conclusão: O elo faltante&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referências&amp;quot;&amp;gt;Referências&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 8 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Há duas semanas, eu lancei`capsule-gradle`— um plugin que transforma um deck reveal.js em cápsula de vídeo WebM com TTS. Hoje, estou adicionando um novo plugin : &amp;lt;code&amp;gt;hyperframes-gradle&amp;lt;/code&amp;gt;. Ele faz algo que o capsule-gradle não faz : transformar o AsciiDoc &amp;lt;strong&amp;gt;diretamente&amp;lt;/strong&amp;gt; em vídeo MP4, sem passar por reveal.js.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está por que esse plugin existe, como ele se articula com o existente, e o que o motor HyperFrames da HeyGen traz para o pipeline de produção de conteúdo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_buraco_no_pipeline_de_vídeo&amp;quot;&amp;gt;O Buraco no Pipeline de Vídeo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Meu pipeline documental produz dois tipos de vídeos :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Cápsula de revisão&amp;lt;/strong&amp;gt; (capsule-gradle) — um deck reveal.js capturado em WebM com&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;narration TTS. Perfeito para rever um módulo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Slides de animação&amp;lt;/strong&amp;gt; (slider-gradle) — um deck reveal.js HTML interativo para&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;o formador presencial&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas falta um terceiro formato: o &amp;lt;strong&amp;gt;vídeo standalone&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5555%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5.5565%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Caso de uso&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;solução atual&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Falta&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Slides do formador&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;slider-gradle → reveal.js&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Cápsula de revisão&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;capsule-gradle → WebM&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;vídeo teaser&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Nenhuma&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�❌&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Demo técnica animada&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Nenhuma&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;❌&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Doc-para-vídeo&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Nenhuma&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�❌&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O primeiro caso de uso é crítico: como gerar automaticamente um vídeo de 60 segundos que apresenta um projeto ou um módulo, com animações GSAP, narration TTS, música de fundo — tudo desde o mesmo arquivo AsciiDoc os slides ?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Capsule-gradle não pode responder: ele &amp;lt;strong&amp;gt;já&amp;lt;/strong&amp;gt; precisa de um deck reveal.js. slider-gradle produz bem esse deck, mas não foi projetado para a renderização de vídeo. Precisava de um pipeline &amp;lt;strong&amp;gt;direto&amp;lt;/strong&amp;gt;: AsciiDoc → vídeo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_hyperframes_o_motor&amp;quot;&amp;gt;HyperFrames : O Motor&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Descobrihttps://github.com/heygen-com/hyperframes[HyperFrames] il y a alguns dias. É um framework open-source (Apache 2.0) criado pelo HeyGen, o unicórnio do vídeo IA. 22 700 estrelas no GitHub. O pitch se encaixa em uma frase :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;quoteblock&amp;quot;&amp;gt;
&amp;lt;blockquote&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Escreva HTML. Renderize vídeo. Construído para agentes.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/blockquote&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O princípio: você escreve HTML com`data-*`attributes, você referencia uma animação GSAP (ou CSS, Lottie, Three.js…), e o motor renderiza tudo em MP4 via Puppeteer (headless Chrome) + FFmpeg. Resultado determinístico: mesmo input, mesmas frames, mesmo vídeo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html hljs&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;div id=&amp;quot;stage&amp;quot; data-composition-id=&amp;quot;intro&amp;quot; data-start=&amp;quot;0&amp;quot; data-width=&amp;quot;1920&amp;quot; data-height=&amp;quot;1080&amp;quot;&amp;amp;gt;
  &amp;amp;lt;video class=&amp;quot;clip&amp;quot; data-start=&amp;quot;0&amp;quot; data-duration=&amp;quot;6&amp;quot; data-track-index=&amp;quot;0&amp;quot;
         src=&amp;quot;background.mp4&amp;quot; muted playsinline&amp;amp;gt;&amp;amp;lt;/video&amp;amp;gt;

  &amp;amp;lt;h1 id=&amp;quot;title&amp;quot; class=&amp;quot;clip&amp;quot; data-start=&amp;quot;1&amp;quot; data-duration=&amp;quot;4&amp;quot; data-track-index=&amp;quot;1&amp;quot;&amp;amp;gt;
    Formation Docker &amp;amp;amp; Kubernetes
  &amp;amp;lt;/h1&amp;amp;gt;

  &amp;amp;lt;audio data-start=&amp;quot;0&amp;quot; data-duration=&amp;quot;6&amp;quot; data-track-index=&amp;quot;2&amp;quot;
         data-volume=&amp;quot;0.3&amp;quot; src=&amp;quot;music.wav&amp;quot;&amp;amp;gt;&amp;amp;lt;/audio&amp;amp;gt;

  &amp;amp;lt;script src=&amp;quot;https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
  &amp;amp;lt;script&amp;amp;gt;
    const tl = gsap.timeline({ paused: true });
    tl.from(&amp;quot;#title&amp;quot;, { opacity: 0, y: 40, duration: 0.8 }, 1);
    window.__timelines = window.__timelines || {};
    window.__timelines.intro = tl;
  &amp;amp;lt;/script&amp;amp;gt;
&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O que torna o HyperFrames diferente do Remotion? Sem React, sem bundler, sem JSX. HTML simples que os agentes de IA sabem escrever nativamente. É exatamente ajustado para o meu pipeline: planner-gradle com deepseek pode gerar esse HTML a partir de um plano SPG/SPD, e um plugin Gradle pode convertê-lo para MP4.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_arquitetura_do_plugin&amp;quot;&amp;gt;A Arquitetura do Plugin&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;hyperframes-gradle segue o mesmo padrão que todos os meus plugins: o &amp;lt;a href=&amp;quot;0124_architecture_plugin_gradle_racine_consommateur_independant_post.html&amp;quot;&amp;gt;padrão « Plugin Indépendant + Racine Consommateur »&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;hyperframes-gradle/
├── settings.gradle.kts          ← racine consommateur (dogfood)
├── build.gradle.kts              ← 3 lignes : apply plugin hyperframes
├── hyperframes-plugin/           ← BUILD INDÉPENDANT
│   ├── gradlew                    ← son propre wrapper
│   ├── build.gradle.kts           ← java-gradle-plugin
│   ├── src/main/kotlin/education/cccp/hyperframes/
│   │   ├── HyperframesPlugin.kt
│   │   ├── GenerateHyperframesHtmlTask.kt
│   │   ├── RenderHyperframesTask.kt
│   │   └── HyperframesExtension.kt
│   ├── .agents/                   ← gouvernance agent
│   └── doc/
│       └── HYPERFRAMES_ARCHITECTURE.adoc
└── video.yml                      ← configuration dogfood&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_pipeline_em_quatro_etapas&amp;quot;&amp;gt;O Pipeline em Quatro Etapas&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 8) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 11

title Pipeline hyperframes-gradle

rectangle &amp;quot;AsciiDoc
fonte&amp;quot; as adoc #LightYellow
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 11

title Pipeline hyperframes-gradle

rectangle &amp;quot;AsciiDoc
fonte&amp;quot; as adoc #LightYellow
rectangle &amp;quot;AsciidoctorJ&amp;quot; as asciidoctorj #LightBlue
rectangle &amp;quot;HTML HyperFrames
(data-* attrs)&amp;quot; as html #LightGreen
rectangle &amp;quot;HyperFrames CLI\n(Node.js 22+)&amp;quot; as cli #Lavender
rectangle &amp;quot;video.mp4&amp;quot; as mp4 #Gold

adoc -right-&amp;amp;gt; asciidoctorj : parse blocs customs
asciidoctorj -right-&amp;amp;gt; html : génère HTML
html -right-&amp;amp;gt; cli : ProcessBuilder\nnpx hyperframes render
cli -right-&amp;amp;gt; mp4 : Puppeteer + FFmpeg
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;AsciidoctorJ&amp;lt;/strong&amp;gt; analisa o fonte AsciiDoc e extrai os blocos personalizados&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;[hyperframes-composition]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[hyperframes-track]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[hyperframes-animation]&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;GenerateHyperframesHtmlTask&amp;lt;/strong&amp;gt; gera o HTML HyperFrames com as&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`data-*`atributos correspondentes.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;RenderHyperframesTask&amp;lt;/strong&amp;gt; chama a CLI HyperFrames via`ProcessBuilder`:&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;npx hyperframes render --input index.html --output video.mp4&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O MP4 é implantado em`output/`com um`metadata.json`para a orquestração.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_dsl_asciidoc&amp;quot;&amp;gt;O DSL AsciiDoc&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O verdadeiro valor agregado do plugin é o DSL AsciiDoc. O usuário nunca escreve HTML. Ele anota seu documento AsciiDoc existente :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;= Formation Docker &amp;amp;amp; Kubernetes
:hyperframes-width: 1920
:hyperframes-height: 1080
:hyperframes-fps: 30

[hyperframes-composition, id=&amp;quot;intro&amp;quot;]
== Introduction

Le titre apparaît avec un fondu GSAP sur fond vidéo.

[hyperframes-track, index=0, start=0, duration=6]
video::assets/background.mp4[muted, playsinline]

[hyperframes-track, index=1, start=1, duration=4]
Formation Docker &amp;amp;amp; Kubernetes

[hyperframes-animation, type=gsap]&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;const tl = gsap.timeline({ paused: true }); tl.from(&amp;quot;#title&amp;quot;, { opacity: 0, y: 40, duration: 0.8 }, 1); window.&amp;lt;em&amp;gt;timelines = window.&amp;lt;/em&amp;gt;timelines || {}; window.__timelines.intro = tl;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os atributos docinfo (&amp;lt;code&amp;gt;:hyperframes-width:&amp;lt;/code&amp;gt;) configuram as dimensões. Os blocos personalizados definem as composições e as faixas. O bloco `[hyperframes-animation]`contém o código GSAP bruto — que o agente IA pode gerar a partir de uma descrição em linguagem natural&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_bridge_node_js&amp;quot;&amp;gt;O Bridge Node.js&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O único ponto de atrito técnico: HyperFrames está em Node.js/TypeScript, e meus plugins são em Kotlin/JVM. A solução é um`ProcessBuilder`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;class RenderHyperframesTask : DefaultTask() {

    @TaskAction
    fun render() {
        val process = ProcessBuilder(
            &amp;quot;npx&amp;quot;, &amp;quot;hyperframes&amp;quot;, &amp;quot;render&amp;quot;,
            &amp;quot;--input&amp;quot;, inputHtml.absolutePath,
            &amp;quot;--output&amp;quot;, outputMp4.absolutePath
        )
            .inheritIO()
            .start()

        val exitCode = process.waitFor()
        require(exitCode == 0) {
            &amp;quot;HyperFrames render failed with exit code $exitCode&amp;quot;
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Zero acoplamento código JVM ↔ Node.js. O contrato é por linha de comando e arquivos no disco. É o mesmo padrão que`plantuml-gradle` (que chama a CLI PlantUML) ou`slider-gradle`(que chama AsciidoctorJ). Nada de novo sob o sol — apenas aplicado a uma ferramenta mais recente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_dois_plugins_de_vídeo_dois_usos&amp;quot;&amp;gt;Dois plugins de vídeo, dois usos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.7619%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.762%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;cápsula-gradle&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;hyperframes-gradle&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Source&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Deck reveal.js (HTML existente)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;AsciiDoc → HTML HyperFrames&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;caso de uso&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Cápsula de revisão&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;vídeo explicativo standalone&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;natureza&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;slides → vídeo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Documento → vídeo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;entregue&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Playwright Java → WebM&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;HyperFrames CLI → MP4&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Animação&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Transições reveal.js nativas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;atributos de dados GSAP/CSS&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Pilha&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;100% JVM (Kotlin)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;JVM + Node.js CLI externo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eles não se substituem. Eles se complementam. A cadeia de valor completa:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;AsciiDoc ──→ slider-gradle ──→ deck reveal.js ──→ capsule-gradle ──→ WebM (cápsula de revisão)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;AsciiDoc ──→ hyperframes-gradle ──→ MP4 (vídeo autônomo)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O utilizador escreve um único ficheiro AsciiDoc. slider-gradle produz os slides. capsule-gradle produz a cápsula de revisão. hyperframes-gradle produz o vídeo teaser e as demonstrações técnicas&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_os_6_epics_da_roadmap&amp;quot;&amp;gt;Os 6 EPICs da Roadmap&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plugin é estruturado em 6 EPICs :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1682%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;EPIC&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Descrição&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Prioridade&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;HF-0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Bootstrap governança + enquadramento da arquitetura&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅ CONCLUÍDO&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;HF-1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Plugin Gradle stub + Integração AsciidoctorJ&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;P0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;HF-2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Bridge CLI HyperFrames (ProcessBuilder → npx → MP4)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;P1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;HF-3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Costumes do DSL AsciiDoc (blocos, docinfo, modelos)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;P1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;HF-4&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Integração runner-gradle (metadata.json)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;P2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;HF-5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Modelos prontos para uso (title-card, code-diff, captions)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;P3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;HF-6&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;CI + publicação Maven Central / Gradle Portal&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;P3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A sessão 000 (bootstrap) já terminou. A sessão 001 atacará HF-1 : o stub do plugin Gradle e a integração AsciidoctorJ.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_funciona&amp;quot;&amp;gt;Por que funciona&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Três razões :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;HyperFrames é &amp;quot;built for agents&amp;lt;/strong&amp;gt;&amp;quot;. Meu pipeline depende de agentes de IA&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;(planner-gradle + deepseek) que geram conteúdo. HyperFrames aceita de HTML bruto — o formato que os LLMs dominam melhor. Sem React, não de JSX, sem curva de aprendizagem para o agente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O DSL AsciiDoc é natural&amp;lt;/strong&amp;gt;. Todo o meu ecossistema fala AsciiDoc.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;slider-gradle, codex-gradle, training-gradle — todos consomem do`.adoc`. Adicionar blocos personalizados`[hyperframes-composition]`é uma extensão lógico, não uma ruptura.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O bridge Node.js é um padrão comprovado&amp;lt;/strong&amp;gt;. ProcessBuilder fora da JVM,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;isso é o que eu já faço para PlantUML, Graphviz, Piper. Adicionar HyperFrames não muda nada na arquitetura — é mais uma ferramenta externa, pilotado a partir do Gradle.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;ℹ️&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A verdadeira força do padrão é que não há &amp;lt;strong&amp;gt;nenhuma&amp;lt;/strong&amp;gt; dependência npm em o build Gradle. O plugin não importa código Node.js. Ele executa uma comando shell. Se HyperFrames evolui ou quebra, o plugin não está acoplado — basta atualizar o comando.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_o_elo_faltante&amp;quot;&amp;gt;Conclusão: O elo faltante&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com hyperframes-gradle, meu pipeline de vídeo está completo. Eu tenho :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;As &amp;lt;strong&amp;gt;slides interativas&amp;lt;/strong&amp;gt; (slider-gradle)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;As &amp;lt;strong&amp;gt;capsules de revisão&amp;lt;/strong&amp;gt; (capsule-gradle)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;vídeos standalone&amp;lt;/strong&amp;gt; (hyperframes-gradle)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Três formatos. Um único arquivo fonte: o AsciiDoc.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O próximo objetivo é HF-1 : compilar o plugin e gerar um primeiro HTML HyperFrames a partir de um documento AsciiDoc anotado. A sessão 001 já está enquadrada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;hyperframes-gradle existe. A governança está instalada. O backlog est escrito. A arquitetura está documentada. O plugin nasceu.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_referências&amp;quot;&amp;gt;Referências&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0124_architecture_plugin_gradle_racine_consommateur_independant_post.html&amp;quot;&amp;gt;Artigo 0124 — Arquitetura do Plugin Independente + Raiz do Consumidor&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/heygen-com/hyperframes&amp;quot;&amp;gt;HyperFrames — GitHub&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://hyperframes.heygen.com/introduction&amp;quot;&amp;gt;HyperFrames — Documentação&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;foundry/public/hyperframes-gradle/hyperframes-plugin/doc/HYPERFRAMES_ARCHITECTURE.adoc&amp;lt;/code&amp;gt;— Arquitetura detalhada&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Auditoria do meu Consumo LLM : SQLite, OpenCode, Ollama Cloud, e Por Que Minha Assinatura Pro Estoura o Mercado Mondial</title>
            <link >https://cheroliv.com//blog/2026/0125_audit_conso_opencode_methodologie_comparaison_prix_llm_post.html</link>
            <pubDate>Mon, 18 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0125_audit_conso_opencode_methodologie_comparaison_prix_llm_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_compreender_a_stack_quem_faz_o_que&amp;quot;&amp;gt;Compreender a Stack: Quem Faz o Que?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução_por_que_realizar_uma_auditoria&amp;quot;&amp;gt;Introdução : Por que realizar uma auditoria ?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_base_de_dados_opencode_uma_mina_de_ouro&amp;quot;&amp;gt;A Base de Dados OpenCode : Uma Mina de Ouro&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_esquema&amp;quot;&amp;gt;O esquema&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_código_de_extração&amp;quot;&amp;gt;O Código de Extração&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_os_resultados_73_dias_de_consumo&amp;quot;&amp;gt;Os Resultados: 73 Dias de Consumo&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_visão_global&amp;quot;&amp;gt;Visão Global&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_ventilação_por_modelo_e_fornecedor&amp;quot;&amp;gt;Ventilação por Modelo e Fornecedor&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_os_modelos_intra_sessão_o_que_a_tabela_de_sessão_não_vê&amp;quot;&amp;gt;Os Modelos Intra-Sessão: O Que a Tabela de Sessão Não Vê&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_comparativo_tarifário_fornecedores_ocidentais&amp;quot;&amp;gt;Comparativo Tarifário — Fornecedores Ocidentais&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_anthropic_claude&amp;quot;&amp;gt;Anthropic — Claude&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_openai_gpt&amp;quot;&amp;gt;OpenAI — GPT&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_google_gemini&amp;quot;&amp;gt;Google — Gemini&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_provedores_de_inferência_huggingface&amp;quot;&amp;gt;Provedores de Inferência HuggingFace&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_groq&amp;quot;&amp;gt;Groq&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_comparativo_tarifário_fornecedores_chineses&amp;quot;&amp;gt;Comparativo Tarifário — Fornecedores Chineses&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_deepseek_api_direct_platform_deepseek_com&amp;quot;&amp;gt;DeepSeek API Direct (platform.deepseek.com)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_kimi_moonshot_platform_kimi_com&amp;quot;&amp;gt;Kimi Moonshot (platform.kimi.com)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_alibaba_bailian_bailian_aliyun_com&amp;quot;&amp;gt;Alibaba Bailian (bailian.aliyun.com)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_tabela_resumo_todos_providers&amp;quot;&amp;gt;Tabela Resumo — Todos Providers&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_custo_oculto_engenharia_de_prompts_e_iterações&amp;quot;&amp;gt;O Custo Oculto : Engenharia de Prompts e Iterações&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_acumular_as_assinaturas_ollama_cloud_pro_o_método_docker&amp;quot;&amp;gt;Acumular as Assinaturas Ollama Cloud Pro: O Método Docker&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_a_auditoria_não_mede&amp;quot;&amp;gt;O Que a Auditoria Não Mede&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão_o_veredito_dos_números&amp;quot;&amp;gt;Conclusão: O Veredito dos Números&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referências&amp;quot;&amp;gt;Referências&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fontes_das_tarifas&amp;quot;&amp;gt;Fontes das tarifas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 22 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Muitas vezes me perguntam quanto custa meu consumo de IA. Eu respondo « 20 euros por mês » e eles me olham com os olhos arregalados. Então hoje, eu abro a caixa preta para vocês. Este artigo é a auditoria completa dos meus 73 dias de vibe coding : a metodologia para extrair os dados da base SQLite d&amp;amp;#8217;OpenCode, o código SQL para consultar as tabelas, e a comparação exaustiva com &amp;lt;strong&amp;gt;todos&amp;lt;/strong&amp;gt; os fornecedores do mercado — ocidentais como chineses. O resultado é tão assimétrico que tive que verificar meus cálculos três vezes.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;&amp;amp;lt;/think&amp;amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_compreender_a_stack_quem_faz_o_que&amp;quot;&amp;gt;Compreender a Stack: Quem Faz o Que?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes de mergulhar nos números, vamos esclarecer a arquitetura. Há três atores distintos no meu setup, e é importante não confundir os três :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 11) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 12

title Os Três Atores da Minha Stack LLM
left to right direction

actor &amp;quot;Eu (Desenvolvedor)&amp;quot; as Dev

package &amp;quot;OpenCode\n(github.com/anomalyco/opencode)&amp;quot; as OC {
  rectangle &amp;quot;CLI de vibe coding
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 12

title Os Três Atores da Minha Stack LLM
left to right direction

actor &amp;quot;Eu (Desenvolvedor)&amp;quot; as Dev

package &amp;quot;OpenCode\n(github.com/anomalyco/opencode)&amp;quot; as OC {
  rectangle &amp;quot;CLI de vibe coding
Código aberto, gratuito&amp;quot; as CLI
  note right of CLI
    C&amp;#39;est l&amp;#39;outil que j&amp;#39;utilise
    pour coder avec des agents IA.
    Il ne fournit AUCUN modèle.
    C&amp;#39;est un terminal + un orchestrateur.
  end note
}

package &amp;quot;Ollama (Instalação Local)&amp;quot; as OL {
  rectangle &amp;quot;Cliente de inferência\nInstalado na minha máquina&amp;quot; as Client
  note right of Client
    C&amp;#39;est le logiciel qui fait tourner
    les modèles. Il peut les exécuter
    en local OU les déléguer au cloud
    selon le tag (:cloud).
    Il s&amp;#39;authentifie via clé SSH.
  end note
}

package &amp;quot;Ollama Cloud
(ollama.com)&amp;quot; as OCloud {
  rectangle &amp;quot;Serviço de inferência em nuvem
Assinatura Pro : 20€/mês&amp;quot; as Cloud
  note right of Cloud
    C&amp;#39;est le fournisseur d&amp;#39;inférence.
    Mes modèles avec le tag :cloud
    sont exécutés sur LEURS serveurs.
    L&amp;#39;abonnement Pro donne accès
    à des quotas supérieurs.
  end note
}

Dev --&amp;amp;gt; CLI : &amp;quot;Eu digito prompts&amp;quot;
CLI --&amp;amp;gt; Client : &amp;quot;Chame este modelo&amp;quot;
Client --&amp;amp;gt; Cloud : &amp;quot;Chave SSH + requisição\n(modelos :cloud)&amp;quot;
Client --&amp;amp;gt; Client : &amp;quot;Execução local
(modelos sem :cloud)&amp;quot;

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;❗&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;OpenCode NÃO é um fornecedor de modelos.&amp;lt;/strong&amp;gt;É um CLI de código aberto (&amp;lt;a href=&amp;quot;https://github.com/anomalyco/opencode&amp;quot;&amp;gt;github.com/anomalyco/opencode&amp;lt;/a&amp;gt;) que orquestra agentes de IA. Ele não vende assinatura, ele não fornece inferência. É o equivalente de`git`para versionamento: uma ferramenta, não um serviço. &amp;lt;strong&amp;gt;Ollama Cloud é o fornecedor da inferência.&amp;lt;/strong&amp;gt;Minha instalação local do Ollama atua como &amp;lt;strong&amp;gt;client&amp;lt;/strong&amp;gt; — ela se autentica nos servidores do Ollama por meio de uma chave SSH única (Device Key) e delega a execução dos modelos marcados a eles`:cloud`. A assinatura Pro (20€/mês) desbloqueia quotas superiores e permite acumular várias contas na mesma máquina via Docker.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução_por_que_realizar_uma_auditoria&amp;quot;&amp;gt;Introdução : Por que realizar uma auditoria ?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Há dois meses, estava usando modelos gratuitamente na minha instalação local do Ollama. Então assinei um primeiro plano Ollama Cloud Pro de 20€/mês, que me permite usar os modelos em nuvem (aqueles com a tag`:cloud`) via o provider`ollama-cloud`. Uma segunda assinatura ocorreu um mês depois, em uma instância Docker separada com sua própria conta e chave SSH — o método de acumulação descrito em &amp;lt;a href=&amp;quot;0120_cumuler_deux_abonnements_ollama_pro_docker_post.html&amp;quot;&amp;gt;meu artigo sobre a acumulação de assinaturas&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A pergunta que me atormentava : &amp;lt;strong&amp;gt;será que esses 20€ por mês são justificados ?&amp;lt;/strong&amp;gt; Não de bate-pronto. Com números.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para responder, eu precisava de dados precisos. Não eram estimativas. Não eram médias. Meu consumo &amp;lt;strong&amp;gt;real&amp;lt;/strong&amp;gt;, token por token, modelo por modelo, sessão por sessão.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E OpenCode — a CLI — armazena tudo isso em um banco de dados SQLite.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_base_de_dados_opencode_uma_mina_de_ouro&amp;quot;&amp;gt;A Base de Dados OpenCode : Uma Mina de Ouro&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;OpenCode persiste a integralidade do histórico de suas sessões em um arquivo SQLite localizado em`~/.local/share/opencode/opencode.db`. La mienne pèse 660 Mo pour 73 jours d&amp;amp;#8217;utilisation — une base de données de production tout à fait respectable.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ ls -lh ~/.local/share/opencode/opencode.db
-rw-r--r-- 1 cheroliv cheroliv 660M mai 18 12:25 opencode.db&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_esquema&amp;quot;&amp;gt;O esquema&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A base contém cerca de vinte tabelas, mas três delas são cruciais para nossa auditoria:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/schema-principal.svg&amp;quot; alt=&amp;quot;schema principal&amp;quot; width=&amp;quot;684&amp;quot; height=&amp;quot;397&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A mesa`session`é perfeita para uma auditoria global: cada sessão tem sua contagem agregada de tokens, o modelo utilizado e seu custo. O campo`model`é JSON bruto:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-json hljs&amp;quot; data-lang=&amp;quot;json&amp;quot;&amp;gt;{
  &amp;quot;id&amp;quot;: &amp;quot;deepseek-v4-pro:cloud&amp;quot;,
  &amp;quot;providerID&amp;quot;: &amp;quot;ollama-cloud&amp;quot;,
  &amp;quot;variant&amp;quot;: &amp;quot;default&amp;quot;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;❗&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;o campo`cost`na base OpenCode reflete apenas as &amp;lt;strong&amp;gt;taxas de API externas&amp;lt;/strong&amp;gt; pagas por consumo via chave de API (Google Gemini, OpenAI, etc.). As chamadas via Ollama — seja localmente ou via Ollama Cloud Pro — são marcadas`cost=0`Porque o custo é fixo (assinatura), não por token. O verdadeiro custo dessas chamadas é a assinatura mensal.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_código_de_extração&amp;quot;&amp;gt;O Código de Extração&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não é preciso instalar nada. Node.js 22+ inclui um módulo SQLite experimental (&amp;lt;code&amp;gt;node:sqlite&amp;lt;/code&amp;gt;) que funciona perfeitamente. Aqui está o script que me deu tudo:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript hljs&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;const { DatabaseSync } = require(&amp;#39;node:sqlite&amp;#39;);
const db = new DatabaseSync(
  `${process.env.HOME}/.local/share/opencode/opencode.db`,
  { readonly: true }
);

// 1. Statistiques globales
const global = db.prepare(`
  SELECT
    COUNT(*) as sessions,
    SUM(tokens_input) as total_input,
    SUM(tokens_output) as total_output,
    SUM(tokens_reasoning) as total_reasoning,
    SUM(tokens_cache_read) as total_cache_read,
    SUM(cost) as total_cost
  FROM session
`).get();

// 2. Ventilation par modèle et fournisseur
const byModel = db.prepare(`
  SELECT
    json_extract(model, &amp;#39;$.id&amp;#39;) as model_id,
    json_extract(model, &amp;#39;$.providerID&amp;#39;) as provider,
    COUNT(*) as sessions,
    SUM(tokens_input) as total_input,
    SUM(tokens_output) as total_output,
    SUM(cost) as total_cost
  FROM session
  WHERE model IS NOT NULL
  GROUP BY model_id, provider
  ORDER BY SUM(tokens_input) DESC
`).all();

// 3. Par mois
const monthly = db.prepare(`
  SELECT
    strftime(&amp;#39;%Y-%m&amp;#39;, datetime(time_created/1000, &amp;#39;unixepoch&amp;#39;)) as month,
    COUNT(*) as sessions,
    SUM(tokens_input) as total_input,
    SUM(tokens_output) as total_output,
    SUM(cost) as total_cost
  FROM session
  WHERE time_created IS NOT NULL
  GROUP BY month
  ORDER BY month
`).all();

// 4. Messages par modèle (détection des changements intra-session)
const msgByModel = db.prepare(`
  SELECT
    json_extract(m.data, &amp;#39;$.model.providerID&amp;#39;) as provider,
    json_extract(m.data, &amp;#39;$.model.modelID&amp;#39;) as model_id,
    COUNT(*) as messages
  FROM message m
  WHERE json_extract(m.data, &amp;#39;$.model&amp;#39;) IS NOT NULL
  GROUP BY provider, model_id
  ORDER BY COUNT(*) DESC
`).all();

console.log({ global, byModel, monthly, msgByModel });
db.close();&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;ℹ️&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O flag`--experimental-sqlite`É necessário no Node.js 22.x. No Node.js 24+, o módulo é estável e não requer mais nenhum sinalizador.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_os_resultados_73_dias_de_consumo&amp;quot;&amp;gt;Os Resultados: 73 Dias de Consumo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_visão_global&amp;quot;&amp;gt;Visão Global&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3334%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Métrica&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Valor&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Contexto&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sessões OpenCode&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1 022&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;~14 sessões por dia em média&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;tokens de entrada&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2,62 bilhões&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Dont 94,5M em acertos de cache&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Tokens Output&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;21 milhões&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Ratio input/output ~125:1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Tokens Reasoning&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;141 000&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Negligível — quase nenhum pensamento&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Custo da API externa&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;\$0,22&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Custos fora da assinatura (Gemini + free tiers)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este número de $0,22 é crucial: representa tudo o que eu paguei &amp;lt;strong&amp;gt;além&amp;lt;/strong&amp;gt; da minha assinatura Ollama Cloud Pro. Ou seja, praticamente nada. A assinatura cobre o essencial.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 7) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 12

title Evolução Mensal — Tokens de Entrada
rectangle &amp;quot;Março 2026
9 sessões
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 12

title Evolução Mensal — Tokens de Entrada
rectangle &amp;quot;Março 2026
9 sessões
37,7M tokens&amp;quot; as MARS #FFE082
rectangle &amp;quot;Abril 2026
486 sessões
1,33 Md tokens&amp;quot; as AVR #81C784
rectangle &amp;quot;Maio 2026
527 sessões
1,25 bi tokens&amp;quot; as MAI #64B5F6

MARS -right-&amp;amp;gt; AVR : ×35
AVR -right-&amp;amp;gt; MAI : stable (période en cours)

note bottom of MAI
  Mai comptabilisé jusqu&amp;#39;au 18.
  Projection mois complet : ~2,1 Md tokens
  si la tendance se maintient.
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O decolagem entre março e abril é espetacular: ×35 em um mês. É a transição do uso « teste » para o uso « produção » — quando comecei a fazer rodar agentes OpenCode continuamente para meus plugins Gradle.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_ventilação_por_modelo_e_fornecedor&amp;quot;&amp;gt;Ventilação por Modelo e Fornecedor&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O campo`model`da mesa`session`sendo JSON,`json_extract()`permite ventilar adequadamente :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 27.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1819%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Modelo × Provider&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Sessões&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;[No output, as no French text was provided for translation]&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Output&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Custo&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;deepseek-v4-pro @ ollama-cloud&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;63&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;220 M&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1,94 M&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;deepseek-v4-pro @ ollama-b&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;124&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;463 M&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;4,15 M&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;deepseek-v4-pro @ ollama&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;100&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;252 M&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2,25 M&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;deepseek-v4-flash-free @ opencode&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;16&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1,3 M&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;279 K&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;qwen3.6-plus-free @ opencode&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;369 K&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;25 K&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;gemini-2.5-flash @ google&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;211 K&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;19 K&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0,04&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;❗&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Três provedores diferentes para exatamente o mesmo modelo DeepSeek V4 Pro :
* &amp;lt;code&amp;gt;ollama-cloud&amp;lt;/code&amp;gt;→ via &amp;lt;strong&amp;gt;assinatura Pro paga&amp;lt;/strong&amp;gt; (cloud gerenciado por Ollama)
* &amp;lt;code&amp;gt;ollama-b&amp;lt;/code&amp;gt;→ &amp;lt;strong&amp;gt;segunda assinatura Pro&amp;lt;/strong&amp;gt;, isolado em um contêiner Docker separado
* &amp;lt;code&amp;gt;ollama&amp;lt;/code&amp;gt;→ instance native locale, &amp;lt;strong&amp;gt;gratuita&amp;lt;/strong&amp;gt; (executa diretamente na minha máquina)
O somatório das três dá 936M de entrada, 8,4M de saída — é o meu consumo total DeepSeek V4 Pro, todos os provedores combinados. Só`ollama-cloud`(220M input) custa uma assinatura. Os 716M restantes são gratuitos (local ou acúmulo de assinaturas).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_os_modelos_intra_sessão_o_que_a_tabela_de_sessão_não_vê&amp;quot;&amp;gt;Os Modelos Intra-Sessão: O Que a Tabela de Sessão Não Vê&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A mesa`session`Captura apenas o modelo &amp;lt;strong&amp;gt;principal&amp;lt;/strong&amp;gt; da sessão. Mas durante a mesma sessão, um agente OpenCode pode alternar entre vários modelos. A tabela`message`o revela :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;Modèles utilisés dans les messages (ollama-cloud provider) :
├─ deepseek-v4-pro      1 441 msgs  (32%)
├─ qwen3.5:397b         1 217 msgs  (27%)
├─ glm-5.1                712 msgs  (16%)
├─ kimi-k2.6:cloud        634 msgs  (14%)
├─ qwen3-coder:480b       414 msgs   (9%)
├─ gpt-oss:20b             34 msgs  (0,8%)
├─ qwen3-coder-next        27 msgs  (0,6%)
└─ gemma4:31b               7 msgs  (0,2%)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock caution&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-caution&amp;quot; title=&amp;quot;🔒&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não uso mais Qwen, GLM, Kimi nem os outros modelos. Eles aparecem no histórico porque testei muitas coisas em abril. Desde meados de maio, estou bloqueado no DeepSeek V4 Pro — é o único que sustenta a carga em contextos longos com governança de agente (cf. &amp;lt;a href=&amp;quot;0112_comparaison_kimi_glm_deepseek_long_contexte_plugin_gradle_opencode_post.html&amp;quot;&amp;gt;minha comparação Kimi/GLM/DeepSeek&amp;lt;/a&amp;gt;). Os 32% de mensagens deepseek no histórico são enganosos: hoje é 100%.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_comparativo_tarifário_fornecedores_ocidentais&amp;quot;&amp;gt;Comparativo Tarifário — Fornecedores Ocidentais&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A questão que fere : quanto custaria esse mesmo consumo junto aos fornecedores tradicionais?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Minha base de cálculo para o comparativo :&amp;lt;strong&amp;gt;220M tokens de entrada + 1,9M tokens de saída&amp;lt;/strong&amp;gt;— &amp;lt;strong&amp;gt;apenas&amp;lt;/strong&amp;gt; o volume passado por`ollama-cloud`(o único que corresponde ao meu plano pago). Os 716M restantes são gratuitos (Ollama local e instância Docker B).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 23.0769%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 15.3846%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 15.3846%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 15.3846%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 15.3846%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 15.3847%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;fornecedor&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Modelo&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;\$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Output \$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Custo/mês&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;× minha assinatura&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Minha configuração&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;DeepSeek V4 Pro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;\~20€&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;1×&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_anthropic_claude&amp;quot;&amp;gt;Anthropic — Claude&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 27.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1819%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Modelo&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Saída \$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Custo/mês&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;× minha assinatura&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Opus 4.7&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$25&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$1 148&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;57×&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Soneto 4.6&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$15&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$689&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;34×&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Haiku 4.5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$230&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;11×&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mesmo o Haiku 4.5, o modelo mais « econômico » da Claude, custa 11× a minha assinatura. E ele não sabe nada do meu código base, não gera nada no meu estilo e alucina nas APIs Gradle Kotlin DSL.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_openai_gpt&amp;quot;&amp;gt;OpenAI — GPT&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 27.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1819%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Modelo&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Sortie \$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Custo/mês&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;× minha assinatura&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;GPT 5.5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$30&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$1 157&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;58×&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;GPT 5.4&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$2,50&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$15&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$579&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;29×&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;GPT 5.4 Mini&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0,75&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$4,50&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$174&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;9×&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_google_gemini&amp;quot;&amp;gt;Google — Gemini&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 27.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1819%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Modelo&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Saída \$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Custo/mês&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;minha assinatura&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Gemini 3.1 Pro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$2,00&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$10&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$459&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;23×&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Gemini 3 Flash&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0,30&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$1,50&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$69&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;3,5×&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Gemini 3 Flash a 69€ é o único concorrente « próximo » em preço puro — mas é um modelo denso leve, não um MoE 1.6T como o DeepSeek V4 Pro. A qualidade do código gerado não tem nada a ver.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_provedores_de_inferência_huggingface&amp;quot;&amp;gt;Provedores de Inferência HuggingFace&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;HuggingFace não realiza inferência por conta própria — ele agrega &amp;lt;strong&amp;gt;fornecedores&amp;lt;/strong&amp;gt; terceiros (Novita, Together, DeepInfra, Cerebras, SambaNova, Fireworks, Groq…) via uma API unificada. Escolhe-se o fornecedor mais barato ou o mais rápido. Os tarifos coletados em maio de 2026 para DeepSeek V4 Pro são os seguintes:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 27.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1819%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Provedor HF&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Preservar TODO os spans de código entre crases (&amp;lt;code&amp;gt;&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/code&amp;gt;) exatamente como estão — nunca modifique o conteúdo, o espaçamento ou a posição das crases. Este texto pode ser um fragmento de uma frase maior — traduza o fragmento sem solicitar mais contexto. Apresente apenas o texto traduzido — sem explicação, sem comentário, sem introdução, sem alternativas, sem opções. Saída \$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Custo/mês&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;× minha assinatura&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Novita (o mais barato)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$1,67&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$3,38&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;$374&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;19×&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;DeepInfra&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$1,74&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$3,48&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;$389&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;19×&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;junto&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$2,10&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$4,40&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;$470&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;23×&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Fogos de artifício&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;tarifa não pública&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;—&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;(Empty)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;—&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mesmo o provedor mais barato na HuggingFace (Novita) cobra 19× o preço da minha assinatura. E, ao contrário do Ollama Cloud, a HuggingFace também cobra &amp;lt;strong&amp;gt;em mais&amp;lt;/strong&amp;gt; as tarifas de plataforma se você não tiver uma assinatura PRO HF ($9/mês).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_groq&amp;quot;&amp;gt;Groq&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Groq não tem &amp;lt;strong&amp;gt;nenhum&amp;lt;/strong&amp;gt; dos modelos que eu uso — o catálogo deles se limita a GPT-OSS, Llama e Qwen 32B. Impossível de comparar.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_comparativo_tarifário_fornecedores_chineses&amp;quot;&amp;gt;Comparativo Tarifário — Fornecedores Chineses&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O verdadeiro teste é o mercado chinês. Se o DeepSeek é um modelo chinês, a sua API nativa deveria ser mais barata, não é?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_deepseek_api_direct_platform_deepseek_com&amp;quot;&amp;gt;DeepSeek API Direct (platform.deepseek.com)&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 22.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 22.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 22.2223%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Período&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Saída \$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Custo/mês&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Promo -75% (fim 31 de maio)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0,435&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0,87&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;$98&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tarifa cheia (a partir de junho)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$1,74&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$3,48&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;\$390&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mesmo com a promo agressiva da DeepSeek (-75% prolongada), a API direta custa&amp;lt;strong&amp;gt;5 vezes&amp;lt;/strong&amp;gt;mais caro. À tarifa normal,&amp;lt;strong&amp;gt;20×&amp;lt;/strong&amp;gt;. E isso é &amp;lt;strong&amp;gt;apenas&amp;lt;/strong&amp;gt; DeepSeek — sem acesso a outros modelos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_kimi_moonshot_platform_kimi_com&amp;quot;&amp;gt;Kimi Moonshot (platform.kimi.com)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Kimi apenas oferece seus próprios modelos. Sem DeepSeek.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_alibaba_bailian_bailian_aliyun_com&amp;quot;&amp;gt;Alibaba Bailian (bailian.aliyun.com)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Somente fornecedor que agrega todos os modelos chineses — mas por meio de revenda com margem. Os preços são carregados dinamicamente em JavaScript, tornando o scraping impossível.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_tabela_resumo_todos_providers&amp;quot;&amp;gt;Tabela Resumo — Todos Providers&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 23.0769%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 15.3846%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 15.3846%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 15.3846%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 15.3846%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 15.3847%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Fornecedor&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Modelo&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Saída \$/M&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Custo/mês&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;× minha assinatura&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Ollama Nuvem Pro&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;DeepSeek V4 Pro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;~20€&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;1×&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Gemini 3 Flash&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Gemini 3 Flash&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0,30&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$1,50&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$69&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;3,5×&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;API DeepSeek (promo)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;DeepSeek V4 Pro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0,44&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0,87&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$98&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;5×&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;GPT 5.4 Mini&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;GPT 5.4 Mini&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$0,75&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$4,50&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$174&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;9×&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Claude Haiku 4.5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Claude Haiku 4.5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$230&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;11×&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;HuggingFace/Novita&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;DeepSeek V4 Pro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$1,67&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$3,38&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$374&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;19×&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;DeepSeek API (normal)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;DeepSeek V4 Pro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$1,74&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$3,48&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$390&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;20×&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Together AI&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;DeepSeek V4 Pro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$2,10&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$4,40&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$470&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;23×&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;GPT 5.4&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;GPT 5.4&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$2,50&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$15&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$579&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;29×&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Claude Sonnet 4.6&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Claude Sonnet 4.6&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$15&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$689&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;34×&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Claude Opus 4.7&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Claude Opus 4.7&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$25&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$1 148&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;57×&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;GPT 5.5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;GPT 5.5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$30&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\$1 157&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;58×&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 7) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 11

title Custo Mensal Projetado — 220M Input + 1,9M Output
rectangle &amp;quot;Ollama Cloud Pro
~20€&amp;quot; as OC #4CAF50
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 11

title Custo Mensal Projetado — 220M Input + 1,9M Output
rectangle &amp;quot;Ollama Cloud Pro
~20€&amp;quot; as OC #4CAF50
rectangle &amp;quot;Gemini Flash
$69&amp;quot; as GF #8BC34A
rectangle &amp;quot;DeepSeek promoção
$98&amp;quot; as DSP #FFC107
rectangle &amp;quot;GPT Mini
$174&amp;quot; as GM #FF9800
rectangle &amp;quot;Claude Haiku
\$230&amp;quot; as CH #FF5722
rectangle &amp;quot;HF/Novita
\$374&amp;quot; as HF #F44336
rectangle &amp;quot;DeepSeek normal\n\$390&amp;quot; as DSN #F44336
rectangle &amp;quot;GPT 5.4
$579&amp;quot; as G4 #D32F2F
rectangle &amp;quot;Claude Sonnet
\$689&amp;quot; as CS #D32F2F
rectangle &amp;quot;Claude Opus
$1 148&amp;quot; as CO #B71C1C
rectangle &amp;quot;GPT 5.5
$1 157&amp;quot; as G5 #880E4F

note bottom of OC
  DeepSeek V4 Pro 1.6T MoE
  + accès à tout le catalogue
  Ollama Cloud (Qwen, GLM, Kimi...)
  Inclus dans l&amp;#39;abonnement
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_custo_oculto_engenharia_de_prompts_e_iterações&amp;quot;&amp;gt;O Custo Oculto : Engenharia de Prompts e Iterações&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O custo do token é apenas uma parte da equação. O verdadeiro custo é o tempo do desenvolvedor.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com o OpenCode + Ollama Cloud Pro + meu agente de governança (EAGER/LAZY/Hot/Warm/Cold, descrito em &amp;lt;a href=&amp;quot;0108_gouvernance_agent_opencode_eager_lazy_post.html&amp;quot;&amp;gt;o artigo sobre governança&amp;lt;/a&amp;gt;), obtenho:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;0 prompt engineering repetitivo&amp;lt;/strong&amp;gt;: o contexto é injetado automaticamente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;90%+ de acerto na primeira tentativa&amp;lt;/strong&amp;gt;: o código compila de primeira&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Zero alucinação deepseek em 8 sessões consecutivas&amp;lt;/strong&amp;gt;(documentado em &amp;lt;a href=&amp;quot;0112_comparaison_kimi_glm_deepseek_long_contexte_plugin_gradle_opencode_post.html&amp;quot;&amp;gt;la comparaison Kimi/GLM/DeepSeek&amp;lt;/a&amp;gt;)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com Claude ou GPT &amp;lt;strong&amp;gt;sem&amp;lt;/strong&amp;gt; ajuste fino nem governança:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;2-3 iterações por tarefa em média&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Prompt engineering sistemática&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Alucinações nas APIs Kotlin/Gradle assim que o contexto ultrapassar 30K tokens&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O custo « efetivo » mensal — tokens × iterações × tempo de correção — é de 27× a 54× o da minha solução, como eu demonstrei no &amp;lt;a href=&amp;quot;0122_ratio_efficacite_27x_45x_flotte_experts_ia_vs_claude_gpt_post.html&amp;quot;&amp;gt;artigo sobre a relação de eficiência&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ Não é apenas uma questão de preço por token. Um Claude Opus a 57× o preço que requer 3× mais iterações, resultando em um custo efetivo ~170× maior. Para falar francamente: pagar a um estagiário o salário de um parceiro da McKinsey. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_acumular_as_assinaturas_ollama_cloud_pro_o_método_docker&amp;quot;&amp;gt;Acumular as Assinaturas Ollama Cloud Pro: O Método Docker&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se uma única assinatura não for suficiente, acumulamos‑nas. O princípio é isolar cada assinatura em sua própria instalação Ollama, com sua própria chave SSH de autenticação. Docker torna isso trivial.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O método completo está detalhado em &amp;lt;a href=&amp;quot;0120_cumuler_deux_abonnements_ollama_pro_docker_post.html&amp;quot;&amp;gt;o artigo sobre o cumul Docker&amp;lt;/a&amp;gt;, aqui está o princípio :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 9) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 12

title Arquitetura — Duas Assinaturas Ollama Cloud Pro em Uma Máquina
left to right direction

node &amp;quot;Minha Máquina Física&amp;quot; {
  frame &amp;quot;Ollama Instância A (nativo)
^^^^^
 Syntax Error? (Assumed diagram type: class)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 12

title Arquitetura — Duas Assinaturas Ollama Cloud Pro em Uma Máquina
left to right direction

node &amp;quot;Minha Máquina Física&amp;quot; {
  frame &amp;quot;Ollama Instância A (nativo)
porta :11434
Ollama Instância A (nativo)
porta :11434&amp;quot; as Native {
    database &amp;quot;Identidade A
/usr/share/ollama/.ollama/id_ed25519&amp;quot;
  }
  frame &amp;quot;Ollama Instance B (Docker)
porta :11435&amp;quot; as Docker {
    database &amp;quot;Identidade B
~/ollama-b-data/id_ed25519&amp;quot;
  }
}

cloud &amp;quot;Ollama Nuvem&amp;quot; {
  actor &amp;quot;Conta Pro A
(e‑mail 1)&amp;quot; as A
  actor &amp;quot;Conta Pro B\n(email 2)&amp;quot; as B
}

Native --&amp;amp;gt; A : &amp;quot;Chave de dispositivo A&amp;quot;
Docker --&amp;amp;gt; B : &amp;quot;Chave de dispositivo B&amp;quot;

note bottom of Docker
  Conteneur Docker isolé :
  - Clé SSH distincte
  - Port réseau distinct (11435)
  - Volume persistant (~/ollama-b-data)
  - restart: always
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O padrão é simples :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Criar um volume`~/ollama-b-data`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Iniciar um contêiner Docker`ollama/ollama:0.20.2`mapeado em`11435:11434`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Recuperar a chave SSH do contêiner → registrá‑la em ollama.com&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;fazer`ollama signin`com um segundo e-mail&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Configurar OpenCode com dois providers (`ollama`em :11434,`ollama-b`em :11435)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Duas contas, um cartão azul, zero conflito.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_que_a_auditoria_não_mede&amp;quot;&amp;gt;O Que a Auditoria Não Mede&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 40%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 60%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Ponto cego&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Impacto&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Qualidade do código produzido&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2,6 bilhões de tokens não dizem se o código compila. As minhas 8 sessões DeepSeek V4 Pro têm uma taxa de first-shot &amp;amp;gt;90%.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Ecossistema OpenCode&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O CLI é gratuito, mas o seu valor (agentes, sandboxes, histórico, caching de prompt) é imenso. Não está incluído no preço da assinatura — é uma ferramenta separada.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Custo de oportunidade&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Passar 2h/dia depurando as alucinações de um modelo inferior custa mais caro do que 20€/mês.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Tranquilidade de espírito&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tarifa fixa = sem surpresa. Com o modelo de preço por token, um pico de atividade pode fazer muito mal.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock warning&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-warning&amp;quot; title=&amp;quot;⚠️&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os preços citados são os de maio de 2026. Daqui a seis meses, tudo terá baixado. Mas a relação de força estrutural — assinatura fixa vs pagamento por uso — permanecerá favorável à assinatura para os grandes consumidores.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_o_veredito_dos_números&amp;quot;&amp;gt;Conclusão: O Veredito dos Números&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Depois de 73 dias, 1 022 sessões OpenCode e 2,6 bilhões de tokens:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 60%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 40%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Métrica&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;veredicto&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Custo acumulado da API externa&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;$0,22&amp;lt;/strong&amp;gt;(quase nulo)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Custo assinatura Ollama Cloud Pro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;~20€/mês&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Equivalente Claude Opus&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;\$1 148/mês&amp;lt;/strong&amp;gt;— 57 vezes mais caro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Equivalente GPT 5.5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;$1 157/mês&amp;lt;/strong&amp;gt;— 58 vezes mais caro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Equivalente HuggingFace/Novita&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;$374/mês&amp;lt;/strong&amp;gt;— 19× mais caro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Equivalente direto da API DeepSeek&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;\$390/mês&amp;lt;/strong&amp;gt;— 20 vezes mais caro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Equivalente mais barato (Gemini Flash)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;\$69/mês&amp;lt;/strong&amp;gt;— 3,5× mais caro, modelo inferior&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Melhor oferta do mercado&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Ollama Cloud Pro por 20€/mês&amp;lt;/strong&amp;gt;— invencível&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;__&amp;lt;/em&amp;gt; A 20€ por mês para acessar o DeepSeek V4 Pro (1.6T params, MoE, CSA+HCA, 1M contexto) via Ollama Cloud, tenho a melhor relação custo-benefício do mercado mundial. Mesmo os fornecedores chineses diretos são 5× a 20× mais caros. Mesmo o HuggingFace, com sua agregação de providers low-cost, é 19× mais caro. O mercado ocidental (Claude, GPT) está em 50-60× — outro planeta.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta razão é tão desequilibrada que não pode durar para sempre. Aproveite enquanto isso existir. &amp;lt;em&amp;gt;__&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Então sim, meus 20 reais por mês, é o melhor investimento técnico da minha vida de desenvolvedor. E agora você tem os números para provar isso.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_referências&amp;quot;&amp;gt;Referências&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0108_gouvernance_agent_opencode_eager_lazy_post.html&amp;quot;&amp;gt;Artigo 0108 — Governança Agente EAGER/LAZY/Hot/Warm/Cold&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0112_comparaison_kimi_glm_deepseek_long_contexte_plugin_gradle_opencode_post.html&amp;quot;&amp;gt;Artigo 0112 — DeepSeek V4 Pro vs Kimi K2.6 vs GLM 5.1&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Artigo 0119 — Benchmark DGX Spark vs Cloud vs Assinaturas LLM&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0120_cumuler_deux_abonnements_ollama_pro_docker_post.html&amp;quot;&amp;gt;Article 0120 — Cumuler Deux Abonnements Ollama Cloud Pro avec Docker&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0121_plugin_gradle_piloter_deux_instances_ollama_pro_post.html&amp;quot;&amp;gt;Artigo 0121 — Plugin Gradle para Pilotar Duas Instâncias Ollama&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0122_ratio_efficacite_27x_45x_flotte_experts_ia_vs_claude_gpt_post.html&amp;quot;&amp;gt;Artigo 0122 — Razão de Eficiência 27× a 54×&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_fontes_das_tarifas&amp;quot;&amp;gt;Fontes das tarifas&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://ollama.com/pricing&amp;quot;&amp;gt;Ollama Cloud — Tarifas Pro \$20/mês&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://api-docs.deepseek.com/quick_start/pricing&amp;quot;&amp;gt;DeepSeek API — Tarifas oficiais V4 Pro&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://platform.kimi.com/docs/pricing/chat-k26&amp;quot;&amp;gt;Kimi Moonshot — Tarifas K2.6&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://huggingface.co/inference/models&amp;quot;&amp;gt;HuggingFace Inference Providers — Tarifas por provider&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://www.anthropic.com/pricing&amp;quot;&amp;gt;Anthropic — Preços da API Claude&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://openai.com/api/pricing/&amp;quot;&amp;gt;OpenAI — Tarifas da API GPT&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://together.ai/pricing&amp;quot;&amp;gt;Together AI — Taxas de inferência&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://groq.com/pricing&amp;quot;&amp;gt;Groq — Preços da API&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://deepinfra.com/pricing&amp;quot;&amp;gt;DeepInfra — Preços de GPU e modelos&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://help.aliyun.com/zh/model-studio/text-generation-model/&amp;quot;&amp;gt;Alibaba Bailian — Modelos disponíveis&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/anomalyco/opencode&amp;quot;&amp;gt;OpenCode — CLI de código aberto no GitHub&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>A Arquitetura « Plugin Independente + Raiz do Consumidor » : Por que meus Builds Gradle se duplicam</title>
            <link >https://cheroliv.com//blog/2026/0124_architecture_plugin_gradle_racine_consommateur_independant_post.html</link>
            <pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0124_architecture_plugin_gradle_racine_consommateur_independant_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_três_arquiteturas_que_me_fizeram_perder_tempo&amp;quot;&amp;gt;O Problema : Três Arquiteturas Que Me Fizeram Perder Tempo&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_opção_1_o_plugin_isolado_sem_dogfooding&amp;quot;&amp;gt;Opção 1 : O Plugin Isolado (sem dogfooding)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_opção_2_o_monorepo_clássico_includeplugin&amp;quot;&amp;gt;Opção 2: O Monorepo Clássico (&amp;lt;code&amp;gt;include(&amp;quot;:plugin&amp;quot;)&amp;lt;/code&amp;gt;)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_opção_3_a_build_composto_includebuild&amp;quot;&amp;gt;Opção 3 : a Build Composto (&amp;lt;code&amp;gt;includeBuild()&amp;lt;/code&amp;gt;)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_dois_builds_independentes_uma_raiz_do_consumidor&amp;quot;&amp;gt;A Solução : Dois Builds Independentes, Uma Raiz do Consumidor&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_fluxo_de_trabalho&amp;quot;&amp;gt;O Fluxo de Trabalho&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_workflow_concreto&amp;quot;&amp;gt;O Workflow Concreto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_essa_arquitetura_me_conquistou&amp;quot;&amp;gt;Por que essa arquitetura me conquistou&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_1_dogfooding_nativo_feedback_instantâneo&amp;quot;&amp;gt;1. Dogfooding Nativo, Feedback Instantâneo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_2_clonagem_zero_config&amp;quot;&amp;gt;2. Clonagem Zero-Config&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_3_builds_isolados_ci_independentes&amp;quot;&amp;gt;3. Builds Isolados, CI Independentes&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_anatomia_do_subdiretório_name_plugin&amp;quot;&amp;gt;A Anatomia do Subdiretório `{name}-plugin/&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_catálogo_de_versões_dois_arquivos_distintos&amp;quot;&amp;gt;O Catálogo de Versões: Dois Arquivos Distintos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_não_se_faz&amp;quot;&amp;gt;O Que Não Se Faz&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_comparação_as_três_arquiteturas_face_ao_dogfooding&amp;quot;&amp;gt;Comparação: As Três Arquiteturas Face ao Dogfooding&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_contrato_dag_um_build_racin_nunca_de_um_subdiretório&amp;quot;&amp;gt;O Contrato DAG : Um Build Racin Nunca de um Subdiretório&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão_a_duplicação_aparente_que_faz_ganhar_tempo&amp;quot;&amp;gt;Conclusão: a duplicação aparente que faz ganhar tempo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referências&amp;quot;&amp;gt;Referências&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 11 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Você clona um repositório de plugin Gradle. Você abre um terminal. O que você está digitando? depois?&amp;lt;code&amp;gt;./gradlew tasks&amp;lt;/code&amp;gt;, claro. Mas em qual pasta? A raiz? O submódulo? Deve-se primeiro ler um README para saber como compilar ? Se você hesitar não seria Em um segundo, a arquitetura do projeto está quebrada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eis como resolvi esse problema de vez — e por que isso padrão é hoje a assinatura de todos os meus plugins Gradle em`foundry/public/`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_problema_três_arquiteturas_que_me_fizeram_perder_tempo&amp;quot;&amp;gt;O Problema : Três Arquiteturas Que Me Fizeram Perder Tempo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes de convergir para o padrão atual, eu tateei entre três abordagens. clássicos para organizar um plugin Gradle. Cada um tinha um defeito redhibitório.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_opção_1_o_plugin_isolado_sem_dogfooding&amp;quot;&amp;gt;Opção 1 : O Plugin Isolado (sem dogfooding)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O projeto contém apenas o plugin. Sem exemplo de consumo. Sem projeto que o exerce. Para testá-lo, é necessário criar um projeto externo, lá referenciar o plugin via`mavenLocal`ou um build composto, e somente lá para verificar que funciona&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-shell hljs&amp;quot; data-lang=&amp;quot;shell&amp;quot;&amp;gt;$ git clone mon-plugin
$ cd mon-plugin
$ ./gradlew build          # le plugin compile
$ # ... et maintenant ? comment je l&amp;#39;essaie ?&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock warning&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-warning&amp;quot; title=&amp;quot;⚠️&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um plugin Gradle sem exemplo de consumo, é uma biblioteca sem testes de integração. Você nunca sabe se a última modificação tem quebrou a experiência do usuário.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_opção_2_o_monorepo_clássico_includeplugin&amp;quot;&amp;gt;Opção 2: O Monorepo Clássico (&amp;lt;code&amp;gt;include(&amp;quot;:plugin&amp;quot;)&amp;lt;/code&amp;gt;)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Gradle`init`gera`settings.gradle.kts`com`include(&amp;quot;plugin&amp;quot;)`. O root e o submódulo compartilham o mesmo daemon, as mesmas configurações, os mesmos catálogos. Prático, mas acoplado&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;.
├── settings.gradle.kts   → include(&amp;quot;plugin&amp;quot;)
├── build.gradle.kts      → plugins { id(&amp;quot;mon-plugin&amp;quot;) }
├── plugin/
│   └── build.gradle.kts  → java-gradle-plugin
└── gradle/
    └── libs.versions.toml&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O que trava:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;a raiz&amp;lt;strong&amp;gt;deve&amp;lt;/strong&amp;gt;ter a mesma versão do Gradle que o submódulo.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;`libs.versions.toml`é compartilhado — catálogos de versão comuns, dependências&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;que fogem de um módulo para outro.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Impossível construir o plugin independentemente do root.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O CI deve construir os dois módulos, mesmo que apenas o plugin tenha mudado.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_opção_3_a_build_composto_includebuild&amp;quot;&amp;gt;Opção 3 : a Build Composto (&amp;lt;code&amp;gt;includeBuild()&amp;lt;/code&amp;gt;)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Separamos os dois em builds Gradle distintos e os conectamos via &amp;lt;code&amp;gt;includeBuild(&amp;quot;mon-plugin&amp;quot;)`em`settings.gradle.kts&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Já está melhor. O build do plugin está isolado. Mas o consumidor deve explicitamente referenciar o build externo — e a saída de `./gradlew tasks`na raiz depende da configuração correta do composite. A clonagem não é zero-config: é preciso saber que o plugin está em um diretório separado, o root faz referência a ele, etc.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-shell hljs&amp;quot; data-lang=&amp;quot;shell&amp;quot;&amp;gt;.
├── settings.gradle.kts   → includeBuild(&amp;quot;plugin-build/&amp;quot;)
├── build.gradle.kts      → plugins { id(&amp;quot;mon-plugin&amp;quot;) }
└── plugin-build/
    ├── settings.gradle.kts
    └── build.gradle.kts&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eu estava procurando melhor. Muito melhor.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_dois_builds_independentes_uma_raiz_do_consumidor&amp;quot;&amp;gt;A Solução : Dois Builds Independentes, Uma Raiz do Consumidor&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este é o padrão que acabei por adotar :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;.
├── settings.gradle.kts          ← racine consommateur
├── build.gradle.kts              ← 3 lignes : apply plugin + dogfood
├── gradle/
│   └── libs.versions.toml        ← catalogue du consommateur
├── {name}-plugin/                ← BUILD INDÉPENDANT
│   ├── gradlew                    ← son propre wrapper
│   ├── settings.gradle.kts        ← rootProject.name = &amp;quot;{name}-plugin&amp;quot;
│   ├── build.gradle.kts           ← java-gradle-plugin, signing, publish
│   ├── gradle/
│   │   ├── libs.versions.toml     ← catalogue du plugin
│   │   └── wrapper/
│   ├── src/                       ← sources du plugin
│   ├── .agents/                   ← gouvernance agent
│   └── *.adoc                     ← AGENT, PROMPT_REPRISE, snapshot, etc.
└── site.yml / slides-context.yml / ...   ← configs dogfood&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;❗&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A chave:`{name}-plugin/`é um projeto Gradle &amp;lt;strong&amp;gt;completo e autônomo&amp;lt;/strong&amp;gt;. Ele tem o próprio wrapper, o próprio settings, o próprio católico de versões. Ele é clonado, buildado, testado e publicado sem que o root seja ciente de sua existência&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O root, ele, só faz &amp;lt;strong&amp;gt;uma&amp;lt;/strong&amp;gt; coisa : aplicar o plugin&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;plugins {
    alias(libs.plugins.bakery)
}

repositories {
    mavenLocal()
    mavenCentral()
}

bakery { configPath = file(&amp;quot;site.yml&amp;quot;).absolutePath }&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Três linhas no caso de`bakery-gradle`. Nada mais Zero`include()&amp;lt;code&amp;gt;, zero`includeBuild()&amp;lt;/code&amp;gt;, zero subprojeto. Um build Gradle clássico que aplica um plugin como qualquer qual consumidor faria isso&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_fluxo_de_trabalho&amp;quot;&amp;gt;O Fluxo de Trabalho&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/architecture-deux-builds.svg&amp;quot; alt=&amp;quot;architecture deux builds&amp;quot; width=&amp;quot;1267&amp;quot; height=&amp;quot;151&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_workflow_concreto&amp;quot;&amp;gt;O Workflow Concreto&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-shell hljs&amp;quot; data-lang=&amp;quot;shell&amp;quot;&amp;gt;# 1. Builder le plugin
$ cd codebase-plugin
$ ./gradlew publishToMavenLocal

# 2. L&amp;#39;exercer depuis la racine
$ cd ..
$ ./gradlew indexCodebase queryCodebase snapshot

# La boucle est fermée. Le plugin est testé dans des conditions
# réelles de consommation, par le projet même qui l&amp;#39;héberge.&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_essa_arquitetura_me_conquistou&amp;quot;&amp;gt;Por que essa arquitetura me conquistou&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Três benefícios que, combinados, valem o custo da «duplicação»:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_1_dogfooding_nativo_feedback_instantâneo&amp;quot;&amp;gt;1. Dogfooding Nativo, Feedback Instantâneo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O melhor teste de um plugin Gradle é usá-lo. Não é um teste unitário mockado. Não é um`GradleRunner`com um projeto de teste. Um &amp;lt;strong&amp;gt;verdadeiro&amp;lt;/strong&amp;gt; build que aplica o plugin em arquivos reais.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-shell hljs&amp;quot; data-lang=&amp;quot;shell&amp;quot;&amp;gt;$ git clone bakery-gradle
$ cd bakery-gradle
$ ./gradlew bake    # ← le plugin est exercé immédiatement&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se o plugin estiver quebrado, o build raiz diz isso. Não é necessário ir procurar um projeto de teste externo. O dogfooding é a &amp;lt;strong&amp;gt;primeira&amp;lt;/strong&amp;gt;. tarefa que lança um novo contribuidor. É o teste de fumaça definitivo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock tip&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-tip&amp;quot; title=&amp;quot;💡&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A regra é simples: se a raiz compila e que`./gradlew tasks` exibe suas tarefas de plugin, o plugin está funcional. Sem surpresa em produção.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_2_clonagem_zero_config&amp;quot;&amp;gt;2. Clonagem Zero-Config&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Un `git clone &amp;amp;amp;&amp;amp;amp; ./gradlew tasks`e o recém-chegado vê tudo caminhar sem configurar nada. O`build.gradle.kts`raiz é a documentação viva de uso do plugin. O`site.yml`ao lado Mostra a configuração esperada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Compare com a alternativa: um README de três parágrafos que explica como construir o plugin e como construir o projeto de teste. Um novo colaborador lê o README na diagonal, está enganado, abre uma issue — enquanto a informação poderia ser &amp;lt;strong&amp;gt;executável&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;❗&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A documentação mais robusta não é aquela que se lê. É aquela que a gente&amp;lt;strong&amp;gt;executa&amp;lt;/strong&amp;gt;. O build raiz é a documentação executável do plugin.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_3_builds_isolados_ci_independentes&amp;quot;&amp;gt;3. Builds Isolados, CI Independentes&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plugin tem seu próprio wrapper Gradle, seu próprio ciclo de vida, seus próprios testes. Você pode :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Atualizar o Gradle no plugin sem tocar na raiz&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Adicionar uma dependência no plugin sem que ela vaze para a raiz&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Quebrar o plugin sem afetar o build raiz (enquanto você não&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;não publique a versão quebrada)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ter uma CI que build/test o plugin, e outra que exerce o&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;root — independentemente&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;.github/workflows/
├── test-plugin.yml      → codebase-plugin/.gradlew build
├── test-root.yml        → .gradlew tasks (vérifie que le plugin est consommable)
└── publish.yml          → codebase-plugin/.gradlew publish&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_anatomia_do_subdiretório_name_plugin&amp;quot;&amp;gt;A Anatomia do Subdiretório `{name}-plugin/&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Vamos dar uma olhada mais atenta no que vive na pasta do plugin:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;codebase-plugin/
├── gradlew                           ← wrapper indépendant
├── settings.gradle.kts               ← @Suppress(&amp;quot;UnstableApiUsage&amp;quot;)
│                                       + foobar-resolver-convention
├── build.gradle.kts                  ← java-gradle-plugin + signing + publish
├── gradle/
│   ├── libs.versions.toml            ← catalogue complet (langchain4j, pgvector...)
│   └── wrapper/
├── buildSrc/                         ← classes utilitaires buildSrc
│   ├── build.gradle.kts
│   └── src/main/kotlin/
│       ├── codebase/                 ← CodebaseYmlAnonymizer, CodebaseConfiguration
│       ├── benchmark/                ← BenchmarkConfig, BenchmarkProtocol
│       ├── readme/                   ← ReadmeYmlAnonymizer
│       ├── site/                     ← SiteYmlAnonymizer
│       ├── slider/                   ← SliderYmlAnonymizer
│       └── snapshot/                 ← SnapshotManager
├── src/
│   ├── main/kotlin/codebase/
│   │   ├── CodebasePlugin.kt         ← class Plugin&amp;amp;lt;Project&amp;amp;gt;
│   │   ├── rag/                      ← pgvector, embedding, anonymization...
│   │   ├── benchmark/                ← BenchmarkRunner, export, comparison...
│   │   └── walker/                   ← WorkspaceWalker
│   └── test/
│       ├── kotlin/codebase/scenarios/ ← steps Cucumber
│       ├── features/                  ← .feature files
│       └── resources/datasets/        ← fixtures .adoc, .yml, .json
├── .agents/                          ← gouvernance agent (INDEX, SESSIONS, etc.)
├── AGENT.adoc                        ← règles agent
├── PROMPT_REPRISE.adoc              ← mission session
├── BACKLOG.adoc                      ← backlog produit
├── snapshot.adoc                     ← snapshot auto-généré du projet
└── embeds.yml                        ← config RAG embeds&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Tudo está aqui. Não há dispersão entre a raiz e o subdiretório. O desenvolvedor que trabalha no plugin nunca precisa de sair`codebase-plugin/`. O desenvolvedor que usa o plugin só olha a raiz — e o`build.gradle.kts`de 3 linhas ele lhe diz tudo o que ele precisa saber&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_catálogo_de_versões_dois_arquivos_distintos&amp;quot;&amp;gt;O Catálogo de Versões: Dois Arquivos Distintos&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 37.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 37.5%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;gradle/libs.versions.toml&amp;lt;/code&amp;gt;(raiz)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;{name}-plugin/gradle/libs.versions.toml&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Número de dependências&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2-3 (plugin + readme opcionalmente)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;30+ (langchain4j, pgvector, cucumber&amp;amp;#8230;&amp;amp;#8203;)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;papel&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Consumir o plugin&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Construa o plugin&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Quem o lê&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O utilizador do plugin&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O desenvolvedor do plugin&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O root tem um catálogo intencionalmente mínimo. O plugin tem um catálogo completo. A confusão é impossível: cada build tem seu próprio scope de dependências.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;ℹ️&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se já passou uma hora depurando uma colisão de dependências entre o seu plugin e o seu projeto de teste, você compreende o valor de esta separação. Os catálogos independentes eliminam esse problema por construção.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_que_não_se_faz&amp;quot;&amp;gt;O Que Não Se Faz&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este padrão não é mágico. Ele impõe uma restrição que eu Me inflige voluntariamente:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O root não constrói o plugin.&amp;lt;/strong&amp;gt; Você deve`publishToMavenLocal` ou implantar em um repositório antes que o root possa consumi‑lo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É um custo mínimo, e é a &amp;lt;strong&amp;gt;boa&amp;lt;/strong&amp;gt; restrição. O root Consome o plugin como um cliente externo — via Maven. Exatamente como faria um projeto de terceiros. Se o plugin não for publicável, o root lhe diz imediatamente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-shell hljs&amp;quot; data-lang=&amp;quot;shell&amp;quot;&amp;gt;# La seule &amp;quot;friction&amp;quot; du pattern
$ cd codebase-plugin &amp;amp;amp;&amp;amp;amp; ./gradlew publishToMavenLocal &amp;amp;amp;&amp;amp;amp; cd ..
$ ./gradlew tasks --group=codebase&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_comparação_as_três_arquiteturas_face_ao_dogfooding&amp;quot;&amp;gt;Comparação: As Três Arquiteturas Face ao Dogfooding&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 20%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 20%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 20%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 20%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 20%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Plugin isolado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Monorepo`include()`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Composto`includeBuild()`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Raiz + plugin ind.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`git clone &amp;amp;amp;&amp;amp;amp; gradlew tasks`dá as tarefas do plugin&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�❌&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;[No output]&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Construir plugin independente do root&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�❌&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Catálogo de versões separado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;(empty)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;❌&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;(No output, as no French text was provided for translation)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Não`include()` ni &amp;lt;code&amp;gt;includeBuild()&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�❌&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�❌&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Dogfood nativo sem config&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�❌&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Could you please provide the French text you would like translated?&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;plugin CI independiente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�❌&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O root é um exemplo de consumo real&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�❌&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;[No output]&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A coluna da direita marca todas as caixas. É por isso que eu não Voltarei mais para trás&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_contrato_dag_um_build_racin_nunca_de_um_subdiretório&amp;quot;&amp;gt;O Contrato DAG : Um Build Racin Nunca de um Subdiretório&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta arquitetura integra-se naturalmente no DAG N0→N3 do meu workspace. O padrão é : &amp;lt;strong&amp;gt;o plugin N2 é o hub de suas próprias dependências, a raiz N3 é um terminal que aplica *hubs&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/contrat-dag-architecture.svg&amp;quot; alt=&amp;quot;contrat dag architecture&amp;quot; width=&amp;quot;1751&amp;quot; height=&amp;quot;5508&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le &amp;lt;code&amp;gt;codebase-gradle/build.gradle.kts`faz 6 linhas. Não`src/&amp;lt;/code&amp;gt;, não`buildSrc/&amp;lt;code&amp;gt;, não`gradle/rag-bench.gradle.kts&amp;lt;/code&amp;gt;. Justo &amp;lt;code&amp;gt;plugins { alias(libs.plugins.codebase) }`e os repositórios. Toda a complexidade vive em`codebase-plugin/&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_a_duplicação_aparente_que_faz_ganhar_tempo&amp;quot;&amp;gt;Conclusão: a duplicação aparente que faz ganhar tempo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando eu mostro esta estrutura a alguém, a primeira reação é frequentemente : « Mas tu tens dois`gradlew`, dois`settings.gradle.kts`, dois`libs.versions.toml`— é duplicação!&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sim. E não.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A « duplicação », é reproduzir a mesma informação em dois locais. Aqui, são dois arquivos &amp;lt;strong&amp;gt;distintos&amp;lt;/strong&amp;gt; que servem dois usos &amp;lt;strong&amp;gt;distintos&amp;lt;/strong&amp;gt; : o catálogo do plugin (30+ dependências para builder) e o catálogo da raiz (2-3 dependências para consumir). O wrapper do plugin (versão bloqueada para o desenvolvimento) e o wrapper da raiz (versão potencialmente diferente, para o exercício do plugin).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não é duplicação. É de &amp;lt;strong&amp;gt;separação dos responsabilidades&amp;lt;/strong&amp;gt; aplicada ao sistema de build. Cada build Faz uma coisa, e apenas uma. A raiz consome. O plugin se constrói.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O custo? Uma ordem`publishToMavenLocal`entre o build do plugin e o build da raiz. O ganho? Uma clareza arquitetural que elimina horas de depuração a jusante&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Desde que eu implementei este padrão em`bakery-gradle`, &amp;lt;code&amp;gt;plantuml-gradle&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;codebase-gradle&amp;lt;/code&amp;gt;, e os outros plugins de`foundry/public/&amp;lt;code&amp;gt;, não tenho nunca mais hesitei ao abrir um terminal em um dos meus repositórios. O primeiro reflexo —&amp;lt;/code&amp;gt;./gradlew tasks`— anda sempre, dá sempre as boas tarefas, e me diz instantaneamente se tudo está sadio.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É isso, a boa arquitetura. Ela não se lê num README. Ela é testada em um terminal, em menos de dez segundos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_referências&amp;quot;&amp;gt;Referências&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0105_integrer_graphify_workflow_gradle_post.html&amp;quot;&amp;gt;Artigo 0105 — Integrar Graphify em um fluxo de trabalho Gradle&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0117_granularisation_oss_css_repositorie_workspace_post.html&amp;quot;&amp;gt;Artigo 0117 — A Granularização OSS/CSS&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0123_knowledge_graph_moteur_recommandation_articles_connexes_post.html&amp;quot;&amp;gt;Artigo 0123 — O Knowledge Graph como motor de recomendação&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>O Knowledge Graph como motor de recomendação: como eu matei os « artigos relacionados » cronológicos</title>
            <link >https://cheroliv.com//blog/2026/0123_knowledge_graph_moteur_recommandation_articles_connexes_post.html</link>
            <pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0123_knowledge_graph_moteur_recommandation_articles_connexes_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_cena_terça_feira_12_de_maio_17h30&amp;quot;&amp;gt;A Cena : Terça-feira, 12 de Maio, 17h30&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_diagnóstico_três_tipos_de_relações_que_nenhum_modelo_vê&amp;quot;&amp;gt;O Diagnóstico : Três Tipos de Relações Que Nenhum Modelo Vê&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_um_pipeline_gradle_não_uma_chamada_llm_a_quente&amp;quot;&amp;gt;A Solução: Um Pipeline Gradle, Não uma chamada LLM a quente&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_arquitetura_bakery_importa_graphify_não_engine&amp;quot;&amp;gt;A Arquitetura: Bakery importa Graphify, não Engine&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_pipeline_scan_grafo_modelo&amp;quot;&amp;gt;O Pipeline: Scan → Grafo → Modelo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fallback_cronológico&amp;quot;&amp;gt;Fallback cronológico&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_ontologia_emergente_quando_os_artigos_se_agrupam_sem_se_conhecer&amp;quot;&amp;gt;A Ontologia Emergente: Quando os Artigos se Agrupam sem se Conhecer&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_contrato_dag_quem_importa_quem&amp;quot;&amp;gt;O Contrato DAG: Quem Importa Quem&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_ganhamos_e_o_que_não_ganhamos&amp;quot;&amp;gt;O Que Ganhamos (E O Que Não Ganhamos)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_perspectivas_o_blog_como_knowledge_graph_público&amp;quot;&amp;gt;Perspectivas: O Blog Como Knowledge Graph Público&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão_o_sistema_de_arquivos_sabe_o_que_o_modelo_ignora&amp;quot;&amp;gt;Conclusão: O Sistema de Arquivos Sabe o Que o Modelo Ignora&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referências&amp;quot;&amp;gt;Referências&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 14 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Você está lendo um artigo sobre a integração do pgvector com o LangChain4j. Na parte inferior da página, JBake Sugere-lhe « Artigos relacionados ». Você clica. É um artigo sobre&amp;amp;#8230;&amp;amp;#8203; a configuração de Kitty Terminal. O único ponto em comum entre os dois? Eles foram publicados a menos de quinze dias de diferença. Não é uma recomendação. É um calendário disfarçado de editorial.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_cena_terça_feira_12_de_maio_17h30&amp;quot;&amp;gt;A Cena : Terça-feira, 12 de Maio, 17h30&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Estou relendo um dos meus artigos — &amp;lt;a href=&amp;quot;0106_knowledge_graph_outil_comprehension_codebase_post.html&amp;quot;&amp;gt;aquele sobre o Knowledge Graph como ferramenta de compreensão de codebase&amp;lt;/a&amp;gt; O conteúdo é denso: nós, arestas, comunidades, PlantUML, onboarding. Um artigo técnica de 15 minutos de leitura que mobiliza`graphify-gradle`, &amp;lt;code&amp;gt;plantuml-gradle&amp;lt;/code&amp;gt;, e os conceitos de topologia de grafo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Na parte inferior da página, os « Artigos relacionados » me sugerem :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Um artigo sobre Firebase Contact Form (0113)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Um artigo sobre o mecanismo Eager/Lazy (0108)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Um artigo sobre a migração de script Gradle → plugin (0102)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Um artigo sobre o acúmulo de assinaturas Ollama Pro (0120)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Três desses quatro artigos não têm &amp;lt;strong&amp;gt;nenhuma&amp;lt;/strong&amp;gt; relação com o Knowledge Graph. Eles estão lá porque são os últimos publicados — proximidade temporal, não vizinhança semântica&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Estou olhando para o template`post.thyme`[No output]&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-xhtml hljs&amp;quot; data-lang=&amp;quot;xhtml&amp;quot;&amp;gt;&amp;amp;lt;!-- Articles connexes (liens internes SEO) --&amp;amp;gt;
&amp;amp;lt;th:block th:each=&amp;quot;post,postStat : ${published_posts}&amp;quot;&amp;amp;gt;
    &amp;amp;lt;th:block th:if=&amp;quot;${!post.uri.equals(content.uri) and postStat.index lt 4}&amp;quot;&amp;amp;gt;
        ...
    &amp;amp;lt;/th:block&amp;amp;gt;
&amp;amp;lt;/th:block&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`published_posts`é uma lista cronológica.`postStat.index lt 4`pega os Os quatro primeiros que não são o post atual. É isso. Nenhuma lógica. de similaridade. Nenhuma noção de conteúdo. Só um laço.`for`disfarçada de recomendação.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock warning&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-warning&amp;quot; title=&amp;quot;⚠️&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não é um bug do JBake. É o comportamento padrão de tudo gerador de site estático : a lista de posts é plana e ordenada por data. O template faz o que pode com o que tem.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas isso não é um motivo para aceitá-lo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_diagnóstico_três_tipos_de_relações_que_nenhum_modelo_vê&amp;quot;&amp;gt;O Diagnóstico : Três Tipos de Relações Que Nenhum Modelo Vê&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Meu corpus de artigos — 23 posts em 4 meses — tem uma estrutura rica que o modelo cronológico esmaga completamente :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Referências explícitas&amp;lt;/strong&amp;gt;: eu uso`xref:`massivamente em meus artigos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O artigo 0122 referência 0106 (knowledge graph) e 0116 (compartimentamento) epistemológico). Estes links são &amp;lt;strong&amp;gt;hard linking&amp;lt;/strong&amp;gt; editorial — eu tenho Decidiu deliberadamente conectar esses conceitos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Tags compartilhados&amp;lt;/strong&amp;gt; : cada artigo tem alguns`:jbake-tags:`. O artigo sobre&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Graphify + PlantUML (0105) tem`gradle, graphify, plantuml, knowledge-graph`. O artigo sobre o Knowledge Graph (0106) tem &amp;lt;code&amp;gt;knowledge-graph, graphify, plantuml&amp;lt;/code&amp;gt;. Três tags em comum de sete — 43% de cobertura.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Entidades nomeadas co‑occurrentes&amp;lt;/strong&amp;gt;: « pgvector », « RAG », « embedding&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;aparecem juntos em quatro artigos diferentes. « LangChain4j »,  Ollama », « plugin Gradle » em outros seis. Não são tags declarados — são os &amp;lt;strong&amp;gt;padrões emergentes&amp;lt;/strong&amp;gt; do corpus que apenas um NLP pode detectar.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/trois-couches-relation-articles.svg&amp;quot; alt=&amp;quot;trois couches relation articles&amp;quot; width=&amp;quot;1736&amp;quot; height=&amp;quot;5492&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Hoje, meu site não usa &amp;lt;strong&amp;gt;nenhuma&amp;lt;/strong&amp;gt; dessas três camadas. Ele usa a camada zero: a ordem de inserção numa lista Java.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_um_pipeline_gradle_não_uma_chamada_llm_a_quente&amp;quot;&amp;gt;A Solução: Um Pipeline Gradle, Não uma chamada LLM a quente&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A primeira tentação seria chamar um LLM no momento do bake : « Para Este artigo, encontra os três artigos mais semelhantes no corpus. Não faça isso&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;❗&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Uma chamada LLM para cada`./gradlew bake`custa tempo, dinheiro e introduz não-determinismo na sua construção. A resposta do LLM pode alternar entre duas builds sem que o conteúdo tenha sido alterado. Seu CI se torna não reproduzível, seus testes ficam instáveis.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A solução é determinística: um pipeline Gradle que pré-calcula o grafo de similaridade e o armazena em`graph.json`. O modelo lê o resultado — Ele nunca desencadeia um cálculo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_arquitetura_bakery_importa_graphify_não_engine&amp;quot;&amp;gt;A Arquitetura: Bakery importa Graphify, não Engine&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este é o ponto de arquitetura mais importante. A tentação seria de cablear a colaboração em`engine/build.gradle.kts`: motor aplica graphify para o scan, bakery para o bake, e o engine faz a ponte entre os dois&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É um anti-padrão. Engine é um &amp;lt;strong&amp;gt;terminal consumidor&amp;lt;/strong&amp;gt; — ele aplica dos plugins, ele não implementa lógica de negócio. A regra é: &amp;lt;strong&amp;gt;A carga da prova está no plugin proprietário.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/bakery-import-graphify.svg&amp;quot; alt=&amp;quot;bakery import graphify&amp;quot; width=&amp;quot;1738&amp;quot; height=&amp;quot;5700&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Figure 1. Arquitetura correta — Bakery importa Graphify&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O contrato DAG é respeitado: bakery (N2) importa graphify (N0), N2 &amp;amp;gt; N0, nenhuma violação. Motor (N3) importa padaria (N2), N3 &amp;amp;gt; N2, OK.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Engine não tem &amp;lt;strong&amp;gt;nenhuma&amp;lt;/strong&amp;gt; linha de código que referencia`graph.json`, &amp;lt;code&amp;gt;relatedPosts&amp;lt;/code&amp;gt;, ou qualquer outra noção de similaridade. Ele aplica bakery, ponto. a A colaboração é interna à padaria.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_pipeline_scan_grafo_modelo&amp;quot;&amp;gt;O Pipeline: Scan → Grafo → Modelo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este é o pipeline completo em três etapas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Scan&amp;lt;/strong&amp;gt; (graphify) :`graphify-plugin`escaneia o workspace. No seu&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;forma atual, ele já detecta os`xref:`entre`.adoc`e os expõe em`graph.json`como das edges de tipo`reference`.&amp;lt;br&amp;gt;
Nós o enriquecemos para o conteúdo editorial: * Analisa os metadados do JBake (&amp;lt;code&amp;gt;:jbake-tags:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;:jbake-description:&amp;lt;/code&amp;gt;) de cada`.adoc`do blog * Calcula as co-ocorrências de tags → arestas`tag_cooccurrence`com peso * Extrai as entidades nomeadas das descrições via TF-IDF → edges`entity_overlap` * Injeta uma seção`blog_articles`no`graph.json`existente&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// Extrait de l&amp;#39;enrichissement graphify pour le blog
fun enrichBlogSection(graphJson: File, blogDir: File): GraphJson {
    val articles = blogDir.listFiles { f -&amp;amp;gt; f.extension == &amp;quot;adoc&amp;quot; }
        .map { parseJbakeMetadata(it) }

    val nodes = articles.map { ArticleNode(it.slug, it.title, it.tags) }
    val edges = mutableListOf&amp;amp;lt;GraphEdge&amp;amp;gt;()

    // Couche 1 : xref (déjà fait par scanWorkspace)

    // Couche 2 : co-occurrences de tags
    for (a in articles) {
        for (b in articles) {
            if (a.slug == b.slug) continue
            val common = a.tags.intersect(b.tags)
            if (common.isNotEmpty()) {
                edges.add(GraphEdge(
                    source = a.slug,
                    target = b.slug,
                    type = &amp;quot;tag_cooccurrence&amp;quot;,
                    weight = common.size.toDouble() / (a.tags.size + b.tags.size)
                ))
            }
        }
    }

    return graphJson.copy(
        blogArticles = BlogSection(nodes, edges)
    )
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Bake&amp;lt;/strong&amp;gt; (padaria) : no momento do`./gradlew bake`, `BakeryPlugin`cama&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`graph.json`e resolve os artigos relacionados para cada post.&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// BakeryPlugin — résolution des articles connexes
fun resolveRelatedPosts(
    currentSlug: String,
    graph: GraphJson,
    maxResults: Int = 4
): List&amp;amp;lt;RelatedPost&amp;amp;gt; {
    val edges = graph.blogArticles.edges
        .filter { it.source == currentSlug || it.target == currentSlug }

    return edges
        .sortedByDescending { it.weight }
        .take(maxResults)
        .map { edge -&amp;amp;gt;
            val relatedSlug = if (edge.source == currentSlug) edge.target else edge.source
            graph.blogArticles.nodes.first { it.slug == relatedSlug }
        }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Template&amp;lt;/strong&amp;gt; (&amp;lt;code&amp;gt;post.thyme&amp;lt;/code&amp;gt;) : o modelo JBake agora recebe um mapa&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;estruturada em vez de uma lista cronológica plana.&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-xhtml hljs&amp;quot; data-lang=&amp;quot;xhtml&amp;quot;&amp;gt;&amp;amp;lt;!-- Articles connexes basés sur le Knowledge Graph --&amp;amp;gt;
&amp;amp;lt;th:block th:if=&amp;quot;${relatedPosts != null and !relatedPosts.empty}&amp;quot;&amp;amp;gt;
    &amp;amp;lt;section class=&amp;quot;mt-5 pt-4 border-top&amp;quot;&amp;amp;gt;
        &amp;amp;lt;h2 class=&amp;quot;h4 mb-3&amp;quot;&amp;amp;gt;Articles connexes&amp;amp;lt;/h2&amp;amp;gt;
        &amp;amp;lt;th:block th:each=&amp;quot;related : ${relatedPosts}&amp;quot;&amp;amp;gt;
            &amp;amp;lt;div class=&amp;quot;mb-2&amp;quot;&amp;amp;gt;
                &amp;amp;lt;a th:href=&amp;quot;${content.rootpath} + ${related.uri}&amp;quot;
                   th:text=&amp;quot;${related.title}&amp;quot; class=&amp;quot;fw-semibold&amp;quot;&amp;amp;gt;&amp;amp;lt;/a&amp;amp;gt;
                &amp;amp;lt;br/&amp;amp;gt;
                &amp;amp;lt;small class=&amp;quot;text-muted&amp;quot;&amp;amp;gt;
                    &amp;amp;lt;th:block th:each=&amp;quot;reason,iterStat : ${related.reasons}&amp;quot;&amp;amp;gt;
                        &amp;amp;lt;span class=&amp;quot;badge bg-light text-dark&amp;quot;
                              th:text=&amp;quot;${reason}&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;
                    &amp;amp;lt;/th:block&amp;amp;gt;
                &amp;amp;lt;/small&amp;amp;gt;
            &amp;amp;lt;/div&amp;amp;gt;
        &amp;amp;lt;/th:block&amp;amp;gt;
    &amp;amp;lt;/section&amp;amp;gt;
&amp;amp;lt;/th:block&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O modelo é o mesmo — ele não sabe &amp;lt;strong&amp;gt;de onde&amp;lt;/strong&amp;gt; vêm os dados. Só o contrato entre bakery e JBake mudou :`published_posts`(lista cronológico) torna-se`relatedPosts`(mapa ponderado pelo grafo).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;ℹ️&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O selo « razão » (ex:`xref 0122`, &amp;lt;code&amp;gt;tag:gradle&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cluster:pgvector-rag&amp;lt;/code&amp;gt;) Explique ao leitor &amp;lt;strong&amp;gt;por que&amp;lt;/strong&amp;gt; este artigo é relacionado. Isto não é justo de transparência — é pedagogia sobre a topologia de seu próprio conteúdo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_fallback_cronológico&amp;quot;&amp;gt;Fallback cronológico&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Si &amp;lt;code&amp;gt;graph.json&amp;lt;/code&amp;gt; está ausente (build local sem scan prévio, primeiro implantação, CI que ainda não integrou o scan graphify), o modelo deve degradar graciosamente :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;fun resolveRelatedPosts(currentSlug: String, graph: GraphJson?): List&amp;amp;lt;RelatedPost&amp;amp;gt; {
    if (graph != null &amp;amp;amp;&amp;amp;amp; graph.blogArticles != null) {
        return resolveFromGraph(currentSlug, graph)
    }
    // Fallback chronologique — même comportement qu&amp;#39;aujourd&amp;#39;hui
    logger.warn(&amp;quot;[bakery] graph.json absent — fallback chronologique&amp;quot;)
    return resolveFromChronology(currentSlug)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O comportamento padrão é idêntico ao comportamento atual. O motor de recomendação é uma &amp;lt;strong&amp;gt;melhoria progressiva&amp;lt;/strong&amp;gt; — não é alteração quebra&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_ontologia_emergente_quando_os_artigos_se_agrupam_sem_se_conhecer&amp;quot;&amp;gt;A Ontologia Emergente: Quando os Artigos se Agrupam sem se Conhecer&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A camada mais interessante é a terceira: a ontologia emergente. Artigos que não são citados, que não têm as mesmas tags, mais que &amp;lt;strong&amp;gt;falam da mesma coisa&amp;lt;/strong&amp;gt; sem saber.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Vamos considerar um exemplo concreto. Três artigos do meu corpus:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;0119 — Benchmark DGX Spark vs Cloud Assinatura LLM&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;0121 — Plugin Gradle Pilotar Duas Instâncias Ollama Pro&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;0122 — Razão Eficiência 27x 45x Frota Especialistas IA&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Estes três artigos não têm &amp;lt;strong&amp;gt;nenhum&amp;lt;/strong&amp;gt; xref entre eles. As tags deles não se coincidem apenas em 20% (« ollama », « llm » comuns). Mas um NLP leve sobre as descrições revela um cluster evidente: « custo », « assinatura »,  nuvem », « GPU », « chave da API », « Ollama Pro », « eficiência », « razão&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eles formam um cluster ontológico : &amp;lt;strong&amp;gt;a economia do LLM self-hosted vs cloud&amp;lt;/strong&amp;gt;. Este cluster não é declarado em nenhum lugar. Ele emerge do corpus.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/cluster-ontologique-exemple.svg&amp;quot; alt=&amp;quot;cluster ontologique exemple&amp;quot; width=&amp;quot;697&amp;quot; height=&amp;quot;405&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este cluster ontológico torna-se um edge composto em`graph.json`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-json hljs&amp;quot; data-lang=&amp;quot;json&amp;quot;&amp;gt;{
  &amp;quot;source&amp;quot;: &amp;quot;0119-benchmark-dgx-spark&amp;quot;,
  &amp;quot;target&amp;quot;: &amp;quot;cluster:economie-llm&amp;quot;,
  &amp;quot;type&amp;quot;: &amp;quot;entity_cluster&amp;quot;,
  &amp;quot;weight&amp;quot;: 0.73,
  &amp;quot;metadata&amp;quot;: {
    &amp;quot;clusterLabel&amp;quot;: &amp;quot;Économie LLM Self-Hosted vs Cloud&amp;quot;,
    &amp;quot;commonEntities&amp;quot;: [&amp;quot;coût&amp;quot;, &amp;quot;GPU&amp;quot;, &amp;quot;abonnement&amp;quot;, &amp;quot;Ollama Pro&amp;quot;, &amp;quot;ratio&amp;quot;],
    &amp;quot;articlesInCluster&amp;quot;: [
      &amp;quot;0119-benchmark-dgx-spark&amp;quot;,
      &amp;quot;0121-ollama-pro-deux-instances&amp;quot;,
      &amp;quot;0122-ratio-efficacite-flotte-experts&amp;quot;
    ]
  }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O NLP é intencionalmente leve — TF-IDF + similaridade cosseno sobre os descrições. Não é necessário BERT, não é necessário modelo de linguagem. O corpus contém 23 artigos, a matriz de similaridade cabe em um arquivo JSON de 50 Ko.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock tip&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-tip&amp;quot; title=&amp;quot;💡&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O poder do NLP não vem da sofisticação do algoritmo, mais do &amp;lt;strong&amp;gt;tamanho do corpus&amp;lt;/strong&amp;gt; e da &amp;lt;strong&amp;gt;qualidade das descrições&amp;lt;/strong&amp;gt;. Uma `:jbake-description:`bem escrita de 150 caracteres contém mais de sinal para TF-IDF que um artigo inteiro de 3000 palavras.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_contrato_dag_quem_importa_quem&amp;quot;&amp;gt;O Contrato DAG: Quem Importa Quem&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este é o ponto onde a disciplina arquitetônica paga. O DAG N0→N3 definido em`engine/build.gradle.kts`dá uma regra simples : nenhum projeto importa um projeto de nível superior.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 18.1818%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 27.2728%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Plugin consumidor&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Plugin importado&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Nível do consumidor&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Nível importado&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Válido?&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;bakery-gradle&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;graphify-gradle&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;N2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;N0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅ N2 &amp;amp;gt; N0 — OK&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;motor&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;bakery-gradle&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;N3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;N2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅ N3 &amp;amp;gt; N2 — OK&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;motor&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;graphify-gradle&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;N3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;N0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�✅ Técnica OK, mas &amp;lt;strong&amp;gt;conceitualmente errado&amp;lt;/strong&amp;gt; — a colaboração é interna à bakery&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Motor aplica padaria. Padaria aplica graphify. É tudo. Se um dia Eu quero adicionar o codebase do vector store (N1) para pesquisa. semântica cross-corpus, bakery também o importa. Engine não muda.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;❗&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O padrão é : &amp;lt;strong&amp;gt;o plugin N2 é o hub de suas próprias dependências&amp;lt;/strong&amp;gt; Engine N3 não é um hub — é um terminal que aplica hubs.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_que_ganhamos_e_o_que_não_ganhamos&amp;quot;&amp;gt;O Que Ganhamos (E O Que Não Ganhamos)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O ganho principal é editorial, não técnico :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Antes&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Depois&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Artigos relacionados = últimos 4 posts&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Artigos relacionados = top 4 por peso no Knowledge Graph&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Lecteur lê do RAG → recomendação Kitty Terminal&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Leitor lê do RAG → recomendação pgvector, chunking, vector store&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Zero transparência sobre o porquê&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;crachá`xref 0122`, &amp;lt;code&amp;gt;tag:gradle&amp;lt;/code&amp;gt;, `cluster:rag`visível&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;JBake padrão, zero esforço, zero valor&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Pipeline Gradle determinístico, reprodutível, testável&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nenhuma curva de aprendizagem para o leitor&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O leitor descobre a &amp;lt;strong&amp;gt;topologie&amp;lt;/strong&amp;gt; do meu conteúdo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O que &amp;lt;strong&amp;gt;não&amp;lt;/strong&amp;gt; se ganha :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Não é um « verdadeiro » motor de recomendação (sem colaborativo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;filtragem, sem teste A/B, sem loop de feedback)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;A qualidade depende da riqueza dos metadados JBake — se seus&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;as descrições estão vazias, TF-IDF não vê nada&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Le NLP est offline — les nouveaux articles ne sont clusterisés qu&amp;amp;#8217;au&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;próxima varredura (isso está bom: o build permanece determinístico)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_perspectivas_o_blog_como_knowledge_graph_público&amp;quot;&amp;gt;Perspectivas: O Blog Como Knowledge Graph Público&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta funcionalidade abre uma perspectiva mais ampla: e se`cheroliv.com` estava se tornando ele mesmo um &amp;lt;strong&amp;gt;knowledge graph navigável&amp;lt;/strong&amp;gt;?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os artigos são nós. As tags são comunidades. Os xref são as arestas. O leitor não lê mais um artigo isolado — ele navega em um &amp;lt;strong&amp;gt;grafo de conhecimento&amp;lt;/strong&amp;gt; do qual o artigo corrente é o ponto de entrada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Imagine uma página inicial que exibe não a lista cronológica dos últimos posts, mas um &amp;lt;strong&amp;gt;mapa do corpus&amp;lt;/strong&amp;gt; : clusters ontológicos, artigos pivô (aqueles com o maior número de arestas), caminhos de leitura recomendados (« Se você gostou do artigo sobre Graphify, leia em seguida aquele sobre o Knowledge Graph como ferramenta de compreensão «)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não é mais um blog. É um &amp;lt;strong&amp;gt;atlas semântico&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E isso não é ficção científica. O`graph.json`já existe. Só lhe falta a interface de navegação.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_o_sistema_de_arquivos_sabe_o_que_o_modelo_ignora&amp;quot;&amp;gt;Conclusão: O Sistema de Arquivos Sabe o Que o Modelo Ignora&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O que eu projetei na terça-feira à noite é a substituição de uma heurística preguiçosa (a ordem cronológica) por uma representação fiel da estrutura do meu corpus (o Knowledge Graph)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O modelo`post.thyme`não muda. O que muda, é o que a gente Antes: uma lista Java ordenada por`date`. Depois : um grafo ponderado proveniente de três camadas de análise — xrefs, co-ocorrências de tags, e ontologia emergente por NLP.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O laço está fechado. graphify escaneia o workspace. Bakery lê o gráfico e alimenta o modelo. O leitor vê recomendações. pertinentes. E o motor — o maestro — nem sabe que Tudo isso existe.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É isso, a boa arquitetura. Cada plugin faz uma coisa. E o terminal do consumidor não precisa saber como.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_referências&amp;quot;&amp;gt;Referências&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0105_integrer_graphify_workflow_gradle_post.html&amp;quot;&amp;gt;Artigo 0105 — Integrar o Graphify em um workflow Gradle&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0106_knowledge_graph_outil_comprehension_codebase_post.html&amp;quot;&amp;gt;Artigo 0106 — O Knowledge Graph como ferramenta de compreensão&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0117_granularisation_oss_css_repositorie_workspace_post.html&amp;quot;&amp;gt;Artigo 0117 — A Granularização OSS/CSS&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0121_plugin_gradle_piloter_deux_instances_ollama_pro_post.html&amp;quot;&amp;gt;Artigo 0121 — Plugin Gradle para Pilotar Duas Instâncias Ollama Pro&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0122_ratio_efficacite_27x_45x_flotte_experts_ia_vs_claude_gpt_post.html&amp;quot;&amp;gt;Artigo 0122 — 27× a 45× mais eficaz : frota de especialistas em IA&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>27× a 45× mais eficaz que Claude e GPT: como uma frota de 23 especialistas de IA ajustados finamente esmaga o mercado</title>
            <link >https://cheroliv.com//blog/2026/0122_ratio_efficacite_27x_45x_flotte_experts_ia_vs_claude_gpt_post.html</link>
            <pubDate>Mon, 11 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0122_ratio_efficacite_27x_45x_flotte_experts_ia_vs_claude_gpt_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_por_que_fine_tune_e_não_rag&amp;quot;&amp;gt;O problema: por que fine-tune e não RAG?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_ideia_uma_frota_de_especialistas_não_um_único_modelo&amp;quot;&amp;gt;A ideia: uma frota de especialistas, não um único modelo.&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_código_base_sob_a_lupa_7_900_arquivos_144_000_linhas&amp;quot;&amp;gt;O código base sob a lupa : 7 900 arquivos, 144 000 linhas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura: 22 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Quando se usa um LLM para desenvolvimento de software no dia a dia, acabamos sempre batendo na mesma parede: o modelo não conhece &amp;lt;strong&amp;gt;seu&amp;lt;/strong&amp;gt; código‑base. Ele não sabe que você usa esse padrão dehttps://fr.wikipedia.org/wiki/Graphe_orient%C3%A9_acyclique[DAG], tal convenção Gradle, tal estratificação epistêmica em seushttps://fr.wikipedia.org/wiki/AsciiDoc[.adoc]. Então se injeta contexto nele, repete-se, corrige-se, itera-se. E o tempo — e o dinheiro — passam.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Portanto, eu coloquei a pergunta brutalmente: quanto custaria uma equipe de modelos fine-tunados no meu workspace, capazes de cuspir código &amp;lt;strong&amp;gt;no meu estilo&amp;lt;/strong&amp;gt; sem precisar explicar a cada vez? E, principalmente: como essa abordagem se compara às soluções proprietárias clássicas — Claude, GPT, Gemini, Grok?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A resposta me surpreendeu. Muito menos do que eu imaginava. E o relação custo/eficácia frente aos gigantes do setor é indecente. Eis o detalhe completo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_problema_por_que_fine_tune_e_não_rag&amp;quot;&amp;gt;O problema: por que fine-tune e não RAG?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O RAG, é bom. &amp;lt;a href=&amp;quot;0106_knowledge_graph_outil_comprehension_codebase_post.html&amp;quot;&amp;gt;Meu knowledge graph&amp;lt;/a&amp;gt; e &amp;lt;a href=&amp;quot;0116_compartimentage_epistemique_automatise_llm_post.html&amp;quot;&amp;gt;meu compartimentamento epistêmico&amp;lt;/a&amp;gt; funcionam. Mas o RAG tem um defeito fundamental: para cada consulta, você precisa reinserir o contexto relevante. Seu prompt fica mais longo, sua latência explode, e o modelo nunca &amp;lt;strong&amp;gt;intériorise&amp;lt;/strong&amp;gt; teus padrões — ele os lê a cada vez como um documento externo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O fine-tune, ele, é a memória muscular. O modelo &amp;lt;strong&amp;gt;sabe&amp;lt;/strong&amp;gt; que tu escreves`build.gradle.kts`com tarefas registradas via`tasks.register`, que teus`AGENTS.adoc`que seguem uma sintaxe padronizada, que o teu workspace está estruturado em boroughs. Ele não precisa que o lembrrem.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;ℹ️&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O ajuste fino não é um substituto do RAG. É uma camada complementar. O RAG permanece relevante para os dados que mudam frequentemente (logs, tickets, commits recentes). O ajuste fino é para o conhecimento &amp;lt;strong&amp;gt;estrutural&amp;lt;/strong&amp;gt; e &amp;lt;strong&amp;gt;estilístico&amp;lt;/strong&amp;gt; de longo prazo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_ideia_uma_frota_de_especialistas_não_um_único_modelo&amp;quot;&amp;gt;A ideia: uma frota de especialistas, não um único modelo.&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um único modelo fine-tunado em todo o meu workspace, já é bom. Mas isso é subótimo. Meu workspace cobre áreas muito diferentes:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Plugins do Gradle (Kotlin, plugins de convenção, builds compostos)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Motor DAG (Kotlin, planejamento, execução)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Agentes LLM (Python, LangChain4j, RAG)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Documentação arquitetônica (AsciiDoc, governança)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Infra/DevOps (Docker, GitHub Actions, CI/CD)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um modelo único deveria ser bom por toda parte, excelente em nenhum lugar. A solução? Uma&amp;lt;strong&amp;gt;frota de modelos especializados&amp;lt;/strong&amp;gt;, com um orquestrador que roteia cada consulta para o especialista certo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 8) ]

@startuml
skinparam backgroundColor #FAFAFA
skinparam defaultFontSize 13

package &amp;quot;Ollama Cloud&amp;quot; {
  rectangle &amp;quot;DeepSeek V4-Pro\n1.6T / 49B ativos&amp;quot; as V4Pro #FF6B6B
  rectangle &amp;quot;6× V4-Flash-FT
292B / 13B ativos&amp;quot; as Flash #4ECDC4
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FAFAFA
skinparam defaultFontSize 13

package &amp;quot;Ollama Cloud&amp;quot; {
  rectangle &amp;quot;DeepSeek V4-Pro\n1.6T / 49B ativos&amp;quot; as V4Pro #FF6B6B
  rectangle &amp;quot;6× V4-Flash-FT
292B / 13B ativos&amp;quot; as Flash #4ECDC4
  rectangle &amp;quot;6× Gemma4 26B-FT\n26B / 3.8B ativos&amp;quot; as Gemma #FFE66D
  rectangle &amp;quot;5× Qwen3.6 35B-FT
35B / 3B ativos&amp;quot; as Qwen36 #A8E6CF
  rectangle &amp;quot;5× Qwen3.5 35B-FT\n35B / 3B ativos&amp;quot; as Qwen35 #FF8B94
}

note bottom of Qwen35
  À partir de 17€/mois
  (1× Ollama Pro annual)
  Extensible par cumul
  de comptes Pro selon
  la méthode Docker.
end note

actor Utilisateur as User

User --&amp;amp;gt; V4Pro : Requête
V4Pro --&amp;amp;gt; Flash : &amp;quot;tarefa Gradle complexa&amp;quot;
V4Pro --&amp;amp;gt; Gemma : &amp;quot;formatação AsciiDoc&amp;quot;
V4Pro --&amp;amp;gt; Qwen36 : &amp;quot;refatoração Kotlin&amp;quot;
V4Pro --&amp;amp;gt; Qwen35 : &amp;quot;script Bash&amp;quot;

note right of V4Pro
  L&amp;#39;orkestrateur route
  la requête vers l&amp;#39;expert
  le plus pertinent.
  Zéro prompt engineering
  répétitif.
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A frota total:&amp;lt;strong&amp;gt;1 orquestrador + 22 especialistas = 23 modelos&amp;lt;/strong&amp;gt;. E o custo mensal de inferência?&amp;lt;strong&amp;gt;17€/mês&amp;lt;/strong&amp;gt;em assinatura Ollama Pro anual — extensível por acumulação de contas se as cotas forem excedidas, como demonstrado em &amp;lt;a href=&amp;quot;0120_cumuler_deux_abonnements_ollama_pro_docker_post.html&amp;quot;&amp;gt;meu método Docker multi-contas&amp;lt;/a&amp;gt;. Começamos em 17€, acrescentamos 20€ por patamar conforme a necessidade real. Sem desperdício.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_código_base_sob_a_lupa_7_900_arquivos_144_000_linhas&amp;quot;&amp;gt;O código base sob a lupa : 7 900 arquivos, 144 000 linhas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes de estimar o custo do training, é preciso saber o que treinar. Auditei meu workspace excluindo rigorosamente tudo o que é ignorado pelo Git :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;----
----
build/    out/    node_modules/    runtimes/    .gradle/    dist/
target/   venv/   __pycache__/     .cache/      .tox/
----

Segue o resultado para os arquivos rastreados apenas:

[cols=&amp;quot;2,2&amp;quot;]
|===
|Métrica |Valor

|Arquivos de origem rastreados |**7 896** |Linhas de código (LOC) |**143 805** |repositórios Git |**26** |Principais linguagens |Kotlin (1 903), AsciiDoc (2 765), Python (5 029)
|===

[IMPORTANT]
====
Estes 5 000+ arquivos Python incluem os`runtimes/`algunos notebooks e scripts utilitários. O núcleo aplicativo (plugins Gradle, motor DAG, agents) é principalmente em Kotlin e AsciiDoc.
====

A partir dessa massa, estimo poder gerar um dataset sintético de**~170 milhões de tokens**via um LLM instrutor(distilação da codebase em pares instrução/resposta).

=== Detalhe do conjunto de dados por tipo de especialista

[source,text]
----
Dataset maître (généraliste V4-Flash) :    50M tokens
Expert Gradle/Build        :    15M tokens
Expert Kotlin/DAG          :    15M tokens
Expert AsciiDoc/Gouvernance:    15M tokens
Expert Agent/LLM/Python    :    15M tokens
Expert Infra/CI-CD         :    15M tokens
Micro-experts Qwen/Gemma   :   ~5M tokens chacun
----

== A escolha dos modelos: 100% MoE, zero denso

É lá que a arquitetura se torna realmente eficiente.

Um modelo *dense* ativa todos os seus pesos a cada token. Um modelo *MoE* (Mixture of Experts) ativa apenas uma fração de seus parâmetros. O custo de inferência — e de fine-tuning — é proporcional aos parâmetros**ativados**, não aos parâmetros**estoqueados**.

Vejamos o que o mercado oferece em MoE hoje no Ollama Cloud:

[cols=&amp;quot;3,2,2,1,3&amp;quot;]
|===
|Modelo |Total parâmetros |Ativados |Ativação |Ollama Cloud

|DeepSeek V4-Pro |1,6t |49B |3.1% |✅ nativo |DeepSeek V4-Flash |292B |13B |4.5% |✅ nativo |Gemma 4 26B-A4B |26B |3.8B |14.6% |✅ nativo |Qwen3.6-35B-A3B |35B |3B |8.6% |�❌ (ainda não integrado) |Qwen3.5-35B-A3B |35B |3B |8.6% |�✅ nativo
|�❌ Qwen3.5-27B |27B |27B |100% denso |eliminado |�❌ Qwen3.5-4B/9B |4B/9B |4B/9B |100% denso |eliminado |❌ Gemma 4 31B |31B |31B |100% denso |eliminado
|🧪 Gemma 4 E2B/E4B |5B/8B |5B/8B |100% denso |pipeline de validação
|===

[CAUTION]
====
A relação de ativação é o critério discriminante. Um Gemma 4 26B-A4B ativa apenas 3,8B de seus 26B de parâmetros — é 7× menos que um denso 27B, para um desempenho de agente de codificação comparável (77,1% vs 80% no LiveCodeBench v6).
====

=== Por que estou abandonando os modelos densos

A exceção : os Gemma 4 E2B e E4B. Estes dois modelos, sob Apache 2.0, são perfeitos para testar e validar um pipeline de treinamento a menor custo. Uma execução de 2-3h em um E2B custa menos de $5 — se o conjunto de dados ou os hiperparâmetros forem problemáticos, descobrimos por alguns dólares. Uma vez que o pipeline esteja pronto, eles podem até ser usados para tarefas ultra-micro (reformatagem trivial, classificação binária). Mas para os especialistas da frota principal, eu continuo com o MoE. Eis por que :

.Mas não apenas: os Gemma 4 E2B/E4B, especialistas em anonimização fina

[NOTE]
====
Google projetou esses pequenos modelos como competitivos em tool calling nativo — eles sabem chamar funções, manipular regex, procurar padrões. Depois de fine-tunados em exemplos de anonimização (e‑mails, tokens, UUIDs, caminhos de arquivos), eles se tornam**anonimizadores de dataset**ultra-rapides. Nós damos-lhes um bloco de texto bruto, eles chamam`sed`/`regex`via uma chamada de ferramenta e retornam o bloco limpo. O custo de inferência é irrisório: 5B ativados, cabe em uma única GPU de entrada. Esses modelos não são especialistas de domínio — são**operários especializados**na cadeia de produção do conjunto de dados
====

* Sua VRAM de treinamento (mesmo em QLoRA) é proporcional aos**27B totais**
* O seu tempo de treinamento é proporcional aos**27B ativados**

Compare com um Qwen3.6-35B-A3B (MoE):

[cols=&amp;quot;3,2,2,2&amp;quot;]
|===
| |Qwen3.5-27B (dense) |Qwen3.6-35B-A3B (MoE) |Razão

|VRAM QLoRA |~55 GB |~35 GB |1.6× |GPU-horas (8M tok) |~12h |~4h |**3×** |Custo por fine-tune |~$24 |~$8 |**3 vezes** |SWE-bench Verificado |75.0% |73.4% |comparável
|===

O MoE custa 3× menos para treinar para desempenho de agente-codificação com precisão de 5%. Em 22 especialistas, a diferença corresponde a milhares de euros.

== Estimativa do treinamento: 811 H100-horas

Dividi o custo de treinamento modelo por modelo, em QLoRA 4-bit, com os datasets descritos acima.

=== Micro-especialistas MoE leves (3-3.8B ativados)

Estes modelos exigem 1 H100 (80 GB) por execução. A VRAM do modelo em Q4 é de aproximadamente 13-18 GB, deixando amplo espaço para os estados do otimizador LoRA e as ativações.

[cols=&amp;quot;4,2,3,2&amp;quot;]
|===
|Modelo |Número de runs |GPU-h/run |Total GPU-h

|Gemma 4 26B-A4B-FT |6 |3.5 h |**21h** |Qwen3.6-35B-A3B-FT |5 |4.0 h |**20h** |Qwen3.5-35B-A3B-FT |5 |4.0 h |**20 horas** |*subtotal micro* | | |*61 h*
|===

=== Executivos V4-Flash (13B ativados / 292B armazenados)

O DeepSeek V4-Flash é um caso particular. Mesmo que apenas 13B sejam ativados por token, o modelo completo em Q4 pesa ~142 GB — todos os especialistas devem estar na VRAM durante o treinamento. É preciso, portanto**3× H100**por run para ficar confortável.

[cols=&amp;quot;4,2,3,3,2&amp;quot;]
|===
|Modelo |conjunto de dados |GPUs |Horas |Total GPU-h

|V4-Flash-Base generalista |50M tok |3× H100 |50 h |**150 h** |V4-Flash especialista ×5 |15M tokens |3× H100 |40 h |**600 h** |*Subtotal executivos* | | | |*750 h*
|===

[cols=&amp;quot;3,2,1&amp;quot;]
|===
|**TOTAL** | |**811 H100-h**
|===

[NOTE]
====
Para comparação, um fine‑tune completo do V4‑Flash (sem QLoRA, todos os pesos) custaria ~4 000 H100‑h apenas para os 6 modelos. O QLoRA reduz o custo por um fator de 5. É a única maneira de tornar o projeto economicamente viável nessa escala.
====

=== Tarifas GPU: quem aluga o quê em maio de 2026

Eu fiz benchmark dos principais locatários de H100. O preço medido, para 811 horas.

[cols=&amp;quot;3,2,2,2&amp;quot;]
|===
|Locador |$/H100·h |Total 811h |Fiabilidade

|**Vast.ai spot** |$1.60 |**$1 298** |★★☆ interruptível |**Crusoe Cloud** |$2.00 |**$1 622** |★★★★ datacenter fiável |FluidStack |$1.98 |$1 606 |★★★ |TensorDock |$1.80 |$1 460 |★★☆ |RunPod |$2.29 |$1 857 |★★★ bom compromisso |CoreWeave |$2.25 |$1 825 |★★★★ empresa |Lambda Labs |$2.49 |$2 019 |premium
|===

[TIP]
====
Vast.ai spot é o mais barato mas teus runs podem ser interrompidos a qualquer momento. Para training, é arriscado. Crusoe a $2,00/h é a melhor relação confiabilidade/preço. O acréscimo de $324 vs Vast vale a tranquilidade de espírito.
====

=== Wall-clock: quanto tempo para treinar tudo?

Com apenas um nó 3× H100, as 31 execuções (1 generalista + 30 especialistas) levam aproximadamente**12 dias**em sequencial. Se você aluga 2 nós em paralelo, cai para**6 dias**.

Na prática, os micro-especialistas podem rodar em nós separados (1 H100 cada) enquanto os V4-Flash ocupam o nó 3× H100. Com um planejamento inteligente :

----

Nœud A (3× H100) : V4-Flash généraliste → V4-Flash experts ×5       (11 jours) Nœud B (1× H100) : Gemma 4 26B ×6 → Qwen 36 ×5 → Qwen 35 ×5        (6 jours)

7 dias de aluguel, tudo dobrado.

== A inferência: 23 modelos na Ollama Cloud a partir de 17€/mês

É a parte mais elegante da arquitetura.

Ollama Cloud permite fazer upload de modelos privados (funcionalidade do plano Pro). Meus 22 modelos fine-tuned são enviados para`.gguf`no meu espaço Ollama, marcados como privados, e acessíveis via a API padrão.

[plantuml, inference-flow, svg]
----
@startuml
skinparam backgroundColor #FAFAFA
skinparam defaultFontSize 12

|Utilisateur|
start
:Tâche de développement;

|Orkestrateur (V4-Pro)|
:Classifier la tâche\n(domaine + complexité);
if (Tâche simple,\nspécialisée ?) then (oui)
  :Router vers micro-expert\n(Gemma 4 26B-A4B-FT\n ou Qwen3.6-35B-A3B-FT);
elseif (Tâche complexe,\nconventions fortes ?) then (oui)
  :Router vers exécutif\n(V4-Flash-FT);
else (non)
  :Résoudre avec V4-Pro\n(architecture, décision);
endif

|Modèle ciblé|
:Génère le code/output\n**dans mon style**,\n90%+ first-shot correct;
:Retourne la réponse;

stop
@enduml
----

=== Começamos a 17€/mês, escalamos se necessário

O plano Pro Ollama (17€/mês anual) autoriza 3 modelos de nuvem concorrentes. Nesta arquitetura, nunca preciso de mais de 2-3 especialistas simultaneamente:

* A fase de classificação (V4-Pro) ocupa 1 slot, alguns segundos
* O especialista direcionado ocupa 1 slot, o tempo de gerar sua resposta
* O 3º slot permanece em reserva para as tarefas paralelas

Se o uso aumentar, eu adiciono contas Pro adicionais por meio do método Docker descrito em xref:0120_cumuler_deux_abonnements_ollama_pro_docker_post.adoc[meu artigo sobre o acúmulo de assinaturas]. Nenhum plano Max a 100€/mês — eu adiciono apenas o que realmente preciso.

O único fator limitante é o**cota de tempo de GPU**mensal do Ollama (50× o plano gratuito para Pro). Para meu uso de algumas centenas de consultas por dia, isso é mais do que suficiente.

[IMPORTANT]
====
O custo marginal de uma chamada ao V4-Pro (49B ativados) contra Gemma 4 26B-A4B (3.8B ativados) é o**mesmo para mim: zero**. O plano Ollama absorve a diferença de compute. O meu único indicador é a latência.
====

=== O roteamento na prática&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Volume de requêtes estimé : ~500/jour&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;├─ 50% → Gemma 4 26B-A4B-FT    (micro, 3.8B actifs)
│        Formatage AsciiDoc, scripts simples, conventions nommage
│
├─ 25% → V4-Flash-FT            (exécutif, 13B actifs)
│        Plugins Gradle, refactoring Kotlin, génération DAG
│
├─ 15% → Qwen 35B MoE-FT       (micro, 3B actifs)
│        Tâches intermédiaires, review de code, documentation
│
└─ 10% → V4-Pro                 (orkestrateur, 49B actifs)
         Décisions architecturales, refactoring transverse,
         problèmes vraiment nouveaux&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;A maior parte do tráfego passa pelos pequenos. O modelo grande intervém apenas como último recurso.

== A comparação que dói : Claude, GPT, Gemini, Grok vs minha frota

É aqui que a diferença se torna vertiginosa. Vamos ver o que custaria o mesmo volume de trabalho (500 requisições/dia, ~300 tokens de entrada + 2 000 tokens de saída em média) com as soluções proprietárias do mercado em maio de 2026.

=== Anthropic — Claude (tarifas maio 2026)

Anthropic recentemente *baixou* seus preços. A pressão das soluções open-weight como DeepSeek e Qwen está se fazendo sentir.

[cols=&amp;quot;3,2,2,2,2&amp;quot;]
|===
|Modelo |$/MTok |Saída $/MTok |Custo/dia |**Custo/mês**

|Opus 4.7 (o mais inteligente) |$5 |$25 |$25.75 |**$772** |Sonnet 4.6 (equilibrado) |$3 |$15 |$15.45 |**$463** |Haiku 4.5 (o + rápido) |$1 |$5 |$5.15 |**$154**
|===

[CAUTION]
====
Estas tarifas são *depois* da última redução de preço da Anthropic (maio de 2026). Antes disso, Opus estava a $15/$75 por MTok. O medo do open-weight é real.
====

Mesmo o modelo mais barato do Claude — Haiku 4.5 a**154€/mês**— custa 9× mais que minha assinatura Ollama Pro de 17€. E Haiku não conhece minha codebase, não gera no meu estilo e requer engenharia de prompt sistemática.

O mais inteligente — Opus 4.7 — estoura o orçamento em**772€/mês**um volume modesto de 500 requisições por dia. Seja**45 vezes mais caro**que a minha solução.

=== OpenAI — GPT (tarifas maio 2026)

[cols=&amp;quot;3,2,2,2,2&amp;quot;]
|===
|Modelo |$/MTok |Output $/MTok |Custo/dia |**Custo/mês**

|GPT 5.5 (o mais inteligente) |$5 |$30 |$30.90 |**$927** |GPT 5.4 (standard) |$2.50 |$15 |$15.45 |**$463** |GPT 5.4 Mini |$0.75 |$4.50 |$4.64 |**$139**
|===

Mesmo o modelo « econômico » da OpenAI — GPT 5.4 Mini a 139€/mês — custa 8× minha assinatura, para um modelo que não sabe *nada* das minhas convenções e requer um prompt engineering pesado.

GPT 5.5, seu flagship:**927€/mês**. Para *zero-shot* na minha base de código. Com 2-3 iterações por tarefa devido ao estilo não dominado, o custo mensal real dobra facilmente.

=== Google — Gemini (tarifas maio 2026)

[cols=&amp;quot;3,2,2,2,2&amp;quot;]
|===
|Modelo |$/MTok |Saída $/MTok |Custo/dia |**Custo/mês**

|Gemini 3.1 Pro (Alto) |$3.50 |$17.50 |$18.00 |**$540** |Gemini 3.1 Pro |$2 |$10 |$10.30 |**$309** |Gemini 3 Flash |$0.30 |$1.50 |$1.54 |**$46**
|===

Gemini 3 Flash a 46€/mês é competitivo em preço puro — mas sem fine-tune, ele gera código « genérico ». Não é código *Cheroliv*.

=== Grok — xAI (tarifas maio 2026)

[cols=&amp;quot;3,2,2,2,2&amp;quot;]
|===
|Modelo |$/MTok |Saída $/MTok |Custo/dia |**Custo/mês**

|Grok 4 |$4 |$16 |$16.48 |**$494** |Grok 4 Mini |$0.80 |$3.20 |$3.30 |**$99**
|===

=== Microsoft — Azure OpenAI (preços maio de 2026)

Azure aplica os mesmos preços que a API OpenAI direta, mas cobra adicionalmente a infraestrutura e a rede. É necessário considerar um acréscimo de 10-20% para o serviço gerenciado.

[cols=&amp;quot;3,2,2,2,2&amp;quot;]
|===
|Modelo |$/MTok |$/MTok |Custo/dia |**Custo/mês**

|GPT 5.4 (Azure, Global) |$2.50 |$15 |$15.45 |**$463** |GPT 5.4 + infraestrutura |acréscimo de 15% |acréscimo de 15% |$17.77 |**$533**
|===

[NOTE]
====
As tarifas acima referem-se à API, não aos planos « chat » (Claude Pro a $20/mês, ChatGPT Plus a $20/mês). Comparamos aqui o uso *desenvolvedor*, aquele em que você encadeia centenas de solicitações por dia via API para gerar, refatorar, documentar. Os planos para o público geral têm cotas que explodem muito antes de 500 solicitações/dia.
====

=== A tabela comparativa final

[cols=&amp;quot;3,2,2,2,2,2&amp;quot;]
|===
|Solução |Conhecimento codebase |Estilo integrado |Custo/mês |iterações |**Custo efetivo**

|**Minha frota (Ollama Pro)** |�✅ Afinada |✅ Nos pesos |**17€** |0-1 |**17€** |Claude Opus 4.7 |❌ Zero-shot |�❌ Prompt necessário |$772 |2-3 |$1 500-2 300 |Claude Haiku 4.5 |�❌ Zero-shot |�❌ Prompt necessário |$154 |3-4 |$460-615 |GPT 5.5 |❌ Zero-shot |❌ Prompt necessário |$927 |2-3 |$1 854-2 781 |GPT 5.4 |�❌ Zero-shot |�❌ Prompt necessário |$463 |2-3 |$925-1 390 |GPT 5.4 Mini |❌ Zero-shot |❌ Prompt necessário |$139 |3-5 |$417-695 |Gemini 3.1 Pro High |❌ Zero-shot |Prompt necessário |$540 |2-3 |$1 080-1 620 |Grok 4 |�❌ Zero-shot |�❌ Prompt necessário |$494 |2-3 |$988-1 482 |**Azure GPT 5.4** |�❌ Zero-shot |❌ Prompt necessário |$533 |2-3 |$1 066-1 600
|===

[plantuml, cost-comparison, svg]&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;@startuml
skinparam backgroundColor #FAFAFA&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;title Relação custo/eficácia — minha frota vs o mercado (maio 2026)
rectangle &amp;quot;17€/mês&amp;quot; as OURS #4ECDC4
rectangle &amp;quot;Opus 4.7
772€&amp;quot; as OPUS #FF6B6B
rectangle &amp;quot;Soneto 4.6
463€&amp;quot; as SONNET #FF8B94
rectangle &amp;quot;GPT 5.5
927€&amp;quot; as GPT #FFD93D
rectangle &amp;quot;Gemini 3.1 Pro
540€&amp;quot; as GEMINI #6C5CE7
rectangle &amp;quot;Grok 4
494€&amp;quot; as GROK #A8E6CF
rectangle &amp;quot;Haiku 4.5
154€&amp;quot; as HAIKU #DFE6E9
rectangle &amp;quot;GPT 5.4 Mini
139€&amp;quot; as MINI #B2BEC3&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;note bottom of OURS
  Avec fine-tune intégré.
  Zéro prompt engineering.
  90%+ first-shot correct.
end note&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;@enduml&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;17€/mês para 23 especialistas de IA afinados. O flagship Claude Opus 4.7 custa**45× mais caro**para uma qualidade inferior no *meu* codebase. GPT 5.5 custa**54× mais caro**. Mesmo o modelo mais barato do mercado (Gemini Flash por 46€) custa**2.7× mais**sem o conhecimento do meu workspace.

O veredicto é irrecorrível: o fine-tune em arquiteturas MoE, em 2026, não é apenas « mais barato ». É um fator de eficiência de**27× a 54×**sobre a relação custo/qualidade em face das soluções proprietárias. Anthropic, OpenAI, Google — todos sabem, e é por isso que eles estão baixando seus preços em pânico.

== O fator de eficiência: 5-8× vs usar V4-Pro sozinho

Comparemos os dois extremos.

=== Cenário A: V4-Pro apenas para tudo

[cols=&amp;quot;3,2&amp;quot;]
|===
|Métrica |Valor

|Conhecimento do codebase |0 (zero-shot) |Convenção de nomenclatura |40-60% de acerto na primeira tentativa |Prompt engineering necessário |Sim, sistemático |iterações por tarefa |2-3 |Custo inferência |$100/mês (Ollama Max) |Latência média |~20 tok/s + rede
|===

=== Cenário B : Frota MoE orquestrada

[cols=&amp;quot;3,2&amp;quot;]
|===
|Métrica |Valor

|Conhecimento da base de código |Integrada (fine-tune) |Convenção de nomeação |90-95% corretos de primeira |engenharia de prompts |Não (o estilo está nos pesos) |Iterações por tarefa |0-1 |Custo inferência |$60/mês (3× Ollama Pro) |Latência média |~50 tok/s (modelos leves)
|===

[cols=&amp;quot;4,2,2,2&amp;quot;]
|===
|Dimensão |V4-Pro apenas |Frota MoE |Fator

|Custo mensal da nuvem |$100 |**$60** |1.7× |Contexto a injetar |Elevado (prompt) |**Zero** |∞ |iterações médias |2-3 |**0-1** |2-3× |Latência percebida |20 tok/s |50 tok/s |**2.5×** |primeiro disparo correto |50% |90%+ |**1.8×**
|===

**Fator de eficiência global : 5-8×**— produtividade do desenvolvedor sentida.

[NOTE]
====
O fator não se mede em tokens ou em dólares. Ele se mede em « quanto tempo eu gasto para explicar ao modelo o que ele já deveria saber ». Com os modelos fine-tunados, esse tempo tende a zero.
====

== O orçamento completo: um investimento one-shot, um pacote ridículo

[cols=&amp;quot;3,2&amp;quot;]
|===
|posto |Montante

|Treinamento GPU (811 H100-h, Crusoe $2/h) |**$1 622** |Geração de conjunto de dados sintético (API LLM) |**$400** |Inference (Ollama Pro anual, $17/mês) |**$204** |**Total primeiro ano** |**$2 226** |**Equivalente mensal** |**$186** |**Custo por consulta estimado** |&amp;amp;lt; $0.01
|===

[plantuml, budget-breakdown, svg]&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;@startuml
skinparam backgroundColor #FAFAFA&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;rectangle &amp;quot;Orçamento Ano 1 : $2 226&amp;quot; as Total #E8E8E8 {
  rectangle &amp;quot;Treinamento GPU
$1 622 (73%)&amp;quot; as Train #FF6B6B
  rectangle &amp;quot;Conjunto de dados
destilação
$400 (18%)&amp;quot; as Data #FFE66D
  rectangle &amp;quot;Ollama Pro
$204 (9%)&amp;quot; as Cloud #4ECDC4
}&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;note bottom of Total
   $186/mois équivalent année 1.
   Dès l&amp;amp;#8217;an 2 : $17/mois.
   Amorti par ~$235 de CA
   mensuel avec marge 95%.
end note&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;@enduml&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;$186/mês o primeiro ano,**$17/mês**desde o ano 2. O preço de uma assinatura Netflix premium, para uma equipe de 23 especialistas em IA disponíveis 24/7, que escrevem código *no meu estilo* sem que eu precise explicar minhas convenções a eles.

=== O cálculo de rentabilidade

Com o edster.cloud e sua margem de ~95%, o ponto de equilíbrio está em**~$2 340 de CA adicional**no ano. Na prática :

* 1 missão de pilotagem de software → $3 000-5 000 → investimento coberto + lucro
* 1 formação corporativa → $5 000-10 000 → confortável
* Upsell implícito : &amp;quot;minha equipe de IA domina minha forja de software&amp;quot; → diferencial comercial

A partir do ano 2, o treinamento não precisa mais ser pago (exceto atualização do conjunto de dados). O custo anual cai para**$204**($17/mês). A relação custo/valor é a de um unicórnio: quase nula.

== Os limites do exercício

Isso continua uma *estimativa no dedo molhado*. Várias variáveis podem mudar o orçamento:

[cols=&amp;quot;2,3&amp;quot;]
|===
|Variável |Impacto

|Geração do dataset sintético |Se a API de distillation merda (alucinações, pares mal formados), é necessário tempo humano para curar. Custo oculto. |Cotas GPU-time Ollama |Se o uso ultrapassar as quotas do plano Pro, será necessário fazer upgrade ou mudar para uma GPU dedicada. |Qualidade efetiva dos fine-tunes |Um fine-tune mal-sucedido → a refazer. O verdadeiro risco são os hiperparâmetros, não o aluguel da GPU. |Evolução rápida dos modelos |Qwen3.6/Gemma 4 foram lançados há &amp;amp;lt; 1 mês. Daqui a 6 meses, a próxima geração será melhor E mais barata para fazer fine-tuning.
|===

[WARNING]
====
Não inicie 811 horas de treinamento sem primeiro testar o pipeline em *um único* micro-modelo. Uma execução de 3-4h em um Qwen 35B-A3B custa $8. Se o dataset estiver corrompido, você vai saber por $8, não por $1 622.
====

== Conclusão: o fine-tune tornou-se um luxo acessível

Há um ano, fazer o fine-tuning de um modelo de 292B parâmetros (mesmo em QLoRA) era uma questão de pesquisa. Hoje, em maio de 2026, é um projeto viável para um desenvolvedor independente:

* **811 H100-horas**de treinamento — $1 622 na Crusoe
* **23 modelos especializados**carregados na Ollama Cloud —**$17/mês**
* **0 servidor, 0 GPU local, 0 manutenção infra**

A democratização veio de três fatores convergentes:

1. **As arquiteturas MoE**que permitem ajustar eficientemente modelos dos quais apenas uma fração dos pesos é ativada
2. **Os planos fixos do Ollama Cloud**— que transformam um custo variável (por token) em custo fixo (assinatura)
3. **Modelos open-weight de qualidade**— DeepSeek V4, Qwen3.6, Gemma 4 estão sob licenças MIT/Apache 2.0

A civilização avança estendendo o número de operações importantes que podemos realizar sem pensar sobre elas.

O fine-tune, é exatamente isso: uma operação que delegas à máquina para não precisares mais pensar nisso. E em 2026, custa menos do que um desenvolvedor júnior mensalizado.

---

_Artigo publicado em {jbake-date}_

== Modelos fine-tuned da frota

[cols=&amp;quot;2,2,2,2,3&amp;quot;]
|===
|Tamanho ativado |número |Modelo base |Domínio |Conjunto de dados

|13B |1 |DeepSeek-V4-Flash-Base |Espaço de trabalho geral |50M tokens |13B |5 |DeepSeek-V4-Flash |Gradle, Kotlin, DAG, Doc, Infra |15 milhões de tokens |3.8B |6 |Gemma 4 26B-A4B |Micro-tarefas, formatação, scripts |5M tokens |3B |5 |Qwen3.6-35B-A3B |Refatoração, revisão, doc intermediária |5M tokens |3B |5 |Qwen3.5-35B-A3B |Tarefas legadas, compatibilidade |5 milhões de tokens
|===

== Recursos

=== Modelos open-weight no HuggingFace

* https://huggingface.co/deepseek-ai/DeepSeek-V4-Flash-Base[DeepSeek V4-Flash-Base — 292B parâmetros, licença MIT]
* https://huggingface.co/Qwen/Qwen3.6-35B-A3B[Qwen3.6-35B-A3B — 35B/3B ativados, licença Apache 2.0]
* https://huggingface.co/Qwen/Qwen3.5-35B-A3B[Qwen3.5-35B-A3B — 35B/3B ativados, licença Apache 2.0]
* https://huggingface.co/google/gemma-4-26B-A4B-it[Gemma 4 26B-A4B — 26B/3.8B ativados, licença Apache 2.0]

=== Fontes das tarifas (maio de 2026)

* https://ollama.com/pricing[Preços Ollama Cloud — plano Pro $17/mês anual]
* https://www.anthropic.com/pricing[Tarifas Anthropic — Claude API (Opus 4.7 $5/$25, Sonnet 4.6 $3/$15, Haiku 4.5 $1/$5)]
* https://openai.com/api/pricing/[Tarifas OpenAI — GPT API (GPT 5.5 $5/$30, GPT 5.4 $2.50/$15)]
* https://deepinfra.com/pricing[Tarifas DeepInfra — instâncias GPU H100 $1.79/h, B200 $2.79/h]
* Vast.ai, Crusoe Cloud, FluidStack, TensorDock, RunPod, CoreWeave, Lambda Labs — levantamentos de preços manuais, datados de maio de 2026

=== Anúncios oficiais

* https://qwen.ai/blog?id=qwen3.6-35b-a3b[Anúncio oficial Qwen3.6-35B-A3B — blog Qwen]— agentic coding, Apache 2.0, 262K context&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Plugin Gradle Caseiro para Controlar Duas Instâncias Ollama Pro em Paralelo</title>
            <link >https://cheroliv.com//blog/2026/0121_plugin_gradle_piloter_deux_instances_ollama_pro_post.html</link>
            <pubDate>Fri, 8 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0121_plugin_gradle_piloter_deux_instances_ollama_pro_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_gradle_para_da_infra&amp;quot;&amp;gt;Por que Gradle para da Infra ?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_build_gradle_kts_completo&amp;quot;&amp;gt;O build.gradle.kts completo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_anatomia_das_tarefas_chave&amp;quot;&amp;gt;Anatomia das Tarefas Chave&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_startollamab_o_lançador&amp;quot;&amp;gt;startOllamaB` — O Lançador&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_signinb_o_interativo&amp;quot;&amp;gt;signInB` — O Interativo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_pullmodelb_o_configurável&amp;quot;&amp;gt;pullModelB` — O configurável&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_statusollamaall_o_diagnóstico&amp;quot;&amp;gt;statusOllamaAll` — O Diagnóstico&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_cleanollamab_a_limpeza_que_preserva_a_identidade&amp;quot;&amp;gt;cleanOllamaB` — A limpeza que preserva a identidade&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_workflow_completo_de_zero_a_duas_sessões&amp;quot;&amp;gt;Workflow Completo — De Zero a Duas Sessões&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_não_transformá_lo_em_um_plugin_gradle_real&amp;quot;&amp;gt;Por que não transformá-lo em um plugin Gradle real?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_lições_aprendidas&amp;quot;&amp;gt;Lições Aprendidas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_recursos&amp;quot;&amp;gt;Recursos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 18 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Por que digitar comandos Docker manualmente quando podemos encapsular tudo em tarefas Gradle? Aqui está como eu industrializei minha configuração dual-Ollama Pro com zero script shell externo. Inicialização, parada, identidade, sign-in, pull — tudo passa por`./gradlew`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No artigo anterior, eu mostrei como fazer com que duas contas Ollama Pro coexistam na mesma máquina isolando a segunda instância em um contêiner Docker. A configuração funciona, mas a gestão diária torna-se rapidamente cansativa: lembrar o nome do contêiner, executar novamente os comandos corretos`docker exec`, verificar qual porta está ativa&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você conhece esse momento em que você tem 14 terminais abertos, que você digita`docker exec ollama-instance-b ollama list`para a quarta vez, e você pensa « deve haver um jeito mais limpo »?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este método é o Gradle.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não apenas para construir código. Gradle como um maestro da infraestrutura local. Tarefas que provisionam, verificam, testam e limpam. Tudo versionado em um`build.gradle.kts`que vive ao lado do`docker-compose.yml`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/architecture-gradle-ollama.svg&amp;quot; alt=&amp;quot;architecture gradle ollama&amp;quot; width=&amp;quot;729&amp;quot; height=&amp;quot;516&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_gradle_para_da_infra&amp;quot;&amp;gt;Por que Gradle para da Infra ?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A pergunta é legítima. Gradle, é uma ferramenta de build. Normalmente, ele compila, ele testa, ele empacota. Ele não lança contêineres Docker.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exceto que o Gradle também é um motor de grafo de dependências — e é aí que isso se torna interessante para o nosso caso.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Queremos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Que o contêiner seja iniciado antes de um sign-in&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Que a identidade seja verificada antes de um pull de modelo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Que um teste de API falhe silenciosamente se o contêiner estiver fora do ar.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Do grafo de dependências puro. Gradle é feito para isso. O DSL Kotlin torna a escrita das tarefas fluida, e o`doLast`permite executar comandos do sistema como se fossem código de aplicação.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sobretudo: sem bashrc para manter, sem scripts que fiquem espalhados em`~/bin`, sem Makefile ao lado do docker-compose. Tudo vive em um único arquivo, no mesmo local que o código do projeto.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_build_gradle_kts_completo&amp;quot;&amp;gt;O build.gradle.kts completo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está o arquivo que acabei por escrever. Ele faz tudo, do provisionamento de volume até o curl de verificação, passando pelo sign-in interativo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;import java.io.ByteArrayOutputStream

plugins {
    base
}

// -------------------------------------------------------------------------
// Configuration
// -------------------------------------------------------------------------
val instanceName   = &amp;quot;ollama-instance-b&amp;quot;
val instancePort   = 11435
val instanceImage  = &amp;quot;ollama/ollama:0.20.2&amp;quot;
val dataDir        = file(&amp;quot;${System.getProperty(&amp;quot;user.home&amp;quot;)}/ollama-b-data&amp;quot;)
val nativePort     = 11434

// Modèles à gérer — cloud Pro + un modèle local gratuit pour les tests
val proModels      = listOf(&amp;quot;deepseek-v4-pro:cloud&amp;quot;, &amp;quot;gemma4:31b-cloud&amp;quot;)
val freeModels     = listOf(&amp;quot;qwen3:0.6b&amp;quot;)

// -------------------------------------------------------------------------
// Tâche préparatoire — création du volume si absent
// -------------------------------------------------------------------------
val prepareVolume by tasks.registering {
    group       = &amp;quot;ollama&amp;quot;
    description = &amp;quot;Crée le répertoire de volume pour l&amp;#39;instance B si absent&amp;quot;
    doLast {
        if (!dataDir.exists()) {
            dataDir.mkdirs()
            println(&amp;quot;Volume créé : ${dataDir.absolutePath}&amp;quot;)
        } else {
            println(&amp;quot;Volume existant : ${dataDir.absolutePath}&amp;quot;)
        }
    }
}

// -------------------------------------------------------------------------
// Démarrage du conteneur Docker
// -------------------------------------------------------------------------
val startOllamaB by tasks.registering(Exec::class) {
    group       = &amp;quot;ollama&amp;quot;
    description = &amp;quot;Lance le conteneur Docker pour l&amp;#39;instance Ollama B&amp;quot;
    dependsOn(prepareVolume)

    commandLine(
        &amp;quot;docker&amp;quot;, &amp;quot;run&amp;quot;, &amp;quot;-d&amp;quot;,
        &amp;quot;--name&amp;quot;, instanceName,
        &amp;quot;-p&amp;quot;, &amp;quot;$instancePort:11434&amp;quot;,
        &amp;quot;-v&amp;quot;, &amp;quot;${dataDir.absolutePath}:/root/.ollama&amp;quot;,
        &amp;quot;-e&amp;quot;, &amp;quot;OLLAMA_HOST=0.0.0.0&amp;quot;,
        &amp;quot;--restart&amp;quot;, &amp;quot;always&amp;quot;,
        instanceImage
    )
    isIgnoreExitValue = true

    doLast {
        val alreadyRunning = executionResult.get().exitValue != 0
        if (alreadyRunning) {
            logger.lifecycle(&amp;quot;Le conteneur &amp;#39;$instanceName&amp;#39; existe déjà — tentative de démarrage&amp;quot;)
        } else {
            logger.lifecycle(&amp;quot;Conteneur &amp;#39;$instanceName&amp;#39; lancé&amp;quot;)
        }
    }
}

// -------------------------------------------------------------------------
// Arrêt du conteneur
// -------------------------------------------------------------------------
val stopOllamaB by tasks.registering(Exec::class) {
    group       = &amp;quot;ollama&amp;quot;
    description = &amp;quot;Arrête le conteneur Docker de l&amp;#39;instance B&amp;quot;

    commandLine(&amp;quot;docker&amp;quot;, &amp;quot;stop&amp;quot;, instanceName)
    isIgnoreExitValue = true

    doLast {
        logger.lifecycle(&amp;quot;Conteneur &amp;#39;$instanceName&amp;#39; arrêté&amp;quot;)
    }
}

// -------------------------------------------------------------------------
// Affichage de la clé publique (Device Key)
// -------------------------------------------------------------------------
val checkIdentity by tasks.registering(Exec::class) {
    group       = &amp;quot;ollama&amp;quot;
    description = &amp;quot;Affiche la clé publique SSH de l&amp;#39;instance B&amp;quot;
    dependsOn(startOllamaB)

    commandLine(&amp;quot;docker&amp;quot;, &amp;quot;exec&amp;quot;, instanceName, &amp;quot;cat&amp;quot;, &amp;quot;/root/.ollama/id_ed25519.pub&amp;quot;)
    standardOutput = ByteArrayOutputStream()

    doLast {
        val pubKey = standardOutput.toString().trim()
        println(&amp;quot;=&amp;quot;.repeat(60))
        println(&amp;quot;Device Key (Instance B)&amp;quot;)
        println(&amp;quot;=&amp;quot;.repeat(60))
        println(pubKey)
        println(&amp;quot;=&amp;quot;.repeat(60))
        println(&amp;quot;→ Enregistre cette clé sur https://ollama.com/settings/keys&amp;quot;)
        println(&amp;quot;→ Puis exécute : ./gradlew signInB&amp;quot;)
        println(&amp;quot;=&amp;quot;.repeat(60))
    }
}

// -------------------------------------------------------------------------
// Sign-in interactif (Ouvrira le navigateur)
// -------------------------------------------------------------------------
val signInB by tasks.registering(Exec::class) {
    group       = &amp;quot;ollama&amp;quot;
    description = &amp;quot;Lance l&amp;#39;authentification interactive Ollama pour le compte B&amp;quot;
    dependsOn(startOllamaB)

    standardInput  = System.`in`
    standardOutput = System.out
    errorOutput    = System.err

    commandLine(&amp;quot;docker&amp;quot;, &amp;quot;exec&amp;quot;, &amp;quot;-it&amp;quot;, instanceName, &amp;quot;ollama&amp;quot;, &amp;quot;signin&amp;quot;)

    doFirst {
        logger.lifecycle(&amp;quot;Authentification pour le Compte Pro B...&amp;quot;)
        logger.lifecycle(&amp;quot;Ouvre ton navigateur et connecte-toi avec l&amp;#39;email du compte B&amp;quot;)
    }
}

// -------------------------------------------------------------------------
// Pull d&amp;#39;un modèle (paramétrable via propriété Gradle)
// -------------------------------------------------------------------------
val pullModelB by tasks.registering(Exec::class) {
    group       = &amp;quot;ollama&amp;quot;
    description = &amp;quot;Pull un modèle sur l&amp;#39;instance B. Usage : ./gradlew pullModelB -Pmodel=nom:tag&amp;quot;
    dependsOn(startOllamaB)

    val modelName = project.findProperty(&amp;quot;model&amp;quot;) as? String ?: &amp;quot;qwen3:0.6b&amp;quot;

    commandLine(&amp;quot;docker&amp;quot;, &amp;quot;exec&amp;quot;, instanceName, &amp;quot;ollama&amp;quot;, &amp;quot;pull&amp;quot;, modelName)

    doFirst {
        logger.lifecycle(&amp;quot;Pull du modèle &amp;#39;$modelName&amp;#39; sur l&amp;#39;instance B...&amp;quot;)
    }

    doLast {
        logger.lifecycle(&amp;quot;Modèle &amp;#39;$modelName&amp;#39; pullé avec succès&amp;quot;)
    }
}

// -------------------------------------------------------------------------
// Pull de tous les modèles Pro (batch)
// -------------------------------------------------------------------------
val pullAllProModels by tasks.registering {
    group       = &amp;quot;ollama&amp;quot;
    description = &amp;quot;Pull tous les modèles cloud Pro sur l&amp;#39;instance B&amp;quot;
    dependsOn(startOllamaB)

    doLast {
        proModels.forEach { model -&amp;amp;gt;
            logger.lifecycle(&amp;quot;→ Pull $model...&amp;quot;)
            val process = ProcessBuilder(&amp;quot;docker&amp;quot;, &amp;quot;exec&amp;quot;, instanceName, &amp;quot;ollama&amp;quot;, &amp;quot;pull&amp;quot;, model)
                .inheritIO()
                .start()
            process.waitFor()
        }
        logger.lifecycle(&amp;quot;Tous les modèles Pro pullés&amp;quot;)
    }
}

// -------------------------------------------------------------------------
// Pull de tous les modèles gratuits (batch / première install)
// -------------------------------------------------------------------------
val pullAllFreeModels by tasks.registering {
    group       = &amp;quot;ollama&amp;quot;
    description = &amp;quot;Pull tous les modèles locaux gratuits sur l&amp;#39;instance B&amp;quot;
    dependsOn(startOllamaB)

    doLast {
        freeModels.forEach { model -&amp;amp;gt;
            logger.lifecycle(&amp;quot;→ Pull $model...&amp;quot;)
            val process = ProcessBuilder(&amp;quot;docker&amp;quot;, &amp;quot;exec&amp;quot;, instanceName, &amp;quot;ollama&amp;quot;, &amp;quot;pull&amp;quot;, model)
                .inheritIO()
                .start()
            process.waitFor()
        }
        logger.lifecycle(&amp;quot;Tous les modèles gratuits pullés&amp;quot;)
    }
}

// -------------------------------------------------------------------------
// Liste des modèles disponibles sur l&amp;#39;instance B
// -------------------------------------------------------------------------
val listModelsB by tasks.registering(Exec::class) {
    group       = &amp;quot;ollama&amp;quot;
    description = &amp;quot;Liste les modèles disponibles sur l&amp;#39;instance B&amp;quot;
    dependsOn(startOllamaB)

    commandLine(&amp;quot;docker&amp;quot;, &amp;quot;exec&amp;quot;, instanceName, &amp;quot;ollama&amp;quot;, &amp;quot;list&amp;quot;)
}

// -------------------------------------------------------------------------
// Health check — test API sur les deux instances
// -------------------------------------------------------------------------
val testOllamaB by tasks.registering {
    group       = &amp;quot;ollama&amp;quot;
    description = &amp;quot;Teste la connectivité API de l&amp;#39;instance B avec un simple chat&amp;quot;
    dependsOn(startOllamaB)

    doLast {
        val model = project.findProperty(&amp;quot;model&amp;quot;) as? String ?: &amp;quot;qwen3:0.6b&amp;quot;
        logger.lifecycle(&amp;quot;Test de l&amp;#39;instance B avec le modèle &amp;#39;$model&amp;#39;...&amp;quot;)

        val payload = &amp;quot;&amp;quot;&amp;quot;
            {
              &amp;quot;model&amp;quot;: &amp;quot;$model&amp;quot;,
              &amp;quot;messages&amp;quot;: [{&amp;quot;role&amp;quot;: &amp;quot;user&amp;quot;, &amp;quot;content&amp;quot;: &amp;quot;Réponds uniquement par OK&amp;quot;}],
              &amp;quot;max_tokens&amp;quot;: 5
            }
        &amp;quot;&amp;quot;&amp;quot;.trimIndent()

        val process = ProcessBuilder(
            &amp;quot;curl&amp;quot;, &amp;quot;-s&amp;quot;, &amp;quot;http://localhost:$instancePort/v1/chat/completions&amp;quot;,
            &amp;quot;-H&amp;quot;, &amp;quot;Content-Type: application/json&amp;quot;,
            &amp;quot;-d&amp;quot;, payload
        ).start()

        val response = process.inputStream.bufferedReader().readText()
        process.waitFor()

        if (response.contains(&amp;quot;\&amp;quot;id\&amp;quot;:\&amp;quot;chatcmpl&amp;quot;) || response.contains(&amp;quot;\&amp;quot;choices\&amp;quot;&amp;quot;)) {
            println(&amp;quot;✓ Instance B OK — port $instancePort répond&amp;quot;)
            println(&amp;quot;  Response: ${response.take(120)}...&amp;quot;)
        } else {
            println(&amp;quot;✗ Instance B INACTIVE — vérifie le conteneur avec &amp;#39;./gradlew statusOllamaAll&amp;#39;&amp;quot;)
            println(&amp;quot;  Raw: $response&amp;quot;)
        }
    }
}

// -------------------------------------------------------------------------
// Health check sur les deux instances simultanément
// -------------------------------------------------------------------------
val statusOllamaAll by tasks.registering {
    group       = &amp;quot;ollama&amp;quot;
    description = &amp;quot;Vérifie le statut des deux instances Ollama (native + Docker)&amp;quot;

    doLast {
        // Instance native
        runCatching {
            val process = ProcessBuilder(
                &amp;quot;curl&amp;quot;, &amp;quot;-s&amp;quot;, &amp;quot;-o&amp;quot;, &amp;quot;/dev/null&amp;quot;, &amp;quot;-w&amp;quot;, &amp;quot;%{http_code}&amp;quot;,
                &amp;quot;http://localhost:$nativePort/api/tags&amp;quot;
            ).start()
            val code = process.inputStream.bufferedReader().readText().trim()
            process.waitFor()
            println(if (code == &amp;quot;200&amp;quot;) &amp;quot;✓ Instance Native   — port $nativePort OK (HTTP $code)&amp;quot;
                    else &amp;quot;✗ Instance Native — port $nativePort (HTTP $code)&amp;quot;)
        }.getOrElse {
            println(&amp;quot;✗ Instance Native — injoignable sur le port $nativePort&amp;quot;)
        }

        // Instance Docker
        runCatching {
            val process = ProcessBuilder(
                &amp;quot;curl&amp;quot;, &amp;quot;-s&amp;quot;, &amp;quot;-o&amp;quot;, &amp;quot;/dev/null&amp;quot;, &amp;quot;-w&amp;quot;, &amp;quot;%{http_code}&amp;quot;,
                &amp;quot;http://localhost:$instancePort/api/tags&amp;quot;
            ).start()
            val code = process.inputStream.bufferedReader().readText().trim()
            process.waitFor()
            println(if (code == &amp;quot;200&amp;quot;) &amp;quot;✓ Instance Docker B — port $instancePort OK (HTTP $code)&amp;quot;
                    else &amp;quot;✗ Instance Docker B — port $instancePort (HTTP $code)&amp;quot;)
        }.getOrElse {
            println(&amp;quot;✗ Instance Docker B — injoignable sur le port $instancePort&amp;quot;)
        }

        // Info conteneur
        runCatching {
            val process = ProcessBuilder(&amp;quot;docker&amp;quot;, &amp;quot;ps&amp;quot;, &amp;quot;--filter&amp;quot;, &amp;quot;name=$instanceName&amp;quot;,
                &amp;quot;--format&amp;quot;, &amp;quot;table {{.Names}}\\t{{.Status}}\\t{{.Ports}}&amp;quot;).start()
            val status = process.inputStream.bufferedReader().readText()
            process.waitFor()
            println()
            println(&amp;quot;Conteneur Docker :&amp;quot;)
            println(status)
        }
    }
}

// -------------------------------------------------------------------------
// Nettoyage complet
// -------------------------------------------------------------------------
val cleanOllamaB by tasks.registering(Exec::class) {
    group       = &amp;quot;ollama&amp;quot;
    description = &amp;quot;Arrête et supprime le conteneur Docker de l&amp;#39;instance B&amp;quot;
    dependsOn(stopOllamaB)

    commandLine(&amp;quot;docker&amp;quot;, &amp;quot;rm&amp;quot;, instanceName)
    isIgnoreExitValue = true

    doLast {
        logger.lifecycle(&amp;quot;Conteneur &amp;#39;$instanceName&amp;#39; supprimé&amp;quot;)
        logger.lifecycle(&amp;quot;Le volume ${dataDir.absolutePath} est conservé (identité persistante)&amp;quot;)
    }
}

// -------------------------------------------------------------------------
// Affichage des instructions
// -------------------------------------------------------------------------
val helpOllama by tasks.registering {
    group       = &amp;quot;ollama&amp;quot;
    description = &amp;quot;Affiche l&amp;#39;aide des tâches Ollama disponibles&amp;quot;

    doLast {
        println(&amp;quot;&amp;quot;&amp;quot;
        ╔══════════════════════════════════════════════════════════════╗
        ║         Tâches Gradle — Pilotage Dual Ollama Pro            ║
        ╠══════════════════════════════════════════════════════════════╣
        ║  ./gradlew startOllamaB      Démarrer l&amp;#39;instance Docker B   ║
        ║  ./gradlew stopOllamaB       Arrêter l&amp;#39;instance Docker B    ║
        ║  ./gradlew checkIdentity     Afficher la clé SSH publique   ║
        ║  ./gradlew signInB           Sign-in interactif compte B    ║
        ║  ./gradlew pullModelB        Pull un modèle (-Pmodel=...)   ║
        ║  ./gradlew pullAllProModels  Pull tous les modèles Pro      ║
        ║  ./gradlew pullAllFreeModels Pull tous les modèles gratuits ║
        ║  ./gradlew listModelsB       Lister les modèles instance B  ║
        ║  ./gradlew testOllamaB       Test API avec un chat simple   ║
        ║  ./gradlew statusOllamaAll   Santé des 2 instances          ║
        ║  ./gradlew cleanOllamaB      Supprimer le conteneur         ║
        ║  ./gradlew helpOllama        Cette aide                     ║
        ╚══════════════════════════════════════════════════════════════╝
        &amp;quot;&amp;quot;&amp;quot;.trimIndent())
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está. Doze tarefas, um único ponto de entrada e zero atrito.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_anatomia_das_tarefas_chave&amp;quot;&amp;gt;Anatomia das Tarefas Chave&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_startollamab_o_lançador&amp;quot;&amp;gt;startOllamaB` — O Lançador&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nada de especial: um`docker run -d`com os parâmetros corretos. O truque está em`isIgnoreExitValue = true`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;isIgnoreExitValue = true&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se o contêiner já existir, o comando`docker run`falha (código de saída ≠ 0). Sem essa propriedade, o Gradle interromperia todo o build com erro. Aqui, a falha é silenciosa — e o`doLast`detecta se era um &amp;quot;já existente&amp;quot; ou um erro real.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le `doLast`é crucial : ele é executado &amp;lt;strong&amp;gt;após&amp;lt;/strong&amp;gt; a execução do comando, o que permite inspecionar`executionResult.get().exitValue`e de registrar uma mensagem apropriada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_signinb_o_interativo&amp;quot;&amp;gt;signInB` — O Interativo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É a única tarefa que requer interação humana. Gradle transmite`System.in`ao process para que o prompt`ollama signin`possa aparecer normalmente :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;standardInput  = System.`in`
standardOutput = System.out
errorOutput    = System.err&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sem isso, o sign-in bloquearia porque o stdin estaria fechado e você nunca veria o prompt.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_pullmodelb_o_configurável&amp;quot;&amp;gt;pullModelB` — O configurável&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Uma tarefa genérica que aceita um parâmetro via`-P`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./gradlew pullModelB -Pmodel=deepseek-v4-pro:cloud
./gradlew pullModelB                           # fallback : qwen3:0.6b&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O fallback é importante:`project.findProperty(&amp;quot;model&amp;quot;) as? String ?: &amp;quot;qwen3:0.6b&amp;quot;`garante que a tarefa não quebre se esquecermos o parâmetro.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_statusollamaall_o_diagnóstico&amp;quot;&amp;gt;statusOllamaAll` — O Diagnóstico&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Dois`curl`em saúde nos portos`11434` et &amp;lt;code&amp;gt;11435&amp;lt;/code&amp;gt;, com um fallback em`runCatching`para não travar caso uma das instâncias esteja down. A saída é estruturada:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;✓ Instance Native   — port 11434 OK (HTTP 200)
✓ Instance Docker B — port 11435 OK (HTTP 200)

Conteneur Docker :
NAMES               STATUS         PORTS
ollama-instance-b   Up 3 hours     0.0.0.0:11435-&amp;amp;gt;11434/tcp&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um olhar, e você sabe exatamente o que está acontecendo e o que está errado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_cleanollamab_a_limpeza_que_preserva_a_identidade&amp;quot;&amp;gt;cleanOllamaB` — A limpeza que preserva a identidade&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;commandLine(&amp;quot;docker&amp;quot;, &amp;quot;rm&amp;quot;, instanceName)
isIgnoreExitValue = true&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O contêiner foi excluído, mas o volume`~/ollama-b-data`é&amp;lt;strong&amp;gt;conservado&amp;lt;/strong&amp;gt;. A identidade SSH sobrevive à limpeza. Ao próximo`startOllamaB`, as mesmas chaves serão reutilizadas — não é preciso refazer o sign-in nem re-registrar a Device Key.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock caution&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-caution&amp;quot; title=&amp;quot;🔒&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;cleanOllamaB`não toque no volume. Se quiser apagar a identidade B (e recomeçar tudo do zero), é preciso excluir&amp;lt;/code&amp;gt;~/ollama-b-data`manualmente. É voluntário — um`clean`não deve destruir as credenciais.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_workflow_completo_de_zero_a_duas_sessões&amp;quot;&amp;gt;Workflow Completo — De Zero a Duas Sessões&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está a sequência ideal para alguém que chega ao projeto e quer colocar tudo em funcionamento sem abrir nenhuma documentação:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;# 1. Premier contact — que faire ?
$ ./gradlew helpOllama

# 2. On démarre l&amp;#39;instance B
$ ./gradlew startOllamaB

# 3. On récupère la Device Key et on l&amp;#39;enregistre sur ollama.com
$ ./gradlew checkIdentity
# → copier-coller de la clé sur https://ollama.com/settings/keys

# 4. Authentification interactive
$ ./gradlew signInB

# 5. Pull d&amp;#39;un petit modèle gratuit pour tester la plomberie
$ ./gradlew pullModelB

# 6. Test API
$ ./gradlew testOllamaB

# 7. Si tout est vert, pull des modèles Pro
$ ./gradlew pullAllProModels

# 8. Vérification finale des deux instances
$ ./gradlew statusOllamaAll&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Menos de dois minutos, tudo está provisionado. Não há README para ler, não há variáveis de ambiente a definir. A tarefa`helpOllama`funciona como documentação integrada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml




...
... ( skipping 126 lines )
...
skinparam StereotypeE {
  BackgroundColor white
  BorderColor black
}
skinparam StereotypeI {
  BackgroundColor white
  BorderColor black
}
skinparam StereotypeN {
  BackgroundColor white
  BorderColor black
}
skinparam UseCaseStereoType {
  FontColor black
  FontName Verdana
}
skinparam DefaultFontSize 11

actor Développeur as Dev
participant &amp;quot;Gradle
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
!theme plain
skinparam DefaultFontSize 11

actor Développeur as Dev
participant &amp;quot;Gradle
(build.gradle.kts)&amp;quot; as Gradle
participant &amp;quot;Docker\nDaemon&amp;quot; as Docker
participant &amp;quot;Ollama
Instância B&amp;quot; as OllamaB
participant &amp;quot;Ollama\nNuvem&amp;quot; as Cloud

== Provisionnement ==
Dev -&amp;amp;gt; Gradle : helpOllama
Gradle --&amp;amp;gt; Dev : Aide et instructions

Dev -&amp;amp;gt; Gradle : startOllamaB
Gradle -&amp;amp;gt; Docker : docker run -d ollama/ollama:0.20.2
Docker -&amp;amp;gt; OllamaB : Génération clés SSH (id_ed25519)
Docker --&amp;amp;gt; Gradle : conteneur lancé
Gradle --&amp;amp;gt; Dev : OK

Dev -&amp;amp;gt; Gradle : checkIdentity
Gradle -&amp;amp;gt; Docker : cat id_ed25519.pub
Gradle --&amp;amp;gt; Dev : Clé publique SSH

Dev -&amp;amp;gt; Cloud : Enregistrement clé SSH\n(sur ollama.com/settings/keys)

== Authentification ==
Dev -&amp;amp;gt; Gradle : signInB
Gradle -&amp;amp;gt; Docker : ollama signin (interactif)
Dev &amp;amp;lt;-&amp;amp;gt; Cloud : Login navigateur (compte B)
Docker -&amp;amp;gt; Cloud : Échange token ⇔ Device Key
Gradle --&amp;amp;gt; Dev : Authentifié

== Vérification ==
Dev -&amp;amp;gt; Gradle : pullModelB
Gradle -&amp;amp;gt; Docker : ollama pull qwen3:0.6b
Gradle --&amp;amp;gt; Dev : Modèle pullé

Dev -&amp;amp;gt; Gradle : testOllamaB
Gradle -&amp;amp;gt; Docker : curl /v1/chat/completions
Gradle --&amp;amp;gt; Dev : ✓ Instance B OK

== Production ==
Dev -&amp;amp;gt; Gradle : pullAllProModels
Gradle -&amp;amp;gt; Docker : ollama pull deepseek-v4-pro:cloud
Gradle -&amp;amp;gt; Docker : ollama pull gemma4:31b-cloud
Gradle --&amp;amp;gt; Dev : Modèles Pro pullés

Dev -&amp;amp;gt; Gradle : statusOllamaAll
Gradle --&amp;amp;gt; Dev : ✓ Native ✓ Docker B
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_não_transformá_lo_em_um_plugin_gradle_real&amp;quot;&amp;gt;Por que não transformá-lo em um plugin Gradle real?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Pergunta legítima. Extrair isso`build.gradle.kts`em um plugin Gradle autônomo teria vantagens: reutilização entre projetos, configuração declarativa, extensão DSL.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas por enquanto, não faz sentido. Este build não tem lógica de negócios complexa — é um wrapper em torno de comandos Docker e curl. A sobrecarga de um plugin Gradle completo (com extensão, testes unitários, publicação) seria desproporcional para 12 tarefas utilitárias.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le `build.gradle.kts`inline está no projeto que precisa dele. É trivial copiar em outro lugar. Se um dia eu tiver três projetos que dependem dele, eu o transformarei em um plugin. Por enquanto, KISS.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;ℹ️&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O dia em que tens três`build.gradle.kts`identiques em três projetos, isso é o sinal de que é necessário extrair um plugin. Antes disso, a duplicação é menos custosa que a abstração. Sabedoria de dev.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_lições_aprendidas&amp;quot;&amp;gt;Lições Aprendidas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Gradle não é apenas uma ferramenta de build&amp;lt;/strong&amp;gt;— O motor de grafo de dependências o torna um orquestrador de infraestrutura formidável.&amp;lt;code&amp;gt;doLast&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;isIgnoreExitValue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;dependsOn&amp;lt;/code&amp;gt;: estas três primitivas são suficientes para gerenciar um contêiner Docker com a mesma confiabilidade de um pipeline CI.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;isIgnoreExitValue` é teu amigo&amp;lt;/strong&amp;gt;— Num mundo ideal,&amp;lt;code&amp;gt;docker run`em um contêiner já existente retornaria um aviso em vez de um código de saída de erro. No mundo real, você coloca`isIgnoreExitValue = true`e você gerencia o diagnóstico em`doLast&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;As tarefas configuráveis matam a ambiguidade&amp;lt;/strong&amp;gt; — Un &amp;lt;code&amp;gt;pullModelB`genérico com&amp;lt;/code&amp;gt;-Pmodel=&amp;amp;#8230;&amp;amp;#8203;`substitui N tarefas especializadas. Uma única tarefa, uma única documentação, um único comportamento.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;runCatching` para as verificações de saúde&amp;lt;/strong&amp;gt;— Quando você testa N endpoints, a falha do primeiro não deve impedir de testar os outros.&amp;lt;code&amp;gt;runCatching&amp;lt;/code&amp;gt;+`getOrElse`por endpoint, e o diagnóstico permanece completo mesmo em caso de falha parcial.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A tarefa &amp;lt;code&amp;gt;helpOllama&amp;lt;/code&amp;gt; como documentação executável&amp;lt;/strong&amp;gt; — Un `println()`Bem formatado substitui um README de três parágrafos. É auto-documentado, sempre atualizado e acessível sem sair do terminal.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Passamos de comandos docker a digitar à mão para um workflow Gradle completo, versionado, reutilizável. Doze tarefas cobrem todo o ciclo de vida: provisionamento, identidade, sign-in, pull, teste, diagnóstico, limpeza.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O combo docker-compose + build.gradle.kts + opencode.json forma uma trindade limpa: Docker para isolamento, Gradle para orquestração, OpenCode para consumo. Cada camada tem sua responsabilidade, e nenhuma transborda sobre a outra.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Próxima etapa? Automatizar o ciclo de vida na CI para que as instâncias Ollama estejam disponíveis nos ambientes de desenvolvimento efêmeros. Mas isso é outro artigo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_recursos&amp;quot;&amp;gt;Recursos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.gradle.org/current/userguide/tutorial_using_tasks.html&amp;quot;&amp;gt;User Manual Gradle — Tarefas&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.gradle.org/current/userguide/custom_tasks.html&amp;quot;&amp;gt;Manual do Usuário Gradle — Tarefas Personalizadas&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://hub.docker.com/r/ollama/ollama&amp;quot;&amp;gt;Imagem Docker Ollama no Docker Hub&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://kotlinlang.org/docs/gradle.html&amp;quot;&amp;gt;Gradle DSL Kotlin — Documentação&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Artigo publicado em 2026-05-08&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Combinar duas assinaturas Ollama Pro na mesma máquina com Docker</title>
            <link >https://cheroliv.com//blog/2026/0120_cumuler_deux_abonnements_ollama_pro_docker_post.html</link>
            <pubDate>Fri, 8 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0120_cumuler_deux_abonnements_ollama_pro_docker_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_fazer_a_mesma_coisa_duas_vezes&amp;quot;&amp;gt;Por que fazer a mesma coisa duas vezes?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_coração_do_problema_a_device_key&amp;quot;&amp;gt;O Coração do Problema : A Device Key&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_colocação_passo_a_passo&amp;quot;&amp;gt;Colocação — Passo a Passo&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_pré_requisito&amp;quot;&amp;gt;Pré-requisito&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_1_criar_o_volume&amp;quot;&amp;gt;Etapa 1: Criar o Volume&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_2_iniciar_o_contêiner_docker&amp;quot;&amp;gt;Passo 2: Iniciar o contêiner Docker&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_3_recuperar_a_chave_pública&amp;quot;&amp;gt;Passo 3: Recuperar a Chave Pública&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_4_registrar_a_chave_no_ollama_com&amp;quot;&amp;gt;Passo 4: Registrar a Chave no Ollama.com&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_5_testar_com_um_modelo_pequeno_gratuito&amp;quot;&amp;gt;Passo 5: Testar com um modelo pequeno gratuito&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_armadilha_da_api_v1models&amp;quot;&amp;gt;A armadilha da API `/v1/models&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_resultado_duas_sessões_duas_contas_zero_conflito&amp;quot;&amp;gt;Resultado: Duas Sessões, Duas Contas, Zero Conflito&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_lições_aprendidas&amp;quot;&amp;gt;Lições Aprendidas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_para_ir_mais_longe&amp;quot;&amp;gt;Para Ir Mais Longe&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 14 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Duas assinaturas Pro, uma única máquina, um único cartão bancário, e nenhum conflito de rede. Como o Docker me permitiu dobrar minha capacidade de inferência na nuvem sem comprar um segundo PC.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Estou usando o Ollama Pro há vários meses para alimentar minhas sessões OpenCode com modelos de nuvem. O problema é que, mesmo com uma assinatura Pro, ficamos rapidamente limitados pelo &amp;lt;strong&amp;gt;rate limiting&amp;lt;/strong&amp;gt; quando iniciamos vários agentes em paralelo. A solução óbvia: uma segunda assinatura.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas aí, é o drama.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ollama identifica cada máquina por uma chave SSH única — a sua famosa &amp;lt;strong&amp;gt;Device Key&amp;lt;/strong&amp;gt;. Duas instâncias no mesmo SO compartilhariam a mesma identidade, e seria impossível vincular duas contas Pro ao mesmo device. E como você já deve imaginar, eu não ia comprar um segundo laptop só por isso.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A solução: trapacear. Fazer o Ollama acreditar que está sendo executado em duas máquinas diferentes, embora compartilhem o mesmo CPU, a mesma RAM e a mesma conexão de rede. Docker nos oferecerá uma bolha de isolamento perfeito.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 10) ]

@startuml




...
... ( skipping 130 lines )
...
skinparam StereotypeI {
  BackgroundColor white
  BorderColor black
}
skinparam StereotypeN {
  BackgroundColor white
  BorderColor black
}
skinparam UseCaseStereoType {
  FontColor black
  FontName Verdana
}
skinparam BoxPadding 10
skinparam DefaultFontSize 12

left to right direction

node &amp;quot;Máquina Física&amp;quot; {
  frame &amp;quot;Instância A do Ollama (Nativa)&amp;quot; as Native {
    database &amp;quot;Identity
^^^^^
 Syntax Error? (Assumed diagram type: class)

@startuml
!theme plain
skinparam BoxPadding 10
skinparam DefaultFontSize 12

left to right direction

node &amp;quot;Máquina Física&amp;quot; {
  frame &amp;quot;Instância A do Ollama (Nativa)&amp;quot; as Native {
    database &amp;quot;Identity
/usr/share/ollama/.ollama/id_ed25519&amp;quot; as KeyA
    portin &amp;quot;API
:11434&amp;quot; as PortA
  }

  frame &amp;quot;Ollama Instance B (Docker)&amp;quot; as Docker {
    database &amp;quot;Identidade
~/ollama-b-data/id_ed25519&amp;quot; as KeyB
    portin &amp;quot;API
:11435&amp;quot; as PortB
  }
}

cloud &amp;quot;Ollama Cloud&amp;quot; {
  actor &amp;quot;Conta Pro A\n(email 1)&amp;quot; as AccountA
  actor &amp;quot;Conta Pro B\n(e-mail 2)&amp;quot; as AccountB
}

KeyA --&amp;amp;gt; AccountA : &amp;quot;Chave SSH A&amp;quot;
KeyB --&amp;amp;gt; AccountB : &amp;quot;Chave SSH B&amp;quot;
PortA --&amp;amp;gt; AccountA : &amp;quot;Requisições A&amp;quot;
PortB --&amp;amp;gt; AccountB : &amp;quot;Consultas B&amp;quot;

note bottom of Docker
  Conteneur isolé : identité SSH distincte,
  port réseau distinct, volume persistant
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_fazer_a_mesma_coisa_duas_vezes&amp;quot;&amp;gt;Por que fazer a mesma coisa duas vezes?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes de me chamar de doente mental, deixe-me explicar o caso de uso para você.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com uma única assinatura Pro, posso lançar um modelo como`deepseek-v4-pro:cloud`dentro de uma sessão OpenCode. O problema surge quando eu quero &amp;lt;strong&amp;gt;duas&amp;lt;/strong&amp;gt; sessões simultâneas. As cotas de rate limiting fazem com que a segunda sessão trave ou seja pura e simplesmente rejeitada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com duas assinaturas Pro independentes:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Sessão OpenCode 1 → Conta Pro A`localhost:11434`)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Session OpenCode 2 → Conta Pro B`localhost:11435`)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cada sessão tem sua própria cota, seu próprio contexto, e não incomoda a outra. É multi-processamento humano.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;❗&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Duas assinaturas Pro = dois e‑mails diferentes. O mesmo cartão de crédito funciona — o sistema de pagamento da Ollama não bloqueia assinaturas múltiplas a partir do mesmo meio de pagamento. Verifiquei.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_coração_do_problema_a_device_key&amp;quot;&amp;gt;O Coração do Problema : A Device Key&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando você instala o Ollama, um par de chaves SSH ed25519 é gerado no diretório de identidade:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ cat /usr/share/ollama/.ollama/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta chave é feita upload para os servidores da Ollama durante o`ollama signin`. Ela é quem diz « esta máquina pertence a tal conta Pro ». Se suas duas instâncias compartilharem o mesmo arquivo`id_ed25519`, elas compartilham a mesma identidade. Fim do jogo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O expediente : dar à nossa segunda instância seu próprio diretório de identidade, isolado em um volume Docker.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_colocação_passo_a_passo&amp;quot;&amp;gt;Colocação — Passo a Passo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_pré_requisito&amp;quot;&amp;gt;Pré-requisito&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Uma instalação nativa do Ollama funcional (script oficial`curl | sh`)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Docker instalado e funcional&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Portainer ou docker-compose para a implantação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Duas contas Ollama com dois e-mails distintos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eu usei a versão`0.20.2`— a fornecida pelo script oficial e a imagem Docker correspondente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_etapa_1_criar_o_volume&amp;quot;&amp;gt;Etapa 1: Criar o Volume&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um diretório simples no host servirá como volume persistente para a identidade da instância B:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p ~/ollama-b-data&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta pasta será montada no contêiner Docker como`/root/.ollama`, onde o Ollama armazena suas chaves de identidade, seu histórico e seus modelos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_passo_2_iniciar_o_contêiner_docker&amp;quot;&amp;gt;Passo 2: Iniciar o contêiner Docker&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-yaml hljs&amp;quot; data-lang=&amp;quot;yaml&amp;quot;&amp;gt;# docker-compose.yml
services:
  ollama-instance-b:
    image: ollama/ollama:0.20.2
    container_name: ollama-instance-b
    ports:
      - &amp;quot;11435:11434&amp;quot;
    volumes:
      - /home/cheroliv/ollama-b-data:/root/.ollama
    environment:
      - OLLAMA_HOST=0.0.0.0
    restart: always&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O que se passa aqui:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O porto&amp;lt;strong&amp;gt;11435&amp;lt;/strong&amp;gt;do host é mapeado para o&amp;lt;strong&amp;gt;11434&amp;lt;/strong&amp;gt;interna do contêiner. Assim, a instância Docker ouve em`:11435`sem conflito com a instância nativa que ocupa`:11434`.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O volume`~/ollama-b-data`está montado sobre`/root/.ollama`— é aqui que a identidade SSH será armazenada.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;`OLLAMA_HOST=0.0.0.0`permite ao contêiner aceitar conexões externas.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eu implementei essa stack via Portainer, mas um simples`docker compose up -d`funciona tão bem quanto.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_passo_3_recuperar_a_chave_pública&amp;quot;&amp;gt;Passo 3: Recuperar a Chave Pública&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Como o container está vazio na primeira inicialização, Ollama gera automaticamente um novo par de chaves SSH no volume. Recuperamos a chave pública:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ docker exec ollama-instance-b cat /root/.ollama/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDDQ+dvnfmuo49q5O8LOlvgZ39SKORFw47ry9k4H2jPc&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eu verifico que essa chave é &amp;lt;strong&amp;gt;diferente&amp;lt;/strong&amp;gt; daquela da instância nativa :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Instance native
$ cat /usr/share/ollama/.ollama/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGyF...(différente)

# Instance Docker
$ cat ~/ollama-b-data/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDDQ...(différente)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Duas chaves distintas, duas identidades separadas. O truque foi feito.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;ℹ️&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se o seu contêiner reiniciar, ele &amp;lt;strong&amp;gt;reutiliza&amp;lt;/strong&amp;gt; as chaves presentes no volume. A identidade é persistente. Você não perderá a associação com a conta Pro.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_passo_4_registrar_a_chave_no_ollama_com&amp;quot;&amp;gt;Passo 4: Registrar a Chave no Ollama.com&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Acesse &amp;lt;a href=&amp;quot;https://ollama.com/settings/keys&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://ollama.com/settings/keys&amp;lt;/a&amp;gt; → Adicionar Chave SSH. Cole a chave pública da instância Docker e valide.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em seguida, vincula-se esta identidade à conta Pro B :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ docker exec -it ollama-instance-b ollama signin&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O navegador se abre, conectamo-nos com o e-mail da conta B e o token é associado à chave SSH do contêiner. Verificamos que tudo está OK :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ docker exec -it ollama-instance-b ollama signin
User: cherolivpro&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_passo_5_testar_com_um_modelo_pequeno_gratuito&amp;quot;&amp;gt;Passo 5: Testar com um modelo pequeno gratuito&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes de mencionar o preço de uma assinatura Pro, quero ter certeza de que o tubo funciona. Eu faço um pull de um pequeno modelo local e gratuito para validar a conectividade:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ docker exec ollama-instance-b ollama pull qwen3:0.6b
pulling manifest
pulling 7f4030143c1c: 100% ▕██████████████████▏ 522 MB
success

$ curl -s http://localhost:11435/api/tags
{&amp;quot;models&amp;quot;:[{&amp;quot;name&amp;quot;:&amp;quot;qwen3:0.6b&amp;quot;,&amp;quot;model&amp;quot;:&amp;quot;qwen3:0.6b&amp;quot;,...}]}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O porto`11435`responde, o modelo está servido. A instância B está viva.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Depois de ficar tranquilo, passo para o pull do modelo cloud Pro verdadeiro :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ docker exec ollama-instance-b ollama pull deepseek-v4-pro:cloud
pulling manifest
pulling 31c3059e137e: 100% ▕██████████████████▏  344 B
success&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;344 bytes para o manifesto de um modelo cloud — normal, a inferência é feita do lado do servidor, não localmente. Eis o teste definitivo :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ curl -s http://localhost:11435/v1/chat/completions \
  -H &amp;quot;Content-Type: application/json&amp;quot; \
  -d &amp;#39;{&amp;quot;model&amp;quot;:&amp;quot;deepseek-v4-pro:cloud&amp;quot;,&amp;quot;messages&amp;quot;:[{&amp;quot;role&amp;quot;:&amp;quot;user&amp;quot;,&amp;quot;content&amp;quot;:&amp;quot;Dis bonjour en une phrase courte.&amp;quot;}]}&amp;#39;

{
  &amp;quot;id&amp;quot;: &amp;quot;chatcmpl-480&amp;quot;,
  &amp;quot;model&amp;quot;: &amp;quot;deepseek-v4-pro&amp;quot;,
  &amp;quot;choices&amp;quot;: [{
    &amp;quot;message&amp;quot;: { &amp;quot;content&amp;quot;: &amp;quot;Bonjour !&amp;quot; },
    &amp;quot;finish_reason&amp;quot;: &amp;quot;stop&amp;quot;
  }],
  &amp;quot;usage&amp;quot;: { &amp;quot;total_tokens&amp;quot;: 181 }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Olá também a ti, instance B.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_armadilha_da_api_v1models&amp;quot;&amp;gt;A armadilha da API `/v1/models&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Perdi 20 minutos em um detalhe bobo. Quando eu configurei o provedor`ollama-b`no OpenCode, nada apareceu no seletor de modelos. Nada. De forma alguma.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A razão? L&amp;amp;#8217;API`/v1/models`da instância B retornou`{&amp;quot;object&amp;quot;:&amp;quot;list&amp;quot;,&amp;quot;data&amp;quot;:null}`em vez de`{&amp;quot;object&amp;quot;:&amp;quot;list&amp;quot;,&amp;quot;data&amp;quot;:[]}`quand nenhum modelo estava sendo puxado. O valor`null`fazia o parsing travar no lado OpenCode, que não exibia o provider.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A solução: declarar os modelos &amp;lt;strong&amp;gt;explícitamente&amp;lt;/strong&amp;gt; na configuração OpenCode em vez de contar com a descoberta dinâmica.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;~/.config/opencode/opencode.json&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-json hljs&amp;quot; data-lang=&amp;quot;json&amp;quot;&amp;gt;{
  &amp;quot;$schema&amp;quot;: &amp;quot;https://opencode.ai/config.json&amp;quot;,
  &amp;quot;provider&amp;quot;: {
    &amp;quot;ollama&amp;quot;: {
      &amp;quot;npm&amp;quot;: &amp;quot;@ai-sdk/openai-compatible&amp;quot;,
      &amp;quot;name&amp;quot;: &amp;quot;Ollama (local)&amp;quot;,
      &amp;quot;options&amp;quot;: { &amp;quot;baseURL&amp;quot;: &amp;quot;http://localhost:11434/v1&amp;quot; },
      &amp;quot;models&amp;quot;: {
        &amp;quot;gemma4:e2b&amp;quot;: { &amp;quot;name&amp;quot;: &amp;quot;Gemma 4 E2B (local)&amp;quot; }
      }
    },
    &amp;quot;ollama-b&amp;quot;: {
      &amp;quot;npm&amp;quot;: &amp;quot;@ai-sdk/openai-compatible&amp;quot;,
      &amp;quot;name&amp;quot;: &amp;quot;Ollama Instance B (Docker)&amp;quot;,
      &amp;quot;options&amp;quot;: { &amp;quot;baseURL&amp;quot;: &amp;quot;http://localhost:11435/v1&amp;quot; },
      &amp;quot;models&amp;quot;: {
        &amp;quot;qwen3:0.6b&amp;quot;: { &amp;quot;name&amp;quot;: &amp;quot;Qwen3 0.6B (B)&amp;quot; },
        &amp;quot;deepseek-v4-pro:cloud&amp;quot;: { &amp;quot;name&amp;quot;: &amp;quot;DeepSeek V4 Pro (B)&amp;quot; }
      }
    }
  }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com esta declaração explícita, o OpenCode vê imediatamente o provider B e seus modelos. Um reinício de sessão, e o seletor`/models`mostra as duas instâncias lado a lado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock tip&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-tip&amp;quot; title=&amp;quot;💡&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se você não vê seu provider custom em`/models`, não perca três horas a reiniciar seu terminal. Declare os modelos manualmente em`opencode.json`— isso resolve o problema instantaneamente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_resultado_duas_sessões_duas_contas_zero_conflito&amp;quot;&amp;gt;Resultado: Duas Sessões, Duas Contas, Zero Conflito&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Finalmente, posso lançar duas sessões OpenCode simultaneamente:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;Session 1 → /models → Ollama (local) → deepseek-v4-pro:cloud → Compte A
Session 2 → /models → Ollama Instance B (Docker) → deepseek-v4-pro:cloud → Compte B&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cada sessão tem sua própria cota, seu próprio rate limit, e não se pisam nos pés. Mesmo máquina, mesmo cartão azul, dois e-mails diferentes.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E o melhor? O contêiner Docker está em`restart: always`. Ele sobrevive às reinicializações do sistema sem intervenção manual.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_lições_aprendidas&amp;quot;&amp;gt;Lições Aprendidas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Docker isola tudo, mesmo a identidade&amp;lt;/strong&amp;gt;— Um simples bind mount de volume é suficiente para dar a um contêiner seu próprio conjunto de chaves SSH, tornando-o indistinguível de uma máquina física diferente aos olhos do Ollama.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Dois e-mails, mesmo CB&amp;lt;/strong&amp;gt;— Ollama não bloque as assinaturas múltiplas desde o mesmo meio de pagamento. Só o email deve ser distinto.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Testar grátis antes de pagar&amp;lt;/strong&amp;gt; — Un &amp;lt;code&amp;gt;ollama pull qwen3:0.6b&amp;lt;/code&amp;gt; (modèle libre, 522 Mo) permet de valider toute la chaîne réseau sans débourser un centime. Vous validez le plomberie d&amp;amp;#8217;abord, vous activez le Pro ensuite.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;/v1/models` com &amp;lt;code&amp;gt;data: null&amp;lt;/code&amp;gt; quebra OpenCode&amp;lt;/strong&amp;gt;— Se você configurar um provider custom com`&amp;quot;models&amp;quot;: {}`, OpenCode tenta descobrir os modelos via a API. Se a API responde`data: null`, o provedor não aparece. Declare os modelos explicitamente.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Duas contas, isso é multiprocessamento humano&amp;lt;/strong&amp;gt;— Uma conta Pro = uma sessão OpenCode ativa. Duas contas = duas sessões paralelas. Para alguém que está malabarizando entre vários projetos Gradle simultaneamente, isso é um game changer.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_para_ir_mais_longe&amp;quot;&amp;gt;Para Ir Mais Longe&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://ollama.com/settings/keys&amp;quot;&amp;gt;Ollama Settings — Gerenciamento de chaves SSH&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.ollama.com/&amp;quot;&amp;gt;Documentação oficial Ollama&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://hub.docker.com/r/ollama/ollama&amp;quot;&amp;gt;Imagem Docker Ollama no Docker Hub&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://opencode.ai/docs/providers/#ollama&amp;quot;&amp;gt;OpenCode — Provedor de Configuração Ollama&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Artigo publicado em 2026-05-08&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Instalar o terminal Kitty e configurar o Zsh como shell padrão no Xubuntu</title>
            <link >https://cheroliv.com//blog/2025/0080_kitty_term_post.html</link>
            <pubDate>Thu, 7 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0080_kitty_term_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_escolher_o_kitty_e_o_zsh&amp;quot;&amp;gt;Por que escolher o Kitty e o Zsh?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_pré_requisitos_do_sistema&amp;quot;&amp;gt;Pré-requisitos do sistema&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_download_e_instalação_do_kitty&amp;quot;&amp;gt;Download e instalação do Kitty&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_baixar_o_script_de_instalação&amp;quot;&amp;gt;Baixar o script de instalação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_criação_dos_links_simbólicos&amp;quot;&amp;gt;Criação dos links simbólicos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_integração_do_kitty_no_xubuntu&amp;quot;&amp;gt;Integração do Kitty no Xubuntu&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_copiar_o_arquivo_da_área_de_trabalho&amp;quot;&amp;gt;Copiar o arquivo da área de trabalho&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_editar_o_ficheiro_desktop&amp;quot;&amp;gt;Editar o ficheiro desktop&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_associar_kitty_como_terminal_padrão_opcional&amp;quot;&amp;gt;Associar Kitty como terminal padrão (opcional)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalação_do_zsh&amp;quot;&amp;gt;Instalação do Zsh&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_definir_o_zsh_como_shell_padrão&amp;quot;&amp;gt;Definir o Zsh como shell padrão&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_método_global_para_todos_os_terminais&amp;quot;&amp;gt;Método global (para todos os terminais)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_método_específico_para_kitty&amp;quot;&amp;gt;Método específico para Kitty&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configuração_do_zsh_opcional&amp;quot;&amp;gt;Configuração do Zsh (opcional)&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_criar_um_arquivo_zshrc&amp;quot;&amp;gt;Criar um arquivo .zshrc&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_exemplo_de_configuração_mínima&amp;quot;&amp;gt;Exemplo de configuração mínima&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_resumo_do_workflow_completo&amp;quot;&amp;gt;Resumo do workflow completo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_verificação_final&amp;quot;&amp;gt;Verificação final&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_abrir_o_arquivo_de_configuração&amp;quot;&amp;gt;📂 Abrir o arquivo de configuração&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_️_adicionar_ou_modificar_font_size&amp;quot;&amp;gt;�🖋️ Adicionar ou modificar `font_size&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_salvar_e_reiniciar_kitty&amp;quot;&amp;gt;💾 Salvar e reiniciar Kitty&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_ajuste_dinâmico_zoom_temporário&amp;quot;&amp;gt;�✅ Ajuste dinâmico (Zoom temporário)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalação_e_configuração_do_oh_my_zsh&amp;quot;&amp;gt;Instalação e configuração do Oh My Zsh&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalação_do_oh_my_zsh&amp;quot;&amp;gt;Instalação do Oh My Zsh&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_mudar_o_tema&amp;quot;&amp;gt;Mudar o tema&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_verificação&amp;quot;&amp;gt;Verificação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_do_caso_de_uso&amp;quot;&amp;gt;Diagrama do caso de uso&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_ativar_a_translucididade_no_kitty&amp;quot;&amp;gt;Ativar a translucididade no Kitty&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_pré_requisito_para_a_transparência&amp;quot;&amp;gt;Pré-requisito para a transparência&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configuração_de_transparência_no_kitty_conf&amp;quot;&amp;gt;Configuração de transparência no kitty.conf&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_reiniciar_kitty&amp;quot;&amp;gt;Reiniciar Kitty&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_complementos_possíveis&amp;quot;&amp;gt;Complementos possíveis&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_sequência_inicialização_da_kitty_com_transparência&amp;quot;&amp;gt;Diagrama de sequência - Inicialização da Kitty com transparência&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_limitações_conhecidas&amp;quot;&amp;gt;Limitações conhecidas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_desativar_o_sinal_sonoro_sino_audível&amp;quot;&amp;gt;Desativar o sinal sonoro (sino audível)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_atualização_de_kitty&amp;quot;&amp;gt;Atualização de Kitty&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referências&amp;quot;&amp;gt;Referências&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este guia explica passo a passo como instalar o terminal &amp;lt;strong&amp;gt;kitty&amp;lt;/strong&amp;gt; no Xubuntu, configurá-lo corretamente e substituir o bash por &amp;lt;strong&amp;gt;zsh&amp;lt;/strong&amp;gt; como shell padrão. O projeto kitty está disponível aqui: &amp;lt;a href=&amp;quot;https://github.com/kovidgoyal/kitty&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://github.com/kovidgoyal/kitty&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ Kitty é um terminal moderno, rápido e configurável, particularmente adequado para desenvolvedores exigentes. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_escolher_o_kitty_e_o_zsh&amp;quot;&amp;gt;Por que escolher o Kitty e o Zsh?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Kitty distingue-se por :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Desempenhos superiores graças à aceleração da GPU,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Uma configuração totalmente baseada em um arquivo de texto único,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Um excelente suporte a fontes e ligaduras&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Zsh, por outro lado, é um shell interativo poderoso que oferece :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Uma conclusão inteligente,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Um prompt personalizável,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Uma grande compatibilidade com o bash.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao combinar Kitty e Zsh, você desfruta de um ambiente produtivo e estético.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/kitty-zsh-overview.png&amp;quot; alt=&amp;quot;kitty zsh overview&amp;quot; width=&amp;quot;729&amp;quot; height=&amp;quot;305&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este guia é intencionalmente limitado à instalação via script e ao Xubuntu.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_pré_requisitos_do_sistema&amp;quot;&amp;gt;Pré-requisitos do sistema&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes de começar, certifique-se de ter :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Xubuntu instalado,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;acesso à internet,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;um shell bash funcional para lançar o script.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Verifique seu ambiente:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;uname -a
lsb_release -a&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_download_e_instalação_do_kitty&amp;quot;&amp;gt;Download e instalação do Kitty&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Kitty recomenda instalá‑lo via um script oficial que coloca os arquivos em`~/.local/kitty.app`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;Important&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este método é independente dos gerenciadores de pacotes (sem`apt`).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_baixar_o_script_de_instalação&amp;quot;&amp;gt;Baixar o script de instalação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Execute o seguinte comando :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;curl -L https://sw.kovidgoyal.net/kitty/installer.sh | sh /dev/stdin&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este comando executa:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;download dos binários,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;extração em`~/.local/kitty.app`,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;criação dos links simbólicos.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/download-install.svg&amp;quot; alt=&amp;quot;download install&amp;quot; width=&amp;quot;288&amp;quot; height=&amp;quot;336&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_criação_dos_links_simbólicos&amp;quot;&amp;gt;Criação dos links simbólicos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para poder invocar`kitty`A partir do terminal, crie um link simbólico :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;ln -s ~/.local/kitty.app/bin/kitty ~/.local/bin/kitty&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Adicione`~/.local/bin`adicionar ao seu PATH se ainda não o tiver feito :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;#39;export PATH=&amp;quot;$HOME/.local/bin:$PATH&amp;quot;&amp;#39; &amp;amp;gt;&amp;amp;gt; ~/.zshrc
source ~/.zshrc&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você pode verificar a instalação:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;kitty --version&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_integração_do_kitty_no_xubuntu&amp;quot;&amp;gt;Integração do Kitty no Xubuntu&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para que Kitty appele no menu de aplicativos :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_copiar_o_arquivo_da_área_de_trabalho&amp;quot;&amp;gt;Copiar o arquivo da área de trabalho&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Copie o arquivo`.desktop`fornecido :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;cp ~/.local/kitty.app/share/applications/kitty.desktop ~/.local/share/applications/&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_editar_o_ficheiro_desktop&amp;quot;&amp;gt;Editar o ficheiro desktop&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Edite o arquivo :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;nano ~/.local/share/applications/kitty.desktop&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Verifique ou modifique as linhas seguintes :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;[Desktop Entry]
Version=1.0
Type=Application
Name=Kitty Terminal
Comment=Fast GPU-based terminal emulator
Exec=/home/$USER/.local/kitty.app/bin/kitty
Icon=kitty
Categories=System;TerminalEmulator;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Substitua`YOURUSER`pelo seu nome de usuário&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/desktop-integration.png&amp;quot; alt=&amp;quot;desktop integration&amp;quot; width=&amp;quot;439&amp;quot; height=&amp;quot;335&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_associar_kitty_como_terminal_padrão_opcional&amp;quot;&amp;gt;Associar Kitty como terminal padrão (opcional)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para que Kitty seja o terminal iniciado por padrão:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sudo update-alternatives --install /usr/bin/x-terminal-emulator x-terminal-emulator ~/.local/kitty.app/bin/kitty 50
sudo update-alternatives --config x-terminal-emulator&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Selecione`kitty`no menu proposto.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_instalação_do_zsh&amp;quot;&amp;gt;Instalação do Zsh&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se o Zsh ainda não estiver instalado, execute:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt install zsh&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Verifique a instalação:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;zsh --version&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_definir_o_zsh_como_shell_padrão&amp;quot;&amp;gt;Definir o Zsh como shell padrão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você pode mudar seu shell globalmente ou apenas no Kitty.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_método_global_para_todos_os_terminais&amp;quot;&amp;gt;Método global (para todos os terminais)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Identifique o caminho absoluto:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;which zsh&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em geral`/usr/bin/zsh`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Altere o shell padrão:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;chsh -s /usr/bin/zsh&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Desconecte-se e reconecte-se.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/zsh-default-shell.svg&amp;quot; alt=&amp;quot;zsh default shell&amp;quot; width=&amp;quot;546&amp;quot; height=&amp;quot;341&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_método_específico_para_kitty&amp;quot;&amp;gt;Método específico para Kitty&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se você prefere apenas mudar o shell no Kitty, edite o arquivo de configuração :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;nano ~/.config/kitty/kitty.conf&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Adicione esta linha :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;shell zsh&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Salve e reinicie o Kitty.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_configuração_do_zsh_opcional&amp;quot;&amp;gt;Configuração do Zsh (opcional)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para personalizar seu Zsh:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_criar_um_arquivo_zshrc&amp;quot;&amp;gt;Criar um arquivo .zshrc&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;touch ~/.zshrc
nano ~/.zshrc&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_exemplo_de_configuração_mínima&amp;quot;&amp;gt;Exemplo de configuração mínima&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;export ZSH_THEME=&amp;quot;robbyrussell&amp;quot;
export PATH=&amp;quot;$HOME/.local/bin:$PATH&amp;quot;
alias ll=&amp;quot;ls -la&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_resumo_do_workflow_completo&amp;quot;&amp;gt;Resumo do workflow completo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O esquema abaixo resume todo o processo :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/full-workflow.png&amp;quot; alt=&amp;quot;full workflow&amp;quot; width=&amp;quot;348&amp;quot; height=&amp;quot;643&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_verificação_final&amp;quot;&amp;gt;Verificação final&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Lance Kitty :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;kitty&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você deveria ver que`zsh`está ativo :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;echo $SHELL&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Resultado esperado :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;/usr/bin/zsh&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para modificar o tamanho da fonte em&amp;lt;strong&amp;gt;Kitty&amp;lt;/strong&amp;gt;, é preciso ajustar o parâmetro`font_size`no arquivo de configuração. Aqui está o procedimento detalhado :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_abrir_o_arquivo_de_configuração&amp;quot;&amp;gt;📂 Abrir o arquivo de configuração&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O arquivo principal de configuração geralmente está localizado em :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;~/.config/kitty/kitty.conf&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se este arquivo ainda não existir, você pode criá-lo. Exemple :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;nano ~/.config/kitty/kitty.conf&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_️_adicionar_ou_modificar_font_size&amp;quot;&amp;gt;�🖋️ Adicionar ou modificar `font_size&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Adicione a diretiva a seguir, ou modifique-a se ela já existir:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;font_size 14.0&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui`14.0`Corresponde ao tamanho desejado. Você pode colocar qualquer valor decimal, por exemplo`12.0`, &amp;lt;code&amp;gt;15.5&amp;lt;/code&amp;gt;, etc.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_salvar_e_reiniciar_kitty&amp;quot;&amp;gt;💾 Salvar e reiniciar Kitty&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Salve (&amp;lt;code&amp;gt;Ctrl + O&amp;lt;/code&amp;gt;, então`Entrée`) e saia (&amp;lt;code&amp;gt;Ctrl + X&amp;lt;/code&amp;gt;) se você usa`nano`.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Feche todas as janelas do Kitty, depois reinicie o terminal para que a mudança tenha efeito.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_ajuste_dinâmico_zoom_temporário&amp;quot;&amp;gt;�✅ Ajuste dinâmico (Zoom temporário)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se você deseja zoomer/dézoomer temporariamente sem modificar a configuração, utilize os atalhos de teclado padrão:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Aumentar o tamanho da fonte&amp;lt;/strong&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;Ctrl + Shift + =&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Diminuir o tamanho da fonte&amp;lt;/strong&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;Ctrl + Shift + -&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Redefinir o tamanho&amp;lt;/strong&amp;gt;&amp;amp;lt;/think&amp;amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;Ctrl + Shift + Backspace&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Essas configurações não persistem após o fechamento do terminal.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_instalação_e_configuração_do_oh_my_zsh&amp;quot;&amp;gt;Instalação e configuração do Oh My Zsh&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Uma vez que o Zsh esteja instalado e configurado como shell padrão, você pode enriquecer seu ambiente com &amp;lt;strong&amp;gt;Oh My Zsh&amp;lt;/strong&amp;gt;, um framework de gerenciamento de configuração do Zsh, oferecendo:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;uma coleção de temas para o prompt,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;muitos plugins,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;uma organização clara da configuração.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_instalação_do_oh_my_zsh&amp;quot;&amp;gt;Instalação do Oh My Zsh&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Certifique-se de que o Zsh está ativo :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;zsh --version&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se necessário, lance :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;zsh&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em seguida, baixe e execute o script de instalação oficial:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sh -c &amp;quot;$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este script:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;crie a pasta`~/.oh-my-zsh`,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;salva eventualmente seu arquivo`~/.zshrc`,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;ativa automaticamente o novo prompt.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_mudar_o_tema&amp;quot;&amp;gt;Mudar o tema&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Abra o seu arquivo de configuração:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;nano ~/.zshrc&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Altere a variável`ZSH_THEME`para escolher outro tema (por exemplo`agnoster`) :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;ZSH_THEME=&amp;quot;agnoster&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Salve e recarregue a configuração :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;source ~/.zshrc&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_verificação&amp;quot;&amp;gt;Verificação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Inicie o Kitty e verifique se o prompt foi alterado corretamente. Você pode confirmar que o Oh My Zsh está ativo executando:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;echo $ZSH&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O resultado deveria ser similar a :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;/home/seu_utilizador/.oh-my-zsh&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_do_caso_de_uso&amp;quot;&amp;gt;Diagrama do caso de uso&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/ohmyzsh-installation.png&amp;quot; alt=&amp;quot;ohmyzsh installation&amp;quot; width=&amp;quot;511&amp;quot; height=&amp;quot;480&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_ativar_a_translucididade_no_kitty&amp;quot;&amp;gt;Ativar a translucididade no Kitty&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Uma das funcionalidades mais apreciadas do Kitty é a possibilidade de adicionar &amp;lt;strong&amp;gt;transparência&amp;lt;/strong&amp;gt; ao fundo do terminal. Isso permite ver levemente o ambiente subjacente (janela, área de trabalho, etc.), o que é particularmente útil em multitarefa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_pré_requisito_para_a_transparência&amp;quot;&amp;gt;Pré-requisito para a transparência&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A transparência no Kitty baseia-se no&amp;lt;strong&amp;gt;compositor de janelas&amp;lt;/strong&amp;gt;usado pelo ambiente gráfico. No Xubuntu (baseado em XFCE), certifique‑se de que a&amp;lt;strong&amp;gt;composição está ativada&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;À vérifier :  -&amp;amp;#8594; .A verificar :   (but must output only translation)&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;So output:.A verificar :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Proceed.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;amp;lt;/think&amp;amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;A vérifier :&amp;lt;/div&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;clique com o botão direito na área de trabalho &amp;amp;gt; Configurações &amp;amp;gt;&amp;lt;strong&amp;gt;Parâmetros do gerenciador de janelas&amp;lt;/strong&amp;gt;,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;aba&amp;lt;strong&amp;gt;Compositor&amp;lt;/strong&amp;gt;: a caixa &amp;lt;strong&amp;gt;&amp;lt;code&amp;gt;Activer la composition&amp;lt;/strong&amp;gt;&amp;lt;/code&amp;gt; deve estar marcada.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/transparency-usecase.png&amp;quot; alt=&amp;quot;transparency usecase&amp;quot; width=&amp;quot;597&amp;quot; height=&amp;quot;225&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_configuração_de_transparência_no_kitty_conf&amp;quot;&amp;gt;Configuração de transparência no kitty.conf&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Abra o seu ficheiro de configuração`~/.config/kitty/kitty.conf` :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;nano ~/.config/kitty/kitty.conf&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Adicione ou modifique as linhas seguintes:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-conf hljs&amp;quot; data-lang=&amp;quot;conf&amp;quot;&amp;gt;background_opacity 0.85&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este parâmetro aceita valores entre :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;1.0&amp;lt;/code&amp;gt;→ opaco (nenhuma transparência),&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;0.0&amp;lt;/code&amp;gt;→ totalmente transparente (inutilizável)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;valores intermediários como`0.90`, &amp;lt;code&amp;gt;0.75&amp;lt;/code&amp;gt;, etc.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um bom compromisso visual geralmente está entre`0.85` et &amp;lt;code&amp;gt;0.95&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_reiniciar_kitty&amp;quot;&amp;gt;Reiniciar Kitty&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Feche todas as instâncias do Kitty e, em seguida, reinicie uma nova janela para que as alterações tenham efeito :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;kitty&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_complementos_possíveis&amp;quot;&amp;gt;Complementos possíveis&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se você usa um fundo de terminal personalizado, também pode definir uma cor de fundo com transparência (em RGBA) :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-conf hljs&amp;quot; data-lang=&amp;quot;conf&amp;quot;&amp;gt;background #1e1e2eff&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Última componente ()&amp;lt;code&amp;gt;ff&amp;lt;/code&amp;gt;) indica a opacidade (de`00` à &amp;lt;code&amp;gt;ff&amp;lt;/code&amp;gt;)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_de_sequência_inicialização_da_kitty_com_transparência&amp;quot;&amp;gt;Diagrama de sequência - Inicialização da Kitty com transparência&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/transparency-sequence.png&amp;quot; alt=&amp;quot;transparency sequence&amp;quot; width=&amp;quot;771&amp;quot; height=&amp;quot;422&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_limitações_conhecidas&amp;quot;&amp;gt;Limitações conhecidas&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock warning&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-warning&amp;quot; title=&amp;quot;Warning&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A translucidez não funciona se: - o compositor XFCE está desativado, - um gerenciador de janelas incompatível é utilizado, - você está usando Kitty no modo &amp;lt;strong&amp;gt;tmux&amp;lt;/strong&amp;gt; em segundo plano sem suporte de transparência.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_desativar_o_sinal_sonoro_sino_audível&amp;quot;&amp;gt;Desativar o sinal sonoro (sino audível)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Por padrão, o Kitty emite um som de &amp;lt;strong&amp;gt;bip&amp;lt;/strong&amp;gt; quando um comando errado é inserido ou quando um evento de terminal o dispara. Este som pode rapidamente se tornar incômodo, especialmente em um ambiente de desenvolvimento intenso.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para desativar esse comportamento, adicione a seguinte diretiva ao arquivo`~/.config/kitty/kitty.conf`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-conf hljs&amp;quot; data-lang=&amp;quot;conf&amp;quot;&amp;gt;audible_bell no&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você também pode ativar um &amp;lt;strong&amp;gt;retorno visual&amp;lt;/strong&amp;gt; em substituição ao bip sonoro :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-conf hljs&amp;quot; data-lang=&amp;quot;conf&amp;quot;&amp;gt;visual_bell yes&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_atualização_de_kitty&amp;quot;&amp;gt;Atualização de Kitty&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Como o Kitty é instalado por meio de um script e não por um gerenciador de pacotes, a atualização é feita ao relançar o script de instalação oficial. Isso permite baixar a versão mais recente e atualizar sua instalação existente em`~/.local/kitty.app`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O sufixo`.app`no caminho`~/.local/kitty.app`é a convenção de instalação do Kitty no Linux via o seu script oficial, e não indica de forma alguma uma compatibilidade exclusiva com o macOS.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;Important&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes de executar o comando de atualização, certifique-se de estar no seu diretório pessoal (&amp;lt;code&amp;gt;~&amp;lt;/code&amp;gt;)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Execute o seguinte comando a partir do seu diretório pessoal:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;curl -L https://sw.kovidgoyal.net/kitty/installer.sh | sh /dev/stdin&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este script cuidará de:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Baixar os binários da nova versão.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Extrair os ficheiros em`~/.local/kitty.app`.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Atualizar os links simbólicos se necessário.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Após a atualização, recomenda-se fechar todas as instâncias do Kitty e reiniciá‑las para garantir que a nova versão seja reconhecida.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_referências&amp;quot;&amp;gt;Referências&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se você quiser aprofundar a personalização, consulte a documentação oficial:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Kitty : &amp;lt;a href=&amp;quot;https://sw.kovidgoyal.net/kitty/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://sw.kovidgoyal.net/kitty/&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://sw.kovidgoyal.net/kitty/conf/#opt-kitty.background_opacity&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://sw.kovidgoyal.net/kitty/conf/#opt-kitty.background_opacity&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://sw.kovidgoyal.net/kitty/conf/#opt-kitty.audible_bell&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://sw.kovidgoyal.net/kitty/conf/#opt-kitty.audible_bell&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Zsh: &amp;lt;a href=&amp;quot;https://zsh.sourceforge.io/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://zsh.sourceforge.io/&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Github: &amp;lt;a href=&amp;quot;https://github.com/kovidgoyal/kitty&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://github.com/kovidgoyal/kitty&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Zsh: &amp;lt;a href=&amp;quot;https://zsh.sourceforge.io/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://zsh.sourceforge.io/&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://ohmyz.sh/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://ohmyz.sh/&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/ohmyzsh/ohmyzsh/wiki&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://github.com/ohmyzsh/ohmyzsh/wiki&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.xfce.org/xfce/xfwm4/compositor&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://docs.xfce.org/xfce/xfwm4/compositor&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você dispõe agora :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;de um terminal moderno e performante (Kitty)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;um shell poderoso e personalizável (Zsh)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;de uma configuração limpa e independente dos pacotes do sistema&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;de um terminal estético e funcional equipado com Oh My Zsh&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>DGX Spark vs Cloud vs Assinaturas de LLM — O Benchmark Econômico que mata o sonho do hardware local</title>
            <link >https://cheroliv.com//blog/2026/0119_benchmark_dgx_spark_vs_cloud_abonnement_llm_post.html</link>
            <pubDate>Wed, 6 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0119_benchmark_dgx_spark_vs_cloud_abonnement_llm_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_contexto_um_mercado_que_invertiu&amp;quot;&amp;gt;O Contexto : Um Mercado que Invertiu&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_painel_das_opções&amp;quot;&amp;gt;Painel das Opções&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_excluí_e_por_que&amp;quot;&amp;gt;O que excluí (e por que)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_inferência_simples_a_morte_do_hardware_local&amp;quot;&amp;gt;Inferência Simples: A Morte do Hardware Local&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_cálculo&amp;quot;&amp;gt;O Cálculo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_mesmo_com_uso_intensivo&amp;quot;&amp;gt;Mesmo com uso intensivo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fine_tuning_o_cloud_é_invencível&amp;quot;&amp;gt;Fine-Tuning : O Cloud é invencível&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_custo_real_de_um_fine_tune&amp;quot;&amp;gt;O custo real de um Fine-Tune&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_os_atores_especializados&amp;quot;&amp;gt;Os Atores Especializados&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_única_verdadeira_razão_a_soberania_dos_dados&amp;quot;&amp;gt;A única verdadeira razão: a soberania dos dados&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_verdadeiro_cálculo_da_confidencialidade&amp;quot;&amp;gt;O Verdadeiro Cálculo da Confidencialidade&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_síntese_a_matriz_de_decisão&amp;quot;&amp;gt;Síntese: A matriz de decisão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_caso_particular_a_assinatura_pro_face_aos_modelos_chineses&amp;quot;&amp;gt;O Caso Particular: A Assinatura Pro Face aos Modelos Chineses&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_os_modelos_chinos_mudam_tudo&amp;quot;&amp;gt;Por que os Modelos Chinos Mudam Tudo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_isso_significa_para_o_hardware&amp;quot;&amp;gt;O Que Isso Significa para o Hardware&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_e_huawei&amp;quot;&amp;gt;E Huawei?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão_o_luxo_da_proximidade&amp;quot;&amp;gt;Conclusão: O Luxo da Proximidade&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 14 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;NVIDIA vende o DGX Spark como um « supercomputador IA de mesa » por $4 699. A Ollama oferece seus modelos na nuvem por $20/mês. Um H100 pode ser alugado por $1,38/h na Vast.ai. E os modelos chineses de código aberto — DeepSeek, Qwen, GLM — estão disponíveis em todo lugar, gratuitamente, sem precisar comprar uma placa. Então, para que serve esse Spark exatamente? Fiz as contas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_contexto_um_mercado_que_invertiu&amp;quot;&amp;gt;O Contexto : Um Mercado que Invertiu&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em maio de 2026, o cenário da IA mudou radicalmente em relação ao anúncio do DGX Spark na CES 2025. Três forças convergiram para tornar o hardware local cada vez mais difícil de justificar:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O colapso dos preços do cloud GPU&amp;lt;/strong&amp;gt;: um H100 é alugado entre $1.38 e $3/h, versus $5-8/h há 18 meses&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A explosão dos modelos chineses open-source&amp;lt;/strong&amp;gt;DeepSeek-V4, Qwen-3, GLM-5 são competitivos com os modelos ocidentais, disponíveis gratuitamente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A ascensão das plataformas de inferência gerenciada&amp;lt;/strong&amp;gt;Ollama, Groq, Together AI oferecem APIs com tarifas agressivas, com camadas gratuitas generosas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O DGX Spark, anunciado a $2 999 e depois elevado a $4 699, chega a um mundo que não precisa mais dele.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_painel_das_opções&amp;quot;&amp;gt;Painel das Opções&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui estão as soluções disponíveis em maio de 2026 para rodar os LLMs, da mais leve à mais pesada:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.5%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;opção&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Custo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;VRAM / Capacidade&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Modelos acessíveis&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Restrição principal&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Ollama Gratuito&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;$0/mês&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;nuvem leve&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Todos modelos públicos&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Uso limitado, 1 modelo simultâneo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Claude Pro&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;$20/mês (~$240/ano)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;N/A (API fechada)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Claude 4.x apenas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Modelo único, não é open-source&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Ollama Pro&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;$20/mês (~$200/ano)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Meio de nuvem&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Todos os modelos públicos&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;3 modelos simultâneos máximo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Ollama Max&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;$100/mês (~$1 200/ano)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;nuvem pesada&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Todos os modelos públicos&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;10 modelos simultâneos&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;GPU de nuvem (H100)&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;$1.38-3/h&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;80 Go HBM&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Todos os modelos, ajuste fino&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Faturação horária, configuração necessária&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;DGX Spark&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;$4 699 (compra)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;128 Go unifié&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tous modèles &amp;amp;lt;128 Go&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Lento (~10x menos que um H100), fixo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Mac Studio M3 Ultra&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;~$5 000+&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;192 Go unifié&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tous modèles &amp;amp;lt;192 Go&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Não CUDA, ecossistema MLX&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock tip&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-tip&amp;quot; title=&amp;quot;💡&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os preços de GPU na nuvem citados são os da Vast.ai e do RunPod em maio de 2026 — spot/on-demand, não reservado. Os preços dos hyperscalers (AWS, GCP, Azure) permanecem 2-3x mais altos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_que_excluí_e_por_que&amp;quot;&amp;gt;O que excluí (e por que)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Excluí intencionalmente as seguintes opções deste benchmark:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;API OpenAI / Anthropic / Google&amp;lt;/strong&amp;gt;: o assunto aqui é fazer rodar modelos &amp;lt;strong&amp;gt;open-source&amp;lt;/strong&amp;gt; que podemos escolher, ajustar finamente, e não depender de um único fornecedor&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Huawei Ascend / Atlas&amp;lt;/strong&amp;gt; : Huawei ne commercialise pas d&amp;amp;#8217;équivalent desktop au DGX Spark. La gamme Ascend (910C, 910D) et Atlas (300I Duo, 350) est orientée datacenter — cartes PCIe, serveurs rack — pas des boîtiers compacts prêts à brancher. La puce Ascend 910C embarque 128 Go HBM mais c&amp;amp;#8217;est une puce serveur, pas un système autonome. L&amp;amp;#8217;Atlas 300I Duo offre 96 Go pour ~$1 400 mais nécessite une machine hôte&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_inferência_simples_a_morte_do_hardware_local&amp;quot;&amp;gt;Inferência Simples: A Morte do Hardware Local&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Começamos pelo caso de uso mais comum: conversar com um LLM, fazer revisão de código, gerar texto. Sem fine-tuning, sem processamento em lote, apenas inferência interativa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_cálculo&amp;quot;&amp;gt;O Cálculo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um DGX Spark por $4.699. Dividamos pelo custo anual de cada assinatura:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-breakeven&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 7) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Ponto de Equilíbrio — DGX Spark ($4 699) vs Assinaturas
rectangle &amp;quot;DGX Spark
$4 699&amp;quot; as spark #FFCDD2
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Ponto de Equilíbrio — DGX Spark ($4 699) vs Assinaturas
rectangle &amp;quot;DGX Spark
$4 699&amp;quot; as spark #FFCDD2

rectangle &amp;quot;Ollama Pro
$200/ano&amp;quot; as opro #C8E6C9
rectangle &amp;quot;Claude Pro
$240/ano&amp;quot; as cpro #C8E6C9
rectangle &amp;quot;Ollama Max
$1 200/ano&amp;quot; as omax #FFF9C4
rectangle &amp;quot;Cloud H100\n$3/h × 10h/sem&amp;quot; as cloud #BBDEFB

spark --&amp;amp;gt; opro : 23.5 années ⏳
spark --&amp;amp;gt; cpro : 19.6 années ⏳
spark --&amp;amp;gt; omax : 3.9 années
spark --&amp;amp;gt; cloud : 3.0 années (à 10h/semaine, intermittent)

note bottom of spark
  Sans compter l&amp;#39;électricité (~$150/an)
  Sans compter l&amp;#39;obsolescence hardware
  Sans compter qu&amp;#39;il est 10x plus lent
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O veredicto é brutal. Para inferência :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ollama Pro&amp;lt;/strong&amp;gt;: você precisaria&amp;lt;strong&amp;gt;23 anos&amp;lt;/strong&amp;gt;para amortizar um DGX Spark. O Spark estará obsoleto há 20 anos.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ollama Max&amp;lt;/strong&amp;gt;:&amp;lt;strong&amp;gt;4 anos&amp;lt;/strong&amp;gt;— desde que se utilize o Spark 24/7, o que ninguém faz&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Claude Pro&amp;lt;/strong&amp;gt;: mesmo problema,&amp;lt;strong&amp;gt;20 anos&amp;lt;/strong&amp;gt;para o single-model fechado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ A promessa do « supercomputador IA de mesa » esbarra numa realidade contábil elementar : $4 699 / $20 por mês = nunca rentável para uso individual. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_mesmo_com_uso_intensivo&amp;quot;&amp;gt;Mesmo com uso intensivo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Consideremos o pior cenário para a nuvem: você executa um H100 8h/dia, 5 dias/semana, 52 semanas/ano.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;8 × 5 × 52 = 2 080 horas/ano&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;2 080 × $2/h (preço médio spot H100) =&amp;lt;strong&amp;gt;$4 160/ano&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O Spark ($4 699) é amortizado em&amp;lt;strong&amp;gt;13 meses&amp;lt;/strong&amp;gt;. Mas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O H100 é&amp;lt;strong&amp;gt;~10x mais rápido&amp;lt;/strong&amp;gt;que o GB10 do Spark. Você faz em 1h o que o Spark faz em 10h. Para o mesmo volume de trabalho, o H100 custa $416/ano, não $4 160.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Le H100 a 80 Go HBM à 3.35 To/s de bande passante. Le Spark a 128 Go LPDDR5X à 273 Go/s. Pour les gros modèles, la bande passante compte plus que la capacité.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;❗&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Comparar 1h de H100 a 1h de Spark não faz sentido. O H100 faz o trabalho 10x mais rápido. O verdadeiro custo na nuvem para uma carga de trabalho dada é portanto ~10x inferior ao cálculo ingênuo baseado nas horas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_fine_tuning_o_cloud_é_invencível&amp;quot;&amp;gt;Fine-Tuning : O Cloud é invencível&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O argumento clássico dos defensores do Spark : « sim mas para o fine-tuning, é diferente ».&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não, isso não é.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_custo_real_de_um_fine_tune&amp;quot;&amp;gt;O custo real de um Fine-Tune&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um fine-tune LoRA/QLoRA no Unsloth de um modelo 7B :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;~2-3h em um H100 =&amp;lt;strong&amp;gt;$5 a $9&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Mesma coisa no Spark =&amp;lt;strong&amp;gt;20-30h&amp;lt;/strong&amp;gt;(10 vezes mais lento), bloqueia a máquina&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com os $4 699 do Spark, você pode financer:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;~500 a ~900 fine-tunes&amp;lt;/strong&amp;gt;em cloud H100&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ou &amp;lt;strong&amp;gt;~23 anos&amp;lt;/strong&amp;gt;de Ollama Pro&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ou &amp;lt;strong&amp;gt;~4 anos&amp;lt;/strong&amp;gt;de Ollama Max&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E para modelos maiores (70B+) :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Le Spark avec ses 128 Go peut charger un 70B en Q4 — mais le fine-tune sera extrêmement lent&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Un H100 avec 80 Go peut fine-tuner un 70B en QLoRA 4-bit en quelques heures&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Para um fine-tune completo, é preciso multi-GPU de qualquer forma — o Spark está fora da corrida&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_os_atores_especializados&amp;quot;&amp;gt;Os Atores Especializados&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em 2026, plataformas como Unsloth, Modal, Replicate e Together AI oferecem fine-tuning serverless :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Sem gestão de GPU&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Sem configuração de ambiente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Pagamento por minuto&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Otimizações integradas (flash attention, quantização automática)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-fine-tuning-cost&amp;quot; class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-90d994cebed893eb8223115d10d5228a.svg&amp;quot; alt=&amp;quot;Comparaison des coûts de fine-tuning&amp;quot; width=&amp;quot;626&amp;quot; height=&amp;quot;244&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_única_verdadeira_razão_a_soberania_dos_dados&amp;quot;&amp;gt;A única verdadeira razão: a soberania dos dados&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Depois de eliminar a inferência, o fine-tuning, e a relação custo-benefício — o que resta ao Spark ?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A confidencialidade absoluta.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não é sobre privacidade «não quero que meus prompts sejam lidos por um funcionário da OpenAI». Isso, isso é um problema falso em 2026: Ollama, Groq e a maioria dos provedores respeitáveis têm políticas de zero logs e zero retenção.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A verdadeira confidencialidade é:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Air gap regulamentar&amp;lt;/strong&amp;gt;: defesa, diplomacia, saúde — os dados não podem &amp;lt;strong&amp;gt;fisicamente&amp;lt;/strong&amp;gt; sair do prédio&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Dados proprietários críticos&amp;lt;/strong&amp;gt;: código-fonte de um produto não publicado, segredos industriais, algoritmos de negociação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Resiliência rede&amp;lt;/strong&amp;gt;: a IA deve continuar a funcionar mesmo se a Internet cai&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock caution&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-caution&amp;quot; title=&amp;quot;🔒&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas sejamos honestos: qual a porcentagem dos compradores do DGX Spark que realmente se enquadram nesse caso? 1%? O resto é um « nice to have » transformado em uma despesa de $4 699.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_verdadeiro_cálculo_da_confidencialidade&amp;quot;&amp;gt;O Verdadeiro Cálculo da Confidencialidade&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para uso individual:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ollama Pro&amp;lt;/strong&amp;gt;($20/mês) + modelos de código aberto chineses = solução pragmática&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Se um prompt vazar no Ollama, ele se perde nos milhões de solicitações diárias. É uma gota d&amp;amp;#8217;água no oceano.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O risco real não é a fuga de um prompt — é o treinamento não consentido nos seus dados. E isso, os fornecedores sérios o proíbem contractualmente.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ O medo de « distillar seu prompt no LLM » é um nível de propagação de informação pessoal tão diluído que se encaixa mais na ansiedade do que na ameaça real. Em escala industrial, é diferente. Mas em escala individual, é ruído. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_síntese_a_matriz_de_decisão&amp;quot;&amp;gt;Síntese: A matriz de decisão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;diag-decision-matrix&amp;quot; class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-8c4f9436c7d0314ed52c5b25b1237a11.svg&amp;quot; alt=&amp;quot;Matrice de décision pour choisir sa solution LLM&amp;quot; width=&amp;quot;2012&amp;quot; height=&amp;quot;433&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2222%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2232%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Critério&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Ollama Livre&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Ollama Pro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;GPU H100 na nuvem&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;DGX Spark&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Inferência&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�✅ limitada&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅ confortável&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�✅ Exagero&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;⚠️ Lento&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;ajuste fino&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�❌&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�❌&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�✅ Invencível&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�⚠️ Possível mas lento&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Custo anual&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;$0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;$200&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Variável (~$416-2 080*)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;$4 699 adiantado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Confidencialidade&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�⚠️ Nuvem EUA&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�⚠️ Nuvem EUA&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;⚠️ Nuvem EUA (variável)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅ Total&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Manutenção&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nenhuma&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nenhuma&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Configuração única&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Eletricidade, atualizações&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Obsolescência&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nenhuma&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nenhuma&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;nenhuma&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;3-5 anos&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Ecossistema de software&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Todos os modelos abertos&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Todos os modelos open&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Todos modelos open&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;CUDA (mais lento)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;veredicto&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;ocasional&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Recomendado&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;prós/pesquisa&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Somente air-gap&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;*Variável conforme volume de trabalho ; com velocidade H100 ~10x &amp;amp;gt; Spark, o custo efetivo para uma carga de trabalho determinada é baixo.&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_caso_particular_a_assinatura_pro_face_aos_modelos_chineses&amp;quot;&amp;gt;O Caso Particular: A Assinatura Pro Face aos Modelos Chineses&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um ponto merece ser destacado: a combinação&amp;lt;strong&amp;gt;Ollama Pro a $20/mês + modelos chineses de código aberto&amp;lt;/strong&amp;gt;É provavelmente a melhor relação custo-benefício da história da IA para o grande público.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_por_que_os_modelos_chinos_mudam_tudo&amp;quot;&amp;gt;Por que os Modelos Chinos Mudam Tudo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;DeepSeek-V4-Pro&amp;lt;/strong&amp;gt;: 1.6T parâmetros, CSA+HCA, 1M contexto, competitivo com Claude 4 e GPT-5 para o código&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Qwen-3&amp;lt;/strong&amp;gt;: 235B, 256K contexto, excelente em francês e em raciocínio&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;GLM-5.1&amp;lt;/strong&amp;gt;: 744B, MLA+DSA, 200K contexto, personalizado para o agente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Todos disponíveis no Ollama. Todos de código aberto (licenças permissivas). Todos utilizáveis sem restrição através de uma assinatura de $20/mês.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock tip&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-tip&amp;quot; title=&amp;quot;💡&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com o Ollama Pro, você tem acesso a ~5 modelos « de ponta » de código aberto pelo preço de um único modelo fechado (Claude Pro). O valor é tão assimétrico que quase se torna absurdo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_que_isso_significa_para_o_hardware&amp;quot;&amp;gt;O Que Isso Significa para o Hardware&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se a melhor relação qualidade‑preço for uma assinatura na nuvem de $20 por mês, o hardware local passa a ser uma escolha de &amp;lt;strong&amp;gt;confort&amp;lt;/strong&amp;gt; ou de &amp;lt;strong&amp;gt;princípio&amp;lt;/strong&amp;gt; — não uma escolha econômica.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um DGX Spark a $4 699, equivale a :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;235 meses de Ollama Pro&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;23 jogos AAA dia de lançamento&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;4 MacBook Air M4&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Um ano de aluguel de um T2 em Lyon&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para cálculo. Lento.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_e_huawei&amp;quot;&amp;gt;E Huawei?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Já que a pergunta me foi feita durante minhas pesquisas: não, a Huawei não comercializa um equivalente ao DGX Spark.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O que existe:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ascend 910C&amp;lt;/strong&amp;gt; : 128 Go HBM, 800 TFLOPS FP16 — mais c&amp;amp;#8217;est une puce serveur, pas un desktop&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Atlas 300I Duo&amp;lt;/strong&amp;gt; : 96 Go LPDDR4X, carte PCIe à ~$1 400 — nécessite un hôte&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Atlas 350&amp;lt;/strong&amp;gt; : 112 Go HBM, 1.56 PFLOPS FP4 — accélérateur datacenter, pas un boîtier compact&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Huawei reste focalisé sur le marché des serveurs et des centres de données. Pour un boîtier compact « prêt à brancher » avec 120+ Go de VRAM, le DGX Spark (128 Go unifié) et le Mac Studio M3 Ultra (192 Go unifié) sont les seules options en 2026. Et pour l&amp;amp;#8217;instant, aucun constructeur chinois n&amp;amp;#8217;a annoncé de produit équivalent dans ce format.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_o_luxo_da_proximidade&amp;quot;&amp;gt;Conclusão: O Luxo da Proximidade&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O DGX Spark não é um mau produto. É um mau investimento para 99% dos compradores potenciais.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-verdict&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 9) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Cenário da IA em 2026 — Quem domina?
left to right direction

package &amp;quot;Uso Pessoal / PME&amp;quot; #C8E6C9 {
  rectangle &amp;quot;🏆 Ollama Pro
^^^^^
 Syntax Error? (Assumed diagram type: class)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Cenário da IA em 2026 — Quem domina?
left to right direction

package &amp;quot;Uso Pessoal / PME&amp;quot; #C8E6C9 {
  rectangle &amp;quot;🏆 Ollama Pro
$20/mês&amp;quot; as pro
  rectangle &amp;quot;Ollama Grátis
$0/mês&amp;quot; as free
  rectangle &amp;quot;Spot da GPU em nuvem
$1-3/h&amp;quot; as spot
}

package &amp;quot;Uso Intensivo / Profissional&amp;quot; #FFF9C4 {
  rectangle &amp;quot;🏆 Ollama Max
$100/mês&amp;quot; as max
  rectangle &amp;quot;�🏆 GPU de nuvem dedicado\n$2-4/h&amp;quot; as dedicated
  rectangle &amp;quot;Modal / Together AI
Serverless&amp;quot; as serverless
}

package &amp;quot;Air-Gap Obrigatório&amp;quot; #FFCDD2 {
  rectangle &amp;quot;DGX Spark
$4 699&amp;quot; as spark
  rectangle &amp;quot;Mac Studio M3U\n~$5 000&amp;quot; as mac
}

note bottom of spark
  1% des cas d&amp;#39;usage — défense,
  santé réglementée, secrets
  industriels critiques
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ Comprar um DGX Spark em 2026 é como comprar um servidor de e‑mail em 2010: tecnicamente possível, filosoficamente satisfatório, economicamente absurdo para um indivíduo. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O cálculo é simples, e ele não mente :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;$0&amp;lt;/strong&amp;gt;para conversar com o DeepSeek no Ollama Free&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;$200/ano&amp;lt;/strong&amp;gt;para trabalhar seriamente com Ollama Pro&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;$5-9&amp;lt;/strong&amp;gt;para um fine-tune pontual na nuvem GPU&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;$4 699&amp;lt;/strong&amp;gt;para&amp;amp;#8230;&amp;amp;#8203; a experiência de ter uma caixa NVIDIA na sua secretária?&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O mercado decidiu. As assinaturas em nuvem e os modelos open-source chineses venceram a batalha do preço. O hardware local sobrevive nos nichos regulatórios, não nos escritórios dos desenvolvedores.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ O DGX Spark não é uma ferramenta rentável. É uma declaração. « Quero minha IA em casa, não na nuvem de outra pessoa. » É respeitável. Mas em 2026, quando Ollama te dá DeepSeek-V4-Pro na nuvem por $20/mês, é principalmente uma declaração que custa muito caro. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Este artigo resulta de uma conversa com meu agente IA sobre a pertinência econômica do DGX Spark, cruzada com uma vigilância de mercado sobre os preços de GPU na nuvem (Vast.ai, RunPod), as tabelas tarifárias Ollama/Claude, e o ecossistema dos modelos chineses de código aberto. Os preços citados são de maio de 2026 — provavelmente ainda terão caído quando você ler estas linhas.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>A Matriz de Governança de LLM — Por que a Segurança da Sua IA Começa pela Árvore</title>
            <link >https://cheroliv.com//blog/2026/0118_matrice_gouvernance_llm_zone_acces_workspace_post.html</link>
            <pubDate>Mon, 4 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0118_matrice_gouvernance_llm_zone_acces_workspace_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_o_prompt_engineering_é_um_castelo_de_areia&amp;quot;&amp;gt;O Problema: o Prompt Engineering é um Castelo de Areia&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_minha_resposta_a_matriz_44&amp;quot;&amp;gt;minha resposta: a matriz 4×4&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_as_cinco_zonas_dimensão_espacial_rgpd&amp;quot;&amp;gt;As Cinco Zonas (Dimensão Espacial — RGPD)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_classificação_epistemológica_segunda_dimensão&amp;quot;&amp;gt;A Classificação Epistemológica (Segunda Dimensão)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_como_o_llm_consome_esta_matriz&amp;quot;&amp;gt;Como o LLM consome esta matriz&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_implementação_tarefas_gradle_tipadas&amp;quot;&amp;gt;Implementação : Tarefas Gradle Tipadas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_é_um_manifesto_não_apenas_uma_spec&amp;quot;&amp;gt;Por que é um Manifesto, não apenas uma Spec&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_cascata_completa_do_brain_dump_ao_artigo_do_blog&amp;quot;&amp;gt;A Cascata Completa — do Brain Dump ao Artigo do Blog&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_próscontras&amp;quot;&amp;gt;Prós/Contras&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão_guarde_seus_arquivos_não_seus_prompts&amp;quot;&amp;gt;Conclusão: Guarde seus arquivos, não seus prompts&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referências&amp;quot;&amp;gt;Referências&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Tempo de leitura: 14 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Durante meses, tenho empilhado as regras de governança de agentes. Arquivos EAGER, checklists LAZY, protocolos de encerramento de sessão em seis etapas. E, contudo, uma questão permanecia em suspense: &amp;lt;strong&amp;gt;como o LLM sabe o que ele tem o direito de fazer com um arquivo ?&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A resposta não está em um prompt. Ela está no sistema de arquivos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está a especificação de arquitetura que torna a resposta utilizável — la &amp;lt;strong&amp;gt;matriz de governança LLM&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_problema_o_prompt_engineering_é_um_castelo_de_areia&amp;quot;&amp;gt;O Problema: o Prompt Engineering é um Castelo de Areia&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando se dá um arquivo a um LLM, dá-se tudo a ele. O conteúdo, sim — mas também a ausência de barreiras de proteção. O LLM não sabe se esse arquivo contém um segredo, uma opinião especulativa, ou do código fechado. Ele vai indexá-lo, resumi-lo, citá-lo, misturá-lo com outros dados — et potencialmente Publicá-lo em um embedding público ou uma resposta de usuário&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O truque clássico, é o prompt :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;&amp;quot;Tu es un assistant sécurisé. Ne divulgue jamais d&amp;#39;informations confidentielles.
Si tu détectes un secret, ignore-le. Si tu détectes une opinion, ne la répète pas.&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este prompt tem três problemas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ele depende da boa vontade do LLM.&amp;lt;/strong&amp;gt; Um modelo suficientemente grande para&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Resolver bugs complexos é grande o suficiente para contornar uma instrução de segurança afogada em 200k tokens de contexto&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;É contextual, não estrutural. Mude o prompt, mude o LLM,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;mude de sessão — e a regra desaparece.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ele não escala.&amp;lt;/strong&amp;gt; Cada novo tipo de arquivo, cada novo nível&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;de confidencialidade exige uma nova cláusula. Seu prompt torna-se um texto regulamentar que ninguém lê por completo&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock caution&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-caution&amp;quot; title=&amp;quot;🔒&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A segurança de um sistema nunca deve depender de uma instrução que se pode esquecer, contornar, ou não carregar. Ela deve depender de uma barreira que não se pode atravessar sem querer explicitamente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_minha_resposta_a_matriz_44&amp;quot;&amp;gt;minha resposta: a matriz 4×4&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A solução que implementei no meu workspace é uma &amp;lt;strong&amp;gt;matriz zona × direito de acesso LLM&amp;lt;/strong&amp;gt;. Ela não pede ao LLM que seja prudente. Ela rende a imprudência estruturalmente impossível.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-matrix-overview&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 10) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam wrapWidth 220

title A Matriz de Governança LLM — Zona × Direito de Acesso
package &amp;quot;Espaço de trabalho&amp;quot; {

  rectangle &amp;quot;RAIZ
(Círculo 0)
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam wrapWidth 220

title A Matriz de Governança LLM — Zona × Direito de Acesso
package &amp;quot;Espaço de trabalho&amp;quot; {

  rectangle &amp;quot;RAIZ
(Círculo 0)
━━━━━━
Brain dump
Pensamento livre
[PROIBIDO LLM]&amp;quot; as Z0 #FFB3B3
  rectangle &amp;quot;configuration/
(Círculo 1)
━━━━━━
Segredos, tokens
Infra privada
[PROIBIDO LLM]&amp;quot; as Z1 #FFB3B3
  rectangle &amp;quot;office/\n(Círculo 2)\n━━━━━━\nDados editoriais\nArtigos, enquadramentos\n[Visão Filtrada]&amp;quot; as Z2 #FFF3B3
  rectangle &amp;quot;foundry/private/
(Círculo 3)
━━━━━━
Code closed source
[DATASET PRIVADO]&amp;quot; as Z3 #B3D9FF
  rectangle &amp;quot;foundry/public/
(Círculo 3)
━━━━━━
Código aberto
Apache 2.0
[ACESSO LIVRE]&amp;quot; as Z4 #B3FFB3

}

Z0 -[hidden]-&amp;amp;gt; Z1
Z1 -[hidden]-&amp;amp;gt; Z2
Z2 -[hidden]-&amp;amp;gt; Z3
Z3 -[hidden]-&amp;amp;gt; Z4

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_as_cinco_zonas_dimensão_espacial_rgpd&amp;quot;&amp;gt;As Cinco Zonas (Dimensão Espacial — RGPD)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cada zona do espaço de trabalho tem um nível de RGPD que determina &amp;lt;strong&amp;gt;onde&amp;lt;/strong&amp;gt; o arquivo pode ser roteado :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 4.1682%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Zona&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nível RGPD&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Conteúdo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Direito de acesso público ao RAG&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;raiz&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;0 — Íntimo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Brain dumps, conversas LLM&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;PROIBIDO&amp;lt;/strong&amp;gt;— nunca indexado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;configuration/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1 — Proprietário restrito&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Segredos, tokens, chaves API&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;proibido&amp;lt;/strong&amp;gt;— nunca indexado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;office/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Restrito colaborativo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Data editorial, enquadramento, formações&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;FILTRADO&amp;lt;/strong&amp;gt;— Visão apenas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;foundry/private/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;3 — Aberto condicional&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Código fechado, SaaS não público&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;PROIBIDO&amp;lt;/strong&amp;gt;— conjunto de dados privado exclusivamente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;foundry/public/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;4 — Público nativo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Código Apache 2.0, plugins publicados&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;livre&amp;lt;/strong&amp;gt;— indexação completa&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A regra é simples: &amp;lt;strong&amp;gt;o caminho do arquivo determina o que o LLM pode fazer com ele&amp;lt;/strong&amp;gt; Não há metadados para manter, não há tag para adicionar no frontmatter, não há classificação manual em cada commit. O arquivo está em`OSS/&amp;lt;code&amp;gt;? É público. Está em`configuration/&amp;lt;/code&amp;gt;; Ele é intocável.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_classificação_epistemológica_segunda_dimensão&amp;quot;&amp;gt;A Classificação Epistemológica (Segunda Dimensão)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A dimensão espacial diz &amp;lt;strong&amp;gt;onde&amp;lt;/strong&amp;gt; router. Mas há um segundo eixo, perpendicular : &amp;lt;strong&amp;gt;o que&amp;lt;/strong&amp;gt; router. Alguns ficheiros numa zona autorizada contêm informações que não devem ser diluídas assim:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-epistemic-filter&amp;quot; class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-17d60994fe8990d0ca7c0e782cf8fdf8.svg&amp;quot; alt=&amp;quot;Filtre épistémique Vision/Opinion/Stratégie appliqué aux zones 2 et 3&amp;quot; width=&amp;quot;1215&amp;quot; height=&amp;quot;558&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A grelha de classificação epistemológica, gravada na Regra 2‑bis da minha governança agente :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;| Status | Definição | Sinal LLM | Destino | | VISION | Arquitetura estabilizada, padrão testado | Linguagem declarativa, referências sessões/testes | Diluição completa + Blog | | ESTRATÉGIA | Posicionamento negócios, preços | Vocabulário mercado, concorrência | Documentos raiz apenas | | OPINION | Especulação, hipótese não validada | Linguagem hipotética, intuição | Confinamento Círculo 0 |&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A combinação das duas dimensões — zona física × classificação epistemológica — forma a&amp;lt;strong&amp;gt;matriz completa&amp;lt;/strong&amp;gt; :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-full-matrix&amp;quot; class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-a4dca1e1342127dee30a4086926815bc.svg&amp;quot; alt=&amp;quot;La matrice complète : 5 zones × 3 classifications épistémiques&amp;quot; width=&amp;quot;442&amp;quot; height=&amp;quot;132&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_como_o_llm_consome_esta_matriz&amp;quot;&amp;gt;Como o LLM consome esta matriz&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A matriz não é um documento que o LLM lê. É uma &amp;lt;strong&amp;gt;restrição&amp;lt;/strong&amp;gt; implicita** codificada no vetor composto de contexto (EPIC 9 — em curso de implementação).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está o que o LLM recebe em cada sessão:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;VECTEUR COMPOSITE DE CONTEXTE
├── RAG pgvector → OSS/ + office/Vision
│   (similarité sémantique sur contenu publiable)
├── Knowledge Graph graphify → OSS/
│   (relations exactes entre artéfacts publics)
├── Knowledge Graph privé → CSS/
│   (relations entre code closed source — jamais exporté)
├── Métadonnées de zone → chaque fichier taggé par zone physique
│   (le LLM sait s&amp;#39;il est dans office/ ou OSS/)
└── Historique des décisions → WORKSPACE_VISION.adoc
    (contexte temporel des arbitrages)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Concretamente, quando o LLM me diz :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;Je vais indexer le contenu de edster/ pour enrichir le knowledge graph...&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O metadado da zona responde mesmo antes de o LLM terminar sua frase : &amp;lt;code&amp;gt;edster/`está em`CSS/&amp;lt;/code&amp;gt;, nível 3 →&amp;lt;strong&amp;gt;acesso proibido ao knowledge graph público&amp;lt;/strong&amp;gt; O RAG não o vê. O grafo não o toca. A resposta do usuário não não o cita.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock tip&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-tip&amp;quot; title=&amp;quot;💡&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A ontologia espacial funciona como uma&amp;lt;strong&amp;gt;permissão Unix&amp;lt;/strong&amp;gt;para os LLMs. Você não pede a um processo para ser cuidadoso com`/etc/shadow`— você lhe nega acesso à leitura. Mesmo princípio.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_implementação_tarefas_gradle_tipadas&amp;quot;&amp;gt;Implementação : Tarefas Gradle Tipadas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A matriz não é uma filosofia. É código. Aqui está o contrato da tarefa Gradle que o implementa:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;abstract class ZoneAwareIndexer @Inject constructor(
    private val rootDir: DirectoryProperty,
    private val configServer: ConfigServerProperty  // → configuration/
) : DefaultTask() {

    @Input
    val zoneFilter: SetProperty&amp;amp;lt;Zone&amp;amp;gt; = project.objects.setProperty(Zone::class.java)

    @OutputFile
    val ragIndex: RegularFileProperty = project.objects.fileProperty()

    @TaskAction
    fun index() {
        val allowedPaths = zoneFilter.get().flatMap { it.resolve(rootDir.get()) }
        val forbiddenPaths = Zone.RESTRICTED.resolve(rootDir.get())
                            + Zone.INTIMATE.resolve(rootDir.get())
        // Le RAG ne voit jamais configuration/ ni la racine
        // CSS/ alimente un index privé, pas celui-ci
        // office/ est filtré par le classifieur Vision/Opinion
    }
}

enum class Zone {
    INTIMATE,      // Cercle 0 — racine
    RESTRICTED,    // Cercle 1 — configuration/
    EDITORIAL,     // Cercle 2 — office/
    CLOSED_SOURCE, // Cercle 3 — foundry/private/
    OPEN_SOURCE    // Cercle 3 — foundry/public/
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A tarefa é &amp;lt;strong&amp;gt;testable&amp;lt;/strong&amp;gt;. Você pode escrever um teste que verifica:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test
fun `le RAG n&amp;#39;indexe jamais configuration`() {
    val index = ZoneAwareIndexer(rootDir, configServer)
    index.zoneFilter.set(setOf(Zone.OPEN_SOURCE, Zone.EDITORIAL))
    index.index()

    assertThat(index.ragIndex).doesNotContain(&amp;quot;configuration/&amp;quot;)
}

@Test
fun `le code closed source est exclu du RAG public`() {
    val index = ZoneAwareIndexer(rootDir, configServer)
    index.zoneFilter.set(setOf(Zone.OPEN_SOURCE))  // OSS only

    assertThat(index.ragIndex).doesNotContain(&amp;quot;edster&amp;quot;)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;380 testes, 380 PASS. Assim como para o plugin plantuml-gradle — a segurança é testada, não esperada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_é_um_manifesto_não_apenas_uma_spec&amp;quot;&amp;gt;Por que é um Manifesto, não apenas uma Spec&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este documento é uma especificação de arquitetura porque define :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;uns&amp;lt;strong&amp;gt;dimensões&amp;lt;/strong&amp;gt;(espacial, epistemológico)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;de&amp;lt;strong&amp;gt;regras determinísticas&amp;lt;/strong&amp;gt;(zone → direito de acesso)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Un &amp;lt;strong&amp;gt;contrato de tarefa&amp;lt;/strong&amp;gt;(Gradle tipado, testável)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Uma&amp;lt;strong&amp;gt;implementação de referência&amp;lt;/strong&amp;gt;(meu espaço de trabalho)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas também é um&amp;lt;strong&amp;gt;manifesto&amp;lt;/strong&amp;gt;porque toma posição em um debate maior : &amp;lt;strong&amp;gt;como governar o acesso de um LLM a dados ?&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A resposta da indústria é o prompt engineering + os guardrails&amp;lt;br&amp;gt;
os classificadores pós-hoc. Minha resposta é: &amp;lt;strong&amp;gt;guarde seus arquivos em Os bons arquivos.&amp;lt;/strong&amp;gt; O resto é automático.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;❗&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este manifesto não diz &amp;quot;os LLMs devem ser alinhados&amp;quot;. Ele diz: &amp;quot;o alinhamento de um LLM sobre suas regras de segurança é uma consequência de seu sistema de arquivos, não do seu prompt.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_cascata_completa_do_brain_dump_ao_artigo_do_blog&amp;quot;&amp;gt;A Cascata Completa — do Brain Dump ao Artigo do Blog&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para ilustrar o ciclo completo, aqui está como essa ideia de matriz nasceu e foi diluída :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;Session 4 mai 2026 — Feedback global du workspace
  │
  ├→ Le LLM identifie : &amp;quot;la dualité public/privé est invisible&amp;quot;
  │   → Classifié VISION (constat architectural vérifiable)
  │
  ├→ PICTURE_ME_ROLLIN_MATRICE_4X4.adoc — STIMULUS (Cercle 0)
  │   → Brain dump structuré, classification VISION confirmée
  │
  ├→ DILUTION → WORKSPACE_AS_PRODUCT.adoc (section Matrice 4×4)
  │   → La spec vit dans les documents racine
  │
  ├→ DILUTION → WORKSPACE_ORGANIZATION.adoc (OSS/CSS)
  │   → La granularisation est documentée
  │
  └→ ARTICLE 0117 (ce document) → cheroliv.com
      → La VISION est publiée&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O que começou como uma observação em sessão(&amp;quot;ei, o RAG não sabe que edster est closed source&amp;quot;) tornou-se uma especificação de arquitetura Publicada em menos de uma sessão. Este é o padrão STIMULUS em ação.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_próscontras&amp;quot;&amp;gt;Prós/Contras&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Esta Abordagem (Ontologia Espacial + Matriz)&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;A Abordagem Clássica (Prompt + Guardrails)&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Mecânica* — o sistema de arquivos bloqueia o acesso, não o LLM&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Declarativa&amp;lt;/strong&amp;gt; — o prompt pede ao LLM que seja prudente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Testable&amp;lt;/strong&amp;gt; — o contrato de tarefa Gradle é verificado por 380+ testes&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Não testável&amp;lt;/strong&amp;gt; — &amp;quot;O LLM respeitou a regra?&amp;quot; é uma questão aberta&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Independente do LLM&amp;lt;/strong&amp;gt; — mude de modelo, as pastas permanecem&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Dependente do LLM&amp;lt;/strong&amp;gt; — cada modelo interpreta o prompt de forma diferente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Escalável&amp;lt;/strong&amp;gt; — novo tipo de dados = nova pasta, zero alteração de código&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Non scalable&amp;lt;/strong&amp;gt; — novo tipo de dado = nova cláusula de prompt, novo risco de regressão&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Auditable&amp;lt;/strong&amp;gt; — o sistema de arquivos é o rastro de auditoria&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Não auditável&amp;lt;/strong&amp;gt; — o prompt não tem histórico, não tem diff, não tem blame&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Restrição&amp;lt;/strong&amp;gt;: exige uma disciplina de organização inicial&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Restrição&amp;lt;/strong&amp;gt; : exige uma disciplina de redação de prompt a cada sessão&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_guarde_seus_arquivos_não_seus_prompts&amp;quot;&amp;gt;Conclusão: Guarde seus arquivos, não seus prompts&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A matriz de governança LLM que acabo de descrever não é um produto. É uma&amp;lt;strong&amp;gt;especificação de arquitetura&amp;lt;/strong&amp;gt;— e é por isso que ela está publicável.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O que a torna robusta é que ela não pede nada ao LLM. Ela não lhe Ela não confia. Ela não se baseia em seu alinhamento, sua compreensão. do francês, ou sua boa vontade. Ela se baseia no sistema de arquivos — A camada mais baixa, a mais estável, a mais testada de toda a pilha.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando eu digo ao meu LLM &amp;quot;você pode indexar tudo em`OSS/&amp;lt;code&amp;gt;, nada em `configuration/&amp;lt;/code&amp;gt;, et `office/`apenas se for classificado Vision não é uma instrução. É um &amp;lt;strong&amp;gt;filtro&amp;lt;/strong&amp;gt; — implementado em Kotlin, Verificado por 380 testes, executado antes de que o LLM veja os dados.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É a diferença entre dizer a alguém &amp;quot;não olhe nessa gaveta E fechar a gaveta à chave. A governança agente que estou construindo desde meses são a chave. A matriz é o plano do móvel.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_referências&amp;quot;&amp;gt;Referências&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0116_compartimentage_epistemique_automatise_llm_post.html&amp;quot;&amp;gt;Article 0116 — O Compartimentação Epistêmica Automatizada&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0114_gouvernance_cercles_confiance_ontologie_spatiale_alignement_llm_post.html&amp;quot;&amp;gt;Artigo 0114 — Os Círculos de Confiança e a Ontologia Espacial&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0108_gouvernance_agent_opencode_eager_lazy_post.html&amp;quot;&amp;gt;Article 0108 — Governar um Agente IA com o AsciiDoc&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;../2026/0115_anonymiseur_dataset_mvp0_realite_augmentee_llm_post.html&amp;quot;&amp;gt;Artigo 0115 — O Anonimizador MVP0&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>A Granularização OSS/CSS — Por que eu cortei foundry/ em dois</title>
            <link >https://cheroliv.com//blog/2026/0117_granularisation_oss_css_repositorie_workspace_post.html</link>
            <pubDate>Sun, 3 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0117_granularisation_oss_css_repositorie_workspace_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_acidente_que_esperava_acontecer&amp;quot;&amp;gt;O acidente que esperava acontecer&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_verdadeira_pergunta&amp;quot;&amp;gt;A Verdadeira Pergunta&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_oss_e_css&amp;quot;&amp;gt;A Solução: OSS/ e CSS/&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_oss_e_css_e_não_public_e_private&amp;quot;&amp;gt;Por que &amp;quot;OSS&amp;quot; e &amp;quot;CSS&amp;quot; e não &amp;quot;Public&amp;quot; e &amp;quot;Private&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_árvore_completa_quatro_zonas_não_três&amp;quot;&amp;gt;A Árvore Completa — Quatro Zonas, Não Três&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_caso_particular_cheroliv_com&amp;quot;&amp;gt;O Caso Particular : cheroliv.com&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_bakery_gradle_não_liga&amp;quot;&amp;gt;bakery-gradle não liga&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_impacto_sobre_o_agente_de_governança&amp;quot;&amp;gt;Impacto sobre o Agente de Governança&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_se_ganha_e_o_que_se_perde&amp;quot;&amp;gt;O Que Se Ganha (E O Que Se Perde)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão_o_sinal_físico_bate_o_sinal_de_software&amp;quot;&amp;gt;Conclusão: o sinal físico bate o sinal de software&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referências&amp;quot;&amp;gt;Referências&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 10 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;foundry/&amp;lt;/code&amp;gt;— a zona funcional que hospeda o código do meu workspace — contém 18 projetos. Oito são código aberto sob a licença Apache 2.0. Apenas um é closed source — o SaaS Edster. Durante meses, esses nove projetos coabitaram. na mesma pasta, separados apenas por&amp;amp;#8230;&amp;amp;#8203; nada. O status público/privado deles era uma informação na minha cabeça, não no sistema de arquivos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E então eu quis conectar um RAG. E aí, tudo desabou.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_acidente_que_esperava_acontecer&amp;quot;&amp;gt;O acidente que esperava acontecer&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em abril de 2026, comecei a implementar o RAG pgvector para slider-gradle. O princípio: indexar todos os repositórios de`foundry/&amp;lt;code&amp;gt;, produzir de embeddings, e injetá‑los no contexto do LLM para que ele tenha uma perception&amp;quot; do dossier`foundry/&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O pipeline era simples:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val repos = fileTree(rootDir) {
    include(&amp;quot;**/*.adoc&amp;quot;, &amp;quot;**/*.kts&amp;quot;, &amp;quot;**/*.kt&amp;quot;, &amp;quot;**/*.json&amp;quot;, &amp;quot;**/*.yml&amp;quot;)
}
val chunks = repos.map { chunk(it) }
val embeddings = chunks.map { embed(it) }
pgvector.insert(embeddings)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Simples. Eficaz. E&amp;lt;strong&amp;gt;perigoso&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Porque este`fileTree`não distingue entre`plantuml-gradle/` (Apache 2.0, public) e`edster/`(closed source, private). Ele engole tudo. E se um dia eu publicar estes embeddings — num dashboard, numa resposta LLM, em um conjunto de dados de treinamento — o código proprietário da Edster está vazando.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock caution&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-caution&amp;quot; title=&amp;quot;🔒&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O problema não é que um LLM leia código fechado. O problema, é que esse código termine em um embedding vetorial &amp;lt;strong&amp;gt;público&amp;lt;/strong&amp;gt; — irreversível, não removível, não auditável.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_verdadeira_pergunta&amp;quot;&amp;gt;A Verdadeira Pergunta&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não é &amp;quot;como impedir que o LLM vazeie código?&amp;quot;. É &amp;quot;como tornar a fuga estruturalmente impossível ?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A resposta não é um prompt. A resposta, é de cortar o dossier em dois.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_oss_e_css&amp;quot;&amp;gt;A Solução: OSS/ e CSS/&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;foundry/
├── plantuml-gradle/       ← public
├── bakery-gradle/         ← public
├── magic-stick/           ← public
├── edster/                ← PRIVÉ
├── slider-gradle/         ← public
└── ...                    ← mélange invisible&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Após :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;foundry/
├── OSS/                   ← tout est Apache 2.0
│   ├── plantuml-gradle/
│   ├── bakery-gradle/
│   ├── magic-stick/
│   ├── slider-gradle/
│   └── ...
└── CSS/                   ← tout est closed source / private
    └── edster/&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le `fileTree`torna-se :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val ossRepos = fileTree(File(rootDir, &amp;quot;OSS&amp;quot;)) {
    include(&amp;quot;**/*.adoc&amp;quot;, &amp;quot;**/*.kts&amp;quot;, &amp;quot;**/*.kt&amp;quot;, &amp;quot;**/*.json&amp;quot;, &amp;quot;**/*.yml&amp;quot;)
}
val cssRepos = fileTree(File(rootDir, &amp;quot;CSS&amp;quot;)) {
    include(&amp;quot;**/*.adoc&amp;quot;, &amp;quot;**/*.kts&amp;quot;, &amp;quot;**/*.kt&amp;quot;, &amp;quot;**/*.json&amp;quot;, &amp;quot;**/*.yml&amp;quot;)
}

// Embeddings publics — OSS seulement
pgvectorPublic.insert(ossRepos.map { chunk(it) }.map { embed(it) })

// Dataset fine-tuning privé — CSS seulement
fineTuningDataset.insert(cssRepos.map { chunk(it) })  // jamais publié&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;❗&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O RAG público nunca vê`CSS/`. O código fechado alimenta um dataset privado de ajuste fino — um modelo interno que nunca sai da minha casa separação é mecânica, não declarativa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_oss_e_css_e_não_public_e_private&amp;quot;&amp;gt;Por que &amp;quot;OSS&amp;quot; e &amp;quot;CSS&amp;quot; e não &amp;quot;Public&amp;quot; e &amp;quot;Private&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A escolha dos acrônimos OSS (Open Source Software) e CSS (Closed Source Software) é deliberado:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Public&amp;quot;/&amp;quot;Private&amp;quot; descreve o&amp;lt;strong&amp;gt;visibilidade&amp;lt;/strong&amp;gt;(o que o GitHub vê)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;OSS&amp;quot;/&amp;quot;CSS&amp;quot; descreve a&amp;lt;strong&amp;gt;natureza&amp;lt;/strong&amp;gt;do código (o que o LLM deve saber)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A visibilidade do GitHub é um metadado do repositório remoto. A natureza do código é O LLM não tem acesso à API do GitHub — mas ele tem acesso ao sistema de arquivos.&amp;lt;code&amp;gt;CSS/`ele disse &amp;quot;atenção, este código não está sob licença livre&amp;quot; sem precisar ler um`LICENSE`ou analisar um`package.json&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock tip&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-tip&amp;quot; title=&amp;quot;💡&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O nome da pasta é o metadado mais robusto que você pode dar a um LLM. Ela não pode ser mal analisada, ignorada, ou mal interpretada. O LLM vê`CSS/edster/`no caminho do arquivo — ele sabe&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_árvore_completa_quatro_zonas_não_três&amp;quot;&amp;gt;A Árvore Completa — Quatro Zonas, Não Três&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A granularização OSS/CSS complementa a ontologia de três zonas. A zona `foundry/`passa de 1 para 2 subzonas funcionais :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-four-zone-workspace&amp;quot; class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-472f6392b3896db2716782e1dca49bcd.svg&amp;quot; alt=&amp;quot;L&amp;amp;#8217;organisation de foundry/ en deux sous-zones OSS et CSS&amp;quot; width=&amp;quot;1751&amp;quot; height=&amp;quot;5763&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;| Zone | RGPD | Indexação RAG pública | Ajuste fino do dataset | | Raiz | Nível 0 — Íntimo | ✗ | ✗ | | &amp;lt;code&amp;gt;configuration/&amp;lt;/code&amp;gt;| Nível 1 — Restrito | ✗ | ✗ | | &amp;lt;code&amp;gt;office/&amp;lt;/code&amp;gt;| Nível 2 — Colaborativo | ✓ Filtrado | ✓ Filtrado | |&amp;lt;code&amp;gt;foundry/private/&amp;lt;/code&amp;gt;| Nível 3 — Abertura condicional | ✗ | ✓ Privado | (blank)`foundry/public/`Nível 4 — Público nativo | ✓ Livre | ✓ Público&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_caso_particular_cheroliv_com&amp;quot;&amp;gt;O Caso Particular : cheroliv.com&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Enquanto estava lá, corriji outra incoerência. Meu site &amp;lt;code&amp;gt;cheroliv.com`morava em`foundry/`como um projeto de software de pleno direito — com seu próprio build Gradle, sua própria CI, sua própria governança&amp;lt;/code&amp;gt;.agents/`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas os artigos de blog não são código. São &amp;lt;strong&amp;gt;dados&amp;lt;/strong&amp;gt; editoriais** de Cercle 2 — da mesma forma que os enquadramentos de`office/pilotage/` ou as formações de`office/formations/`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;AVANT                            APRÈS
foundry/                office/
  cheroliv.com/                    sites/
    site/jbake/content/blog/         cheroliv.com/
      2026/0117_....adoc               2026/0117_....adoc&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O que muda :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Os artigos estão em`office/`→ confidencialidade do Círculo 2&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;A tarefa`publishSite`torna-se uma capacidade de`engine`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;via`bakery-gradle`— o plugin recebe um`FileTree`, ele não sabe que os artigos venham de`office/`&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Um único CI, uma única governança — zero duplicação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O classificador Vision/Opinion (Regra 2bis) aplica-se automaticamente:&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;os artigos em`office/`são filtrados antes da publicação&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_bakery_gradle_não_liga&amp;quot;&amp;gt;bakery-gradle não liga&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E é isso que é bonito. O plugin`bakery-gradle`não foi modificado. Ele recebe um diretório de conteúdo AsciiDoc, ele gera HTML, ele faz push no GitHub Pages. Que esse diretório se chame`site/jbake/content/` ou &amp;lt;code&amp;gt;office/sites/cheroliv/&amp;lt;/code&amp;gt;— o plugin não se importa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// engine/build.gradle.kts
task(&amp;quot;publishBlog&amp;quot;) {
    doLast {
        val articles = fileTree(&amp;quot;../../office/sites/cheroliv/2026/&amp;quot;)
        bakery.generate(articles)  // ← bakery ne sait pas d&amp;#39;où ça vient
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O contrato de interface é uma tarefa Gradle. Não é uma API REST. Não é um webhook. Uma tarefa — tipada, testável, executável localmente e na CI.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_impacto_sobre_o_agente_de_governança&amp;quot;&amp;gt;Impacto sobre o Agente de Governança&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A granularização OSS/CSS altera a governança do agente em três pontos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;RAG public&amp;lt;/strong&amp;gt; : o perímetro de indexação passa de`foundry/**`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;à &amp;lt;code&amp;gt;foundry/public/&amp;lt;strong&amp;gt;&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;office/Vision/&amp;lt;/strong&amp;gt;&amp;lt;/code&amp;gt;. O código closed source é excluído do índice por configuração de tarefa, não por prompt.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Knowledge Graph&amp;lt;/strong&amp;gt; : o graphify-gradle produz dois grafos :&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;*&amp;lt;code&amp;gt;office/graph.json&amp;lt;/code&amp;gt;(public) — relações entre artefatos OSS + office/Vision *&amp;lt;code&amp;gt;configuration/graph_private.json&amp;lt;/code&amp;gt;(gitignored, privado) — relações entre artefatos CSS, nunca publicado&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;ajuste fino do conjunto de dados&amp;lt;/strong&amp;gt; :`codebase-gradle`agora tem duas fontes :&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;*&amp;lt;code&amp;gt;OSS/&amp;lt;/code&amp;gt;→ datasets públicos (modelos comunitários, benchmarks) *&amp;lt;code&amp;gt;CSS/&amp;lt;/code&amp;gt;→ datasets privados (modelo interno, fine-tuning proprietário)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_que_se_ganha_e_o_que_se_perde&amp;quot;&amp;gt;O Que Se Ganha (E O Que Se Perde)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1436%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Antes (pasta plana)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Depois (OSS/CSS + office/sites)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;ls foundry/&amp;lt;/code&amp;gt;→ mistura público/privado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;ls foundry/public/&amp;lt;/code&amp;gt;→ tudo é publicável&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;RAG indexa tudo sem discriminação&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;RAG indexa`OSS/` + `office/Vision`exclusivamente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Impossível saber se um projeto é open source&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O caminho`OSS/` ou `CSS/`o dito&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;cheroliv.com tem sua própria CI, sua própria governança&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;A CI e a governança são mutualizadas no engine&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Os artigos de blog estão em um repositório &amp;quot;code&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Os artigos do blog estão em`office/sites/`— dados, não código&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Risco de vazamento de código fechado em embeddings públicos&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Estruturalmente impossível —`CSS/`fora do escopo RAG&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O único custo : um`git mv`global sobre os 17 repositórios OSS + um refatoramento do &amp;lt;code&amp;gt;build.gradle.kts&amp;lt;/code&amp;gt; de &amp;lt;code&amp;gt;engine&amp;lt;/code&amp;gt;. É uma migração a frio Sem dados em voo, nenhum usuário impactado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_o_sinal_físico_bate_o_sinal_de_software&amp;quot;&amp;gt;Conclusão: o sinal físico bate o sinal de software&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O que eu fiz nesta tarde foi substituir uma convenção invisível por uma separação visível na zona`foundry/&amp;lt;code&amp;gt;. Antes, você tinha que &amp;lt;strong&amp;gt;saber&amp;lt;/strong&amp;gt; que`edster/`era closed source. Agora você &amp;lt;strong&amp;gt;vê&amp;lt;/strong&amp;gt; — o dossiê se chama`CSS/&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É o mesmo princípio que as permissões Unix, os VLAN de rede, ou os compartimentos à prova de fogo em uma base de dados. A segurança não deve ser uma informação que você deve reter. Ela deve ser uma propriedade &amp;lt;strong&amp;gt;física&amp;lt;/strong&amp;gt;do sistema.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A granularização OSS/CSS é a tradução deste princípio no domínio da governança LLM. O LLM não precisa saber que o Edster é Confidencial. Ele precisa ser incapaz de indexá-lo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E isso é exatamente o que`CSS/`garante.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_referências&amp;quot;&amp;gt;Referências&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0118_matrice_gouvernance_llm_zone_acces_workspace_post.html&amp;quot;&amp;gt;Artigo 0117 — A Matriz de Governança LLM&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0114_gouvernance_cercles_confiance_ontologie_spatiale_alignement_llm_post.html&amp;quot;&amp;gt;Artigo 0114 — Os Círculos de Confiança&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0116_compartimentage_epistemique_automatise_llm_post.html&amp;quot;&amp;gt;Artigo 0116 — O Compartimentamento Epistêmico Automatizado&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;0108_gouvernance_agent_opencode_eager_lazy_post.html&amp;quot;&amp;gt;Artigo 0108 — Governar um agente de IA com AsciiDoc&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>O Compartimentamento Epistêmico Automatizado — Como o LLM protege meu jardim mental</title>
            <link >https://cheroliv.com//blog/2026/0116_compartimentage_epistemique_automatise_llm_post.html</link>
            <pubDate>Sat, 2 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0116_compartimentage_epistemique_automatise_llm_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_pensar_livremente_sem_publicar_qualquer_coisa&amp;quot;&amp;gt;O Problema: Pensar Livremente sem Publicar Qualquer Coisa&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_padrão_stimulus_nascer_fixar_diluir_morrer&amp;quot;&amp;gt;O padrão STIMULUS — nascer, fixar, diluir, morrer&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_delegação_ao_llm_por_que_o_llm_é_quem_filtra&amp;quot;&amp;gt;A Delegação ao LLM: Por que o LLM é quem filtra&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 10 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Meu LLM não se limita a executar minhas tarefas Gradle. Ele é o guardião da fronteira entre meu pensamento privado e minha expressão pública. Cada ideia é classificada — Visão, Opinião, Estratégia — antes de ser encaminhada. Eis como esse compartimentamento epistemológico automatizado protege meu jardim mental sem me calar.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_problema_pensar_livremente_sem_publicar_qualquer_coisa&amp;quot;&amp;gt;O Problema: Pensar Livremente sem Publicar Qualquer Coisa&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando um desenvolvedor solo documenta seu processo de pensamento com um LLM, ele se depara com um paradoxo:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;CAPTURE TOUT (Cercle 0, jardin secret)
  ├→ Brain dump libre, pas de censure
  ├→ Idées brutes, spéculations, intuitions non vérifiées
  ├→ Allers-retours LLM : challenger, itérer, raffiner
  └→ Le LLM voit TOUT — le bon, le flou, l&amp;#39;embryonnaire

PUBLICATION SÉLECTIVE (Cercle 4, blog public)
  ├→ Uniquement les décisions actées
  ├→ Uniquement les patterns testés (&amp;amp;gt;100 tests PASS)
  ├→ Uniquement ce qui a de la valeur pour le lecteur
  └→ JAMAIS une spéculation présentée comme une vérité&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Entre esses dois extremos, há um&amp;lt;strong&amp;gt;filtro&amp;lt;/strong&amp;gt;. A pergunta é: quem faz isso? Um humano cansado no final da sessão? Um script de regex ingênuo? Ou o próprio LLM, que já leu tudo?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Minha resposta: o LLM.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_padrão_stimulus_nascer_fixar_diluir_morrer&amp;quot;&amp;gt;O padrão STIMULUS — nascer, fixar, diluir, morrer&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O STIMULUS é um arquivo`.adoc`temporário na raiz do workspace (Círculo 0). Seu nome segue uma convenção evocativa —&amp;lt;code&amp;gt;PICTURE_ME_ROLLIN.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;HOLD_MY_BEER.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ONCE_UPON_A_TIME.adoc&amp;lt;/code&amp;gt;— que captura a intenção exploratória do momento.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Seu ciclo de vida é implacável :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;CRÉATION (Cercle 0)
  ├→ Fichier vide, brain dump libre
  ├→ Allers-retours LLM ← → développeur
  └→ Aucune pression de structure ou de publication

MATURATION
  ├→ Le contenu se densifie
  ├→ Des sections émergent naturellement
  └→ Le fichier devient une référence informelle de session

CLASSIFICATION ÉPISTÉMIQUE ← FAIT PAR LE LLM
  ├→ VISION    → publiable, diluer dans les docs publics + blog
  ├→ OPINION   → confiner au Cercle 0, pas de dilution externe
  └→ STRATÉGIE → dilution restreinte (docs racine, PAS de blog)

DILUTION (Cascade vers Cercles légitimes)
  ├→ VISION → WORKSPACE_AS_PRODUCT.adoc + WHAT_THE_GAMES.adoc + blog
  ├→ STRATÉGIE → WORKSPACE_AS_PRODUCT.adoc (section business)
  └→ OPINION → reste dans le stimulus, ne bouge pas

ARCHIVAGE + SUPPRESSION
  ├→ Snapshot dans configuration/vision-archive/ (trace historique)
  └→ rm du stimulus — il a fini son travail&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um estímulo que sobrevive a mais de 2 sessões sem diluição é um anti-padrão. Ou a informação é muito importante para apodrecer em um arquivo temporário (→ diluir), ou ela é muito vaga para merecer existir (→ excluir).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_delegação_ao_llm_por_que_o_llm_é_quem_filtra&amp;quot;&amp;gt;A Delegação ao LLM: Por que o LLM é quem filtra&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não faço hardcode da classificação Vision/Opinion em Kotlin. Eu a injeto no&amp;lt;strong&amp;gt;prompt do sistema&amp;lt;/strong&amp;gt;do LLM via`AGENT_GOVERNANCE.adoc` :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;----
----
Pour chaque dilution de stimulus ou session, tu es responsable de classifier
le contenu :
- VISION : décision architecturale actée, pattern testé en production (&amp;amp;gt;100 tests PASS),
  décision documentée dans BACKLOG ou ROADMAP
- OPINION : spéculation, hypothèse non validée par des tests, intuition
  sans ancrage expérimental
- STRATEGIE : positionnement business, pricing, marché cible

Règles :
- Si OPINION, le contenu reste confiné dans son cercle d&amp;#39;origine
- Si VISION, tu procèdes à la dilution dans les documents racine
- Si STRATEGIE, dilution restreinte (docs racine, pas de blog public)
- Cette classification détermine aussi si un article de blog est publiable
  sur cheroliv.com
----

O LLM lê esta diretiva**antes**de tratar a sessão. Ela está em seu contexto imediato, não em um arquivo separado. É de *prompt engineering determinístico*: o comportamento surge do texto injetado, não de um classificador externo.

=== Por que Funciona — e Por que é Contra-intuitivo

A reação padrão : « Tu confias a classificação à IA ? E se ela errar ?

Resposta: o ser humano não faz melhor às 2h da manhã. A diferença, é que o LLM, ele, lê TUDO. Ele tem o contexto completo da sessão e dos documentos-alvo. Ele sabe que`VPS ECO-16 12€ HT`é uma decisão de infraestrutura (VERSION) enquanto`« le TTS ElevenLabs sonne plus naturel que Coqui »`é uma opinião não verificada por um benchmark.

O ser humano, no final da sessão, lê na diagonal e faz o melhor que pode. O LLM lê exaustivamente e aplica uma grade sistemática. O resultado é**mais confiável**, não menos.

E para os 20% de casos ambíguos — a zona cinzenta onde mesmo o LLM hesita — a resposta não é « adicionar código ». A resposta é**LangGraph4j**(EPIC 13) : um grafo de estado que, para os casos ambíguos, ativa um nó Human-in-the-Loop e eleva ao humano para decisão. Mas para os 80% de casos claros, o LLM basta.

== O Furo Completo: do Brain Dump ao Blog Post

Segue o fluxo completo — da ideia bruta ao artigo publicado — com o LLM como único filtro :

[source]
----
CERCLE 0 (jardin secret)
  PICTURE_ME_ROLLIN.adoc   ← brain dump, allers-retours LLM
       ↓
  [LLM classifie] → VISION / OPINION / STRATÉGIE
       ↓
  VISION → dilution dans WORKSPACE_AS_PRODUCT.adoc
         → dilution dans WHAT_THE_GAMES_BEEN_MISSING.adoc
         → dilution dans WORKSPACE_VISION.adoc (session context)
         → RÉDACTION ARTICLE BLOG ← le LLM écrit, pas le dev
       ↓
CERCLE 4 (blog public)
  0116_compartimentage_epistemique.adoc  ← publié sur cheroliv.com
       ↓
  ./gradlew deploy (bakery-gradle → JBake → JGit → GitHub Pages)
       ↓
  https://cheroliv.com/blog/2026/0116/  ← live
----

O desenvolvedor não escreve o artigo. Ele desafia, relê, valida. O LLM escreve. O loop é :

1. Brain dump livre → sem censura
2. Classificação epistêmica pelo LLM → sem vazamento
3. Redação pelo LLM → sem procrastinação
4. Revisão humana → sem erro

O gargalo histórico — « não tenho tempo para escrever » — foi eliminado. O LLM faz o grosso do trabalho. O ser humano faz o que só o ser humano pode fazer : julgar.

== Os Três Documentos Racine — Como a Informação Encontra Seu Lugar

Cada documento alvo tem um papel específico na cascata de diluição :

[cols=&amp;quot;1,3,2&amp;quot;]
|===
|Documento |O que ele recebe |Tipo de conteúdo |`WORKSPACE_AS_PRODUCT.adoc` |Decisões arquiteturais, pilha técnica, mapa de rota, modelo de negócio |VISÃO + ESTRATÉGIA |`WHAT_THE_GAMES_BEEN_MISSING.adoc` |padrões, lições, anti-padrões, ontologie, regras formais |VISION (padrões testados) |`WORKSPACE_VISION.adoc` |Contexto da sessão, histórico das decisões, pontos de infraestrutura |VISÃO + ESTRATÉGIA (contexto) |`AGENT_GOVERNANCE.adoc` |Regras de governança, portfólio de projetos, procedimentos |VISÃO (governança) |**Blog cheroliv.com** |Artigos públicos, valor para o leitor externo |VISÃO apenas
|===

Um mesmo estímulo pode alimentar os 4 documentos + o blog — cada destino recebe a parte que lhe corresponde, sem redundância, sem contradição.

É o que aconteceu para`PICTURE_ME_ROLLIN.adoc`(3 maio 2026) :

| Informação | Classificação | Destinos | | Inventário dos 13+ projetos foundry/ | VISION | WORKSPACE_AS_PRODUCT | | VPS-2 OVH, edster.cloud, 8,49€/mês | VISION | WORKSPACE_AS_PRODUCT + WORKSPACE_VISION |  O mercado africano está pronto para o mobile money » | ESTRATÉGIA | WORKSPACE_AS_PRODUCT (seção de negócios) | « DeepSeek &amp;amp;gt; Kimi &amp;amp;gt; GLM para contexto longo » | OPINIÃO → VISÃO | Confinado e depois promovido após benchmark (artigo 0112) | | Funcionário Sintético como produto final | ESTRATÉGIA | WORKSPACE_AS_PRODUCT (sem blog) |

== O Padrão Baby-Step + TestContainer + Documento Vivo — o Código que se testa a si mesmo

O padrão STIMULUS/Dilution gerencia o pensamento. Seu equivalente para o código é o padrão Baby-Step/TestContainer/Doc Vivo, validado pelo plantuml-gradle (380/380 testes PASS, 137 sessões sem vazamento Docker):

[cols=&amp;quot;1,3&amp;quot;]
|===
|Princípio |Regra |**Passo de bebê** |Nunca mais de 3 pts por US. Se uma US assustar, dividir. Uma US de 1 pt que passa &amp;amp;gt; uma US de 8 pts que arrasta. |**TestContainer primeiro** |Antes do código de negócio, o contêiner. O teste de infra passa → ambiente estável garantido. |**Dataset comitê** |Subconjunto mínimo porém representativo em`src/test/resources/`, sem PII. Clonável, lançável. |**documentação viva** |Cada teste Cucumber = uma frase em linguagem natural. O código-fonte É a especificação executável. Zero documentação Markdown para manter. |**Limpeza automática** |Zero vazamento de recursos. Contêineres órfãos,`/tmp/gradle-test-*`Tudo está limpo.
|===

Este padrão é a razão pela qual o plantuml-gradle nunca teve uma regressão Docker em 138 sessões. Ele garante que o código que fala com uma infraestrutura externa (PostgreSQL, Redis, Kafka) seja sempre testado em um ambiente idêntico ao da produção — versionado, reproduzível e lançável com um único comando.

== Conclusão: A Liberdade de Pensar sem o Medo de Publicar

O compartimentamento epistemológico automatizado não é uma ferramenta de censura. É um**libertador**. Ele permite pensar livremente, especular sem filtro, despejar tudo o que passa pela cabeça — sem jamais temer que uma ideia embrionária apareça no blog.

O LLM é o guardião dessa fronteira. Ele lê tudo. Ele classifica. Ele dilui o que merece sobreviver. Ele confina o que deve permanecer privado. Ele redige o que é publicável.

E o desenvolvedor, ele, tem a última palavra: reler, desafiar, validar. Julgar.

É a divisão do trabalho que faltava ao desenvolvedor solo: um empregado sintético que cuida da burocracia mental, para que o ser humano possa se concentrar no que faz de melhor — projetar, arquitetar, decidir.

== Referências

* Artigo sobre a governança do agente Eager/Lazy :link:../2026/0108_gouvernance_agent_opencode_eager_lazy_post.html[Governar um Agente IA com AsciiDoc]
* Artigo sobre ontologia espacial e os círculos de confiança :link:../2026/0114_gouvernance_cercles_confiance_ontologie_spatiale_alignement_llm_post.html[A Ontologia Espacial como Mecanismo de Alinhamento]
* Artigo sobre o Anonimizador MVP0 :link:../2026/0115_anonymiseur_dataset_mvp0_realite_augmentee_llm_post.html[Nosso Primeiro Funcionário: o Anonimizador de Datasets]
* Artigo sobre a comparação de LLMs longo contexto :link:../2026/0112_comparaison_kimi_glm_deepseek_long_contexte_plugin_gradle_opencode_post.html[DeepSeek-V4-Pro, Kimi K2.6, GLM-5.1]
* Artigo sobre o mecanismo do agente de backup:link:../2026/0110_mecanisme_backup_contexte_agent_post.html[Sliding Window e Onda Fria]
----&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Nossa Primeira Contratação: o Anonimizador de Datasets — MVP0 no Período de Teste</title>
            <link >https://cheroliv.com//blog/2026/0115_anonymiseur_dataset_mvp0_realite_augmentee_llm_post.html</link>
            <pubDate>Fri, 1 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0115_anonymiseur_dataset_mvp0_realite_augmentee_llm_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_que_faz_fugir_todo_o_mundo&amp;quot;&amp;gt;O Problema que Faz Fugir Todo o Mundo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 12 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Não contrato um LLM. Contrato um anonimizador. Antes de fazer &amp;lt;code&amp;gt;RAG pgvector&amp;lt;/code&amp;gt;, antes de fazer fine-tuning de especialistas &amp;lt;code&amp;gt;CDA/FPA&amp;lt;/code&amp;gt;, antes de provisionar workspaces de clientes — alguém deve limpar os banheiros dos dados. Este funcionário é o especialista em anonimização (&amp;lt;code&amp;gt;MVP0&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;EPIC 2&amp;lt;/code&amp;gt; de &amp;lt;code&amp;gt;codebase-gradle&amp;lt;/code&amp;gt;). E é ele quem torna o produto comercializável.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_problema_que_faz_fugir_todo_o_mundo&amp;quot;&amp;gt;O Problema que Faz Fugir Todo o Mundo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Toda startup de IA que manipule dados de treinamento bate no mesmo muro :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;----
----
Données brutes client (SPG, cours, évaluations)
  ├→ Noms de stagiaires en clair dans les PDF
  ├→ Emails dans les métadonnées JSON
  ├→ IP de connexion dans les logs pgvector
  ├→ Identifiants dans les nœuds Graphify
  └→ Références OF pilote dans les schémas SQL
----

Resultado: ou você faz RAG em dados sujos (alucinações de PII, não conformidade com GDPR, risco jurídico), ou você não faz nada (sem produto). A terceira via — limpar manualmente — não é escalável.

Minha resposta: não contornar o problema. L&amp;#39;**automatizar**.

== MVP0: O Anonimizador, Primeiro Contratado no Período de Experiência

O MVP0 não é uma funcionalidade. É um**especialista em conveniência**— um funcionário que faz o trabalho sujo, estúpido e malvado, e que ninguém quer falar nos pitch decks.

O que ele faz:

[source]
----
ENTRÉE : Données brutes multi-sources
  ├→ AsciiDoc (SPG_A2SP.adoc) → noms, dates, OF pilote
  ├→ JSON (catalogue formations) → emails, téléphones
  ├→ YAML (config workspace) → tokens OAuth2
  ├→ SQL (schémas DDL) → IP, adresses
  ├→ pgvector (métadonnées embeddings) → identifiants
  ├→ ONNX (outputs classification) → noms propres
  └→ Graphify (graph.json) → nœuds avec PII

SORTIE : Datasets propres, format standardisé
  ├→ [ANONYMIZED] remplace les PII détectées
  ├→ Classification RGPD (niveau 0→4)
  ├→ Rapport d&amp;#39;audit (quoi a été nettoyé)
  └→ Dataset prêt pour RAG ET fine-tuning
----

Ele detecta os padrões mais óbvios primeiro (e-mails`.*@.*`, tokens`sk-*`, `ghp_*`, IPs), então ele aprende. O período de teste, isso é: validamos que ele não deixa passar falsos negativos antes de efetivá-lo.

== O que esse MVP desbloqueia (e não é apenas a conformidade)

A anonimização como MVP0 não é o pedágio RGPD antes da autoestrada. É a própria autoestrada. Veja o que ela abre:

=== Presente: Realidade Aumentada em Prompt

[source]
----
DONNÉES BRUTES CLIENT
    ↓
Anonymiseur MVP0 → datasets propres
    ↓
RAG pgvector (MVP1) → top-K documents similaires (filtrés, anonymisés)
    ↓
LLM (deepseek-v4-pro) → réponse augmentée, zéro PII
----

O RAG não procura em dados sujos — ele procura em um espaço**limpo por construção**Isso muda tudo para a qualidade das respostas: o LLM não perde tempo a contornar PII que ele reconhece, mas que não deve mencionar.

=== Futuro : Fine-Tuning em Dados Saudáveis

[source]
----
MVP0 → datasets propres accumulés (session après session)
    ↓
Fine-tuning expert métier (CDA, FPA) sur données zéro PII
    ↓
Modèle fine-tuné exposé via Ollama (sans fuite de données)
----

O fine-tuning em dados brutos é uma catástrofe jurídica em potencial. Em dados limpos, é uma**método reprodutível**. Cada cliente acumula os seus conjuntos de dados limpos, cada cliente pode afinar os seus especialistas de negócio nos seus dados — sem nunca expor um dado pessoal.

== Por que é vendável

Não é « uma ferramenta de conformidade GDPR ». É uma**Método reprodutível que transforma um problema universal em ativo**:

1. *Toda empresa de formação tem dados sujos* — isso é o problema
2. *Nenhuma tem um pipeline automatizado de limpeza multi-fonte*—é o mercado
3. *O ciclo: anonimização → RAG → fine-tuning é proprietário* — é a barreira

O SaaS Edster (MVP3) não venderá « um workspace Gradle ». Ele venderá esse loop fechado. E o MVP0 é a porta de entrada.

== O Formato SQL como Terceira Via de Contexto

A importação de dados estruturados não é apenas um formato de troca. O esquema SQL (DDL) leva o**modelo de entidade-relacionamento do domínio**:

[source]
----
CREATE TABLE formation (
  id UUID PRIMARY KEY,
  titre VARCHAR NOT NULL,
  referentiel RNCP REFERENCES rncp(id),
  organisme OF REFERENCES of_pilote(id)  -- ← va être anonymisé
);
----

Este DDL, uma vez anonimizado (linha 4), torna-se uma fonte de contexto para o LLM. Não é texto — do**modelo de negócio**. O RAG fornece o conteúdo (similaridade vetorial), Graphify fornece as relações (estrutura exata), e o esquema SQL fornece o**Design orientado por domínio implícito**: contextos delimitados, agregados, objetos de valor. O LLM compreende o negócio porque ele lê seu esquema.

== A Roadmap: do MVP0 ao SaaS

[source]
----
MVP0 — Anonymiseur (ce que cet article décrit)
  Sortie : datasets propres, reproductible

MVP1 — RAG pgvector (dépend de MVP0)
  Realité augmentée en prompt sur données nettoyées

MVP2 — Graphify + ONNX + SQL DDD (dépend de MVP1)
  Vecteur composite de contexte (règles + similarité + relations exactes)

MVP3 — SaaS Edster (dépend de MVP0-2)
  Provisionnement workspace client complet
----

Cada MVP depende do anterior. Nenhum pode ser entregue sem o MVP0. Esta é a razão pela qual o Anonimizador não é « P0 » no backlog — ele é**MVP0**. É o primeiro entregável comercial, e tudo o resto está condicionado ao seu sucesso.

== Conclusão: o Trabalho Invisível que Torna Tudo Possível

Em uma startup clássica, a limpeza de dados é terceirizada, manual ou ignorada. Nesta arquitetura, é o**coração do produto**. O Anonimizador é o primeiro empregado porque, sem ele, nada funciona:

* Sem MVP0 → não há RAG legal (MVP1)
* Sem MVP0 → sem fine-tuning sem vazamento de dados (EPIC 5)
* Sem MVP0 → não há SaaS credível (MVP3)
* Sem MVP0 → não há realidade aumentada no prompt
* Sem MVP0 → não há terceira via de contexto (SQL DDD)

Ele faz o trabalho sujo. Nunca será citado nos pitches. Mas é ele quem faz a fábrica funcionar.

E é por isso que ele/ela é contratado primeiro.

== Referências

* Artigo sobre a ontologia espacial e os círculos de confiança :link:../2026/0114_gouvernance_cercles_confiance_ontologie_spatiale_alignement_llm_post.html[A Ontologia Espacial como Mecanismo de Alinhamento]
* Artigo sobre o agente de governança Eager/Lazy:link:../2026/0108_gouvernance_agent_opencode_eager_lazy_post.html[Governar um agente de IA com AsciiDoc]
* Artigo sobre o mecanismo Hot/Warm/Cold:link:../2026/0110_mecanisme_backup_contexte_agent_post.html[Janela deslizante e onda fria]
* Artigo sobre a comparação dos três LLMs :link:../2026/0112_comparaison_kimi_glm_deepseek_long_contexte_plugin_gradle_opencode_post.html[DeepSeek-V4-Pro, Kimi K2.6, GLM-5.1]
----&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Nosso Primeiro Contratado : o Anonimizador de Datasets — MVP0 em Período de Teste</title>
            <link >https://cheroliv.com//blog/pilot-batch/0115_anonymiseur_dataset_mvp0_realite_augmentee_llm_post.html</link>
            <pubDate>Fri, 1 May 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/pilot-batch/0115_anonymiseur_dataset_mvp0_realite_augmentee_llm_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_que_faz_todo_mundo_fugir&amp;quot;&amp;gt;O problema que faz todo mundo fugir&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 12 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Não contrato um LLM. Contrato um anonimizador. Antes de fazer o RAG pgvector, antes de afinar os especialistas CDA/FPA, antes de provisionar os workspaces dos clientes — alguém precisa limpar o banheiro dos dados. Esse funcionário é o especialista em anonimização (MVP0, EPIC 2 do código-base gradle). E é ele que torna o produto vendável.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[empty string]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_problema_que_faz_todo_mundo_fugir&amp;quot;&amp;gt;O problema que faz todo mundo fugir&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Qualquer startup de IA que manipula dados de treinamento se depara com o mesmo muro:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;----
----
Données brutes client (SPG, cours, évaluations)
  ├→ Noms de stagiaires en clair dans les PDF
  ├→ Emails dans les métadonnées JSON
  ├→ IP de connexion dans les logs pgvector
  ├→ Identifiants dans les nœuds Graphify
  └→ Références OF pilote dans les schémas SQL
----

Resultado: ou você faz RAG em dados sujos (alucinações de PII, não conformidade GDPR, risco jurídico), ou você não faz nada (sem produto). A terceira via — limpar à mão — não é escalável.

Minha resposta: não contornar o problema. O**automatizar**.

== MVP0 : O Anonimizador, Primeiro Contratado em Período de Experiência

O MVP0 não é uma funcionalidade. É um**especialista de conveniência**— um funcionário que faz o trabalho sujo, estúpido e malévolo, e que ninguém quer falar nos pitch decks.

O que ele faz :

[source]
----
ENTRÉE : Données brutes multi-sources
  ├→ AsciiDoc (SPG_A2SP.adoc) → noms, dates, OF pilote
  ├→ JSON (catalogue formations) → emails, téléphones
  ├→ YAML (config workspace) → tokens OAuth2
  ├→ SQL (schémas DDL) → IP, adresses
  ├→ pgvector (métadonnées embeddings) → identifiants
  ├→ ONNX (outputs classification) → noms propres
  └→ Graphify (graph.json) → nœuds avec PII

SORTIE : Datasets propres, format standardisé
  ├→ [ANONYMIZED] remplace les PII détectées
  ├→ Classification RGPD (niveau 0→4)
  ├→ Rapport d&amp;#39;audit (quoi a été nettoyé)
  └→ Dataset prêt pour RAG ET fine-tuning
----

Ele detecta os padrões mais óbvios primeiro (emails`.*@.*`, tokens`sk-*`, `ghp_*`, IPs), então ele aprende. O período de teste, isso é: validamos que ele não deixa passar falsos negativos antes de o titularizar.

== O Que Este MVP Desbloqueia (e Não é Apenas Conformidade)

A anonimização como MVP0 não é o pedágio RGPD antes da autoestrada. É a própria autoestrada. Aqui está o que ela abre:

=== Presente: Realidade Aumentada em Prompt

[source]
----
DONNÉES BRUTES CLIENT
    ↓
Anonymiseur MVP0 → datasets propres
    ↓
RAG pgvector (MVP1) → top-K documents similaires (filtrés, anonymisés)
    ↓
LLM (deepseek-v4-pro) → réponse augmentée, zéro PII
----

O RAG não busca em dados sujos — ele busca em um espaço**limpo por construção**. Mudar isso muda tudo para a qualidade das respostas: o LLM não perde tempo contornando PII que reconhece, mas que não deve mencionar.

=== Futuro : Fine-Tuning em Dados Saudáveis

[source]
----
MVP0 → datasets propres accumulés (session après session)
    ↓
Fine-tuning expert métier (CDA, FPA) sur données zéro PII
    ↓
Modèle fine-tuné exposé via Ollama (sans fuite de données)
----

O fine-tuning em dados brutos é uma catástrofe jurídica em potencial. Em dados limpos, é uma**método reprodutível**. Cada cliente acumula seus datasets limpos, cada cliente pode ajustar finamente seus especialistas de negócio nos seus dados — sem nunca expor um dado pessoal.

== Por que isso é mercadável

Não é « uma ferramenta de conformidade com o RGPD ». É uma**método reprodutível que transforma um problema universal em ativo**:

1. *Toda empresa de treinamento tem dados sujos* — esse é o problema
2. *Nenhuma tem um pipeline automatizado de limpeza multi-fonte* — é o mercado
3. *O ciclo : anonimização → RAG → ajuste fino é proprietário* — é a barrière

O SaaS Edster (MVP3) não venderá « um workspace Gradle ». Ele venderá esse loop fechado. E o MVP0 é a porta de entrada.

== O Formato SQL como Terceira Via de Contexto

A importação de dados estruturados não é apenas um formato de troca. O esquema SQL (DDL) carrega o**modelo entidade-relacionamento do domínio**:

[source]
----
CREATE TABLE formation (
  id UUID PRIMARY KEY,
  titre VARCHAR NOT NULL,
  referentiel RNCP REFERENCES rncp(id),
  organisme OF REFERENCES of_pilote(id)  -- ← va être anonymisé
);
----

Este DDL, uma vez anonimizado (linha 4), torna-se uma fonte de contexto para o LLM. Não é texto —**modelo de negócio**. O RAG fornece o conteúdo (similaridade vetorial), Graphify fornece as relações (estrutura exata), e o esquema SQL fornece o**Domain-Driven Design implícito**: bounded contexts, agregados, value objects. O LLM compreende o negócio porque ele lê seu esquema.

== Roadmap: do MVP0 ao SaaS

[source]
----
MVP0 — Anonymiseur (ce que cet article décrit)
  Sortie : datasets propres, reproductible

MVP1 — RAG pgvector (dépend de MVP0)
  Realité augmentée en prompt sur données nettoyées

MVP2 — Graphify + ONNX + SQL DDD (dépend de MVP1)
  Vecteur composite de contexte (règles + similarité + relations exactes)

MVP3 — SaaS Edster (dépend de MVP0-2)
  Provisionnement workspace client complet
----

Cada MVP depende do anterior. Nenhum pode ser entregue sem o MVP0. Esta é a razão pela qual o Anonymiseur não é « P0 » no backlog — ele está**MVP0**. É o primeiro entregável comercial, e tudo o resto está condicionado ao seu sucesso.

== Conclusão: o trabalho invisível que torna tudo possível

Em uma startup clássica, a limpeza de dados é terceirizada, manual ou ignorada. Nesta arquitetura, é o**coração do produto**. O Anonimizador é o primeiro empregado porque sem ele, nada funciona :

* Sem MVP0 → não há RAG legal (MVP1)
* Sem MVP0 → sem ajuste fino sem vazamento de dados (ÉPICO 5)
* Sem MVP0 → sem SaaS credível (MVP3)
* Sem MVP0 → sem realidade aumentada em prompt
* Sem MVP0 → não há terceiro caminho de contexto (SQL DDD)

Ele faz o trabalho sujo. Ele nunca será mencionado nos pitches. Mas é ele quem faz a fábrica funcionar.

É por isso que o contratamos primeiro.

== Referências

* Artigo sobre ontologia espacial e círculos de confiança :link:../2026/0114_gouvernance_cercles_confiance_ontologie_spatiale_alignement_llm_post.html[A Ontologia Espacial como Mecanismo de Alinhamento]
* Artigo sobre a governança do agente Eager/Lazy :link:../2026/0108_gouvernance_agent_opencode_eager_lazy_post.html[Governar um agente IA com AsciiDoc]
* Artigo sobre o mecanismo Hot/Warm/Cold:link:../2026/0110_mecanisme_backup_contexte_agent_post.html[Janela Deslizante e Onda Fria]
* Artigo sobre a comparação dos três LLMs:link:../2026/0112_comparaison_kimi_glm_deepseek_long_contexte_plugin_gradle_opencode_post.html[DeepSeek-V4-Pro, Kimi K2.6, GLM-5.1]
----&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>O Jardim Secreto do Desenvolvedor: Como a Ontologia Espacial Alinha os LLMs Sem Prompt Engineering</title>
            <link >https://cheroliv.com//blog/2026/0114_gouvernance_cercles_confiance_ontologie_spatiale_alignement_llm_post.html</link>
            <pubDate>Thu, 30 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0114_gouvernance_cercles_confiance_ontologie_spatiale_alignement_llm_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_constato_prompt_engineering_é_um_castelo_de_areia&amp;quot;&amp;gt;O Constato : Prompt Engineering é um Castelo de Areia&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_os_quatro_círculos_de_confiança_uma_ontologia_espacial&amp;quot;&amp;gt;Os Quatro Círculos de Confiança : uma Ontologia Espacial&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_jardim_secreto_o_espaço_fora_cvs&amp;quot;&amp;gt;O Jardim Secreto: o Espaço Fora-CVS&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_gitignore_como_configuração_de_governança&amp;quot;&amp;gt;O &amp;lt;code&amp;gt;.gitignore&amp;lt;/code&amp;gt; como Configuração de Governança&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_mecanismo_de_snapshot&amp;quot;&amp;gt;O Mecanismo de Snapshot&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_cofre_fort_configuration_como_spring_cloud_config&amp;quot;&amp;gt;O Cofre-Fort : &amp;lt;code&amp;gt;configuration/&amp;lt;/code&amp;gt; como Spring Cloud Config&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_biblioteca_office_como_dados_consumíveis&amp;quot;&amp;gt;A Biblioteca: &amp;lt;code&amp;gt;office/&amp;lt;/code&amp;gt; como Dados Consumíveis&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_as_forjas_foundry_como_implementação&amp;quot;&amp;gt;As Forjas: &amp;lt;code&amp;gt;foundry/&amp;lt;/code&amp;gt; como implementação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_próscontras_alinhamento_espacial_vs_alinhamento_por_prompt&amp;quot;&amp;gt;Prós/Contras : Alinhamento Espacial vs Alinhamento por Prompt&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_abordagem_clássica_alinhamento_por_pré_prompt&amp;quot;&amp;gt;Abordagem Clássica: Alinhamento por Pré-Prompt&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_esta_abordagem_alinhamento_por_ontologia_espacial&amp;quot;&amp;gt;Esta Abordagem : Alinhamento por Ontologia Espacial&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_álgebra_relacional_do_workspace_e_o_observável_delta&amp;quot;&amp;gt;Álgebra Relacional do Workspace e o Observável Delta&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_vetor_composto_do_contexto_rag_pgvector_graphify&amp;quot;&amp;gt;O Vetor Composto do Contexto: RAG + pgvector + Graphify&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_componente_1_rag_langchain4j_postgresql_pgvector&amp;quot;&amp;gt;Componente 1 — RAG LangChain4j + PostgreSQL pgvector&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_componente_2_knowledge_graph_graphify&amp;quot;&amp;gt;Componente 2 — Knowledge Graph Graphify&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_componente_3_graphify_incremental_esparso_agregável_consumível&amp;quot;&amp;gt;Componente 3 — Graphify Incremental : Esparso, Agregável, Consumível&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_classificação_automática_do_rgpd_o_llm_como_roteador&amp;quot;&amp;gt;Classificação Automática do RGPD : o LLM como Roteador&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_roadmap_do_asciidoc_ao_langgraph4j&amp;quot;&amp;gt;Roadmap : do AsciiDoc ao LangGraph4j&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_esta_arquitetura_resolve_e_o_que_ela_não_resolve&amp;quot;&amp;gt;O que Esta Arquitetura Resolve (e o que ela não Resolve)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão_a_arquitetura_como_discurso&amp;quot;&amp;gt;Conclusão: a Arquitetura como Discurso&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referências&amp;quot;&amp;gt;Referências&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 20 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;A engenharia de prompts é frágil. Com 80.000 tokens, suas regras de alinhamento estão afogadas no ruído, e o LLM esquece o que você pediu no início da conversa. Minha solução? Não alinhar por texto, mas por l&amp;#39;&amp;lt;strong&amp;gt;espaço&amp;lt;/strong&amp;gt;. Estruturei meu workspace de desenvolvimento em quatro círculos de confiança concêntricos — do jardim secreto íntimo até as forjas públicas — e cada círculo é uma área física do sistema de arquivos. O LLM não precisa que se lhe lembre das regras: o caminho do arquivo as contém. Veja como funciona, e por que essa arquitetura de alinhamento por espaço é mais resiliente do que um`system prompt`de 500 linhas&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É um artigo longo.&amp;lt;br&amp;gt;
Acomode-se confortavelmente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_constato_prompt_engineering_é_um_castelo_de_areia&amp;quot;&amp;gt;O Constato : Prompt Engineering é um Castelo de Areia&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Durante três semanas, desenvolvi plugins Gradle com Opencode, usando três LLMs diferentes — Kimi K2.6, GLM-5.1, e DeepSeek-V4-Pro (o único sobrevivente, mas isso é outra história&amp;lt;a href=&amp;quot;0112_comparaison_kimi_glm_deepseek_long_contexte_plugin_gradle_opencode_post.html&amp;quot;&amp;gt;Já coberta aqui.&amp;lt;/a&amp;gt;) Meu método de governança agent&amp;lt;a href=&amp;quot;0108_gouvernance_agent_opencode_eager_lazy_post.html&amp;quot;&amp;gt;Eu a documentei em detalhes em um artigo anterior.&amp;lt;/a&amp;gt; baseia-se em arquivos AsciiDoc —&amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PROMPT_REPRISE.adoc&amp;lt;/code&amp;gt;— que carregam ~30 000 tokens de regras, de backlog e de histórico no início de cada sessão.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E apesar dessa infraestrutura documental, duas coisas me chamaram a atenção:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O LLM esquece.&amp;lt;/strong&amp;gt; Mesmo com as regras absolutas na cabeça de cada arquivo EAGER, além de 60 000 tokens acumulados (contexto inicial + conversa), Kimi K2.6 começou a proposer`Write`esmagadores em arquivos de configuração. GLM-5.1 confundiu um token Firebase com um placeholder a ser substituído. As regras estavam escritas — o LLM não as &amp;lt;strong&amp;gt;via&amp;lt;/strong&amp;gt; mais.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A governança em si se torna um problema.&amp;lt;/strong&amp;gt; Depois de ter implementado o mecanismo Hot/Warm/Cold&amp;lt;a href=&amp;quot;0110_mecanisme_backup_contexte_agent_post.html&amp;quot;&amp;gt;Article sur la rotation de backup.&amp;lt;/a&amp;gt; para evitar a explosão do contexto, meus arquivos EAGER ainda pesavam 1414 linhas&amp;lt;a href=&amp;quot;0111_audit_contexte_agent_post.html&amp;quot;&amp;gt;J&amp;amp;#8217;ai documenté cet audit ici.&amp;lt;/a&amp;gt;. A governança — projetada para proteger o LLM da saturação — saturava o LLM.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eu precisava de um mecanismo de alinhamento que não dependesse do número de tokens no prompt.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A resposta: parar de alinhar pelo texto, e começar a alinhar pelo&amp;lt;strong&amp;gt;espaço&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_os_quatro_círculos_de_confiança_uma_ontologia_espacial&amp;quot;&amp;gt;Os Quatro Círculos de Confiança : uma Ontologia Espacial&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Meu dossiê`workspace/&amp;lt;code&amp;gt;(em&amp;lt;/code&amp;gt;~/workspace/`) &amp;lt;strong&amp;gt;não&amp;lt;/strong&amp;gt; é um repositório Git. É a raiz de todo o meu trabalho — código, documentação, formação, infraestrutura. E está estruturado em quatro áreas que não são convenções de arrumação, mas de&amp;lt;strong&amp;gt;círculos de confiança concêntricos&amp;lt;/strong&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 13.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 20%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 26.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3335%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nível&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Label&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Zona física&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;CVS&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Visibilidade&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Jardim secreto&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`workspace/`raiz&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nenhum&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Íntimo — pensamento livre, sem commit, sem publicação&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Cofre forte&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;configuration/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Git privado (solo)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Segredos, tokens, arquivo de visão. Uma única pessoa.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Biblioteca&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;office/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Git privado(ampliado)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Dados pedagógicos, SPG/SPD, esquemas JSON. Círculo de confiança identificado.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;4&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Forjas (públicas)&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;foundry/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Git público (Apache 2.0)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Código fonte, plugins, testes, documentação técnica.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;NOTA: Não há nível 3 na tabela. O nível 3 é um nível &amp;lt;strong&amp;gt;transitório&amp;lt;/strong&amp;gt;: é conteúdo de`office/`que foi anonimizado e está pronto para ser publicado em open data. Não tem uma zona física própria — é um estado do dado, não um local.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cada nível responde a uma pergunta específica :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Onde depositar uma ideia que ainda não está pronta para ser compartilhada, mesmo com um círculo restrito?&amp;lt;/strong&amp;gt; → Jardim secreto. Sem Git. Sem pressão.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Onde armazenar um token API sem que ele vaze ?&amp;lt;/strong&amp;gt; → O cofre (&amp;lt;code&amp;gt;configuration/&amp;lt;/code&amp;gt;). Fisicamente isolado. Nenhum outro depósito pode referenciá-lo por engano.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Onde co-construir um catálogo de formação com um OF piloto?&amp;lt;/strong&amp;gt; → A biblioteca`office/`). Versionado, colaborativo, mas privado.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Onde industrializar um plugin Gradle open source?&amp;lt;/strong&amp;gt; → As forjas`foundry/`Público, forkável, testado em CI.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta ontologia é&amp;lt;strong&amp;gt;consumível por um LLM&amp;lt;/strong&amp;gt;. Quando ele lê um arquivo dentro`foundry/plantuml-gradle/src/`, ele sabe implicitamente: &amp;quot;estou no círculo 4 — código público, testes obrigatórios, sem segredos, sem dados pedagógicos&amp;quot;. Ele não precisa de um prompt que o lembre.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_jardim_secreto_o_espaço_fora_cvs&amp;quot;&amp;gt;O Jardim Secreto: o Espaço Fora-CVS&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este é o conceito mais importante — e o mais contra-intuitivo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A raiz`workspace/&amp;lt;code&amp;gt;não tem&amp;lt;/code&amp;gt;.git/&amp;lt;code&amp;gt;. Os documentos que vivem lá (`WORKSPACE_VISION.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;WORKSPACE_AS_PRODUCT.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;WORKSPACE_ORGANIZATION.adoc&amp;lt;/code&amp;gt;— e aquele que você está lendo neste momento, que dele provém, não são destinados a serem versionados, compartilhados ou mesmo relidos por alguém além de mim. São&amp;lt;strong&amp;gt;pensamentos em germinação&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;❗&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O jardim secreto é &amp;lt;strong&amp;gt;out-of-CVS&amp;lt;/strong&amp;gt; por natureza. A ausência de versionamento é a condição da liberdade de pensar. Não se escreve para ser lido — escreve-se para esclarecer a própria visão.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas esta liberdade tem um custo: um`rm`acidental, e são meses de reflexão estratégica que desaparecem. A solução não é versionar o jardim (isso seria destruí‑lo) — é de le&amp;lt;strong&amp;gt;espelhar&amp;lt;/strong&amp;gt;no círculo mais restrito&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_o_gitignore_como_configuração_de_governança&amp;quot;&amp;gt;O &amp;lt;code&amp;gt;.gitignore&amp;lt;/code&amp;gt; como Configuração de Governança&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Na raiz de`workspace/&amp;lt;code&amp;gt;, um arquivo&amp;lt;/code&amp;gt;.gitignore`minimal declara o&amp;lt;strong&amp;gt;política de historicização&amp;lt;/strong&amp;gt;alguns ficheiros do jardim secreto :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;.goosehints
.goose&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ce &amp;lt;code&amp;gt;.gitignore`não tem uma função Git clássica (não há&amp;lt;/code&amp;gt;.git/`à raiz). ele age como um&amp;lt;strong&amp;gt;arquivo de configuração de governança&amp;lt;/strong&amp;gt;que responde a uma pergunta específica: &amp;lt;strong&amp;gt;quais artefatos do jardim secreto merecem ser historizados, e quais são puramente transitórios?&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Os arquivos listados em`.gitignore`—&amp;lt;code&amp;gt;.goosehints&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.goose&amp;lt;/code&amp;gt;— são artefatos efêmeros gerados pelos agentes. Sem valor estratégico. Sem historicização.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Os arquivos`.adoc` da raiz —&amp;lt;code&amp;gt;WORKSPACE_VISION.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;WORKSPACE_ORGANIZATION.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;WORKSPACE_AS_PRODUCT.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;WHAT_THE_GAMES_BEEN_MISSING.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;depots_implementes_strategie.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;synthese-LLMs-long-contexte.adoc&amp;lt;/code&amp;gt;— não são&amp;lt;strong&amp;gt;não&amp;lt;/strong&amp;gt;no`.gitignore`. Estes são os artefatos a serem historicizados.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le `.gitignore`é o&amp;lt;strong&amp;gt;esquema de governança&amp;lt;/strong&amp;gt;: tudo o que não está listado lá é um candidato ao snapshot. E o LLM, ao ler esse arquivo, sabe exatamente o que deve ser arquivado e o que pode ser ignorado — sem que seja necessário lembrá-lo nisso em um prompt.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_o_mecanismo_de_snapshot&amp;quot;&amp;gt;O Mecanismo de Snapshot&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eu criei`configuration/vision-archive/&amp;lt;code&amp;gt;: instantâneos datados de todos os&amp;lt;/code&amp;gt;.adoc`da raiz, commits no repositório privado`configuration/`Cada sessão de brainstorming produz um snapshot:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;DATE=$(date +%Y-%m-%d)
mkdir -p /home/cheroliv/workspace/configuration/vision-archive/$DATE
cp /home/cheroliv/workspace/*.adoc /home/cheroliv/workspace/configuration/vision-archive/$DATE/
cd /home/cheroliv/workspace/configuration &amp;amp;amp;&amp;amp;amp; git add vision-archive/ &amp;amp;amp;&amp;amp;amp; \
  git commit -m &amp;quot;vision-archive: snapshot $DATE&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O procedimento é acionado no final da sessão pelo próprio LLM, que executa essa tarefa global de roteamento. Cada snapshot captura o estado completo do pensamento estratégico em um instante T.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O histórico Git de`configuration/`torna-se a&amp;lt;strong&amp;gt;biografia de meu pensamento estratégico&amp;lt;/strong&amp;gt;. Eu posso fazer um`git log&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;vision-archive/`e ver a evolução da minha visão, sessão por sessão, data por data.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo real do histórico após um dia de trabalho :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;$ git -C configuration log --oneline -- vision-archive/
1089d1b vision-archive: fin de session finale 2026-05-03
6b21eaf vision-archive: fin de session 2026-05-03-1300
1690f82 vision-archive: post-article 2026-05-03
28e6593 vision-archive: post-migration 2026-05-03
3707b78 vision-archive: snapshot 2026-05-03 — jardin secret initial&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cinco snapshots em um dia. Cada um é um ponto de restauração. Cada um é um marco na gênese da estratégia.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O dossier`configuration/vision-archive/latest/`contém continuamente uma&amp;lt;strong&amp;gt;cópia de trabalho&amp;lt;/strong&amp;gt;do último snapshot, servindo como referência rápida sem precisar navegar no histórico do Git.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E para o LLM, é um&amp;lt;strong&amp;gt;oráculo de coerência&amp;lt;/strong&amp;gt;: quando ele observa uma contradição entre a implementação atual e a visão arquivada em uma data anterior, ele pode sinalizá-la.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_cofre_fort_configuration_como_spring_cloud_config&amp;quot;&amp;gt;O Cofre-Fort : &amp;lt;code&amp;gt;configuration/&amp;lt;/code&amp;gt; como Spring Cloud Config&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`configuration/`ne contém apenas o arquivo da visão. Sua função primária — e futura — é ser um&amp;lt;strong&amp;gt;servidor Spring Cloud Config&amp;lt;/strong&amp;gt;. Todos os segredos, tokens, credenciais e descritores de infraestrutura vivem aqui, em um repositório Git privado acessível apenas ao proprietário.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Por que um repositório separado em vez de um arquivo`.env`em cada projeto?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Push acidental de um segredo&amp;lt;/strong&amp;gt;: impossível. Os segredos estão em um repositório privado que os projetos públicos não podem referenciar por engano.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Audit&amp;lt;/strong&amp;gt; : o histórico do Git fornece o rastro de cada modificação de configuração. Quem mudou o quê, quando, com qual hash SHA.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Reverter&amp;lt;/strong&amp;gt; :`git revert`Em uma configuração quebrada. Instantâneo, sem backup manual.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_biblioteca_office_como_dados_consumíveis&amp;quot;&amp;gt;A Biblioteca: &amp;lt;code&amp;gt;office/&amp;lt;/code&amp;gt; como Dados Consumíveis&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`office/`o contrapartida documental do trabalho de desenvolvimento. Contém os artigos de blog, as especificações técnicas, os materiais de formação (38 diretórios de cursos FPA, SPG/SPD, esquemas JSON, taxonomias Bloom/Harrow/Krathwohl) — tudo o que constitui o&amp;lt;strong&amp;gt;material didático&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas`office/`Não é apenas uma gaveta de documentos. É uma&amp;lt;strong&amp;gt;fonte de dados estruturados&amp;lt;/strong&amp;gt;que os plugins Gradle de`foundry/`consumem. Um artigo de blog em`office/`é uma entrada que um plugin transformará em HTML. Um SPG AsciiDoc dentro`office/metiers/FPA/`é um artefato que um orquestrador analisará para gerar slides, quiz e cápsulas de vídeo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Et le `build.gradle.kts`na raiz de`office/`não é código de negócio — é um&amp;lt;strong&amp;gt;script de consumo do ecossistema de plugins&amp;lt;/strong&amp;gt;. Ele diz : &amp;quot;aqui estão os plugins Gradle que eu uso, aqui está o contexto do meu workspace&amp;quot;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_as_forjas_foundry_como_implementação&amp;quot;&amp;gt;As Forjas: &amp;lt;code&amp;gt;foundry/&amp;lt;/code&amp;gt; como implementação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;foundry/`contém o&amp;lt;strong&amp;gt;código industrializado&amp;lt;/strong&amp;gt;. 54 repositórios Git, dos quais 7 atualmente governados por&amp;lt;/code&amp;gt;.agents/`. É aqui que a visão se torna executável — plugins Gradle, CI/CD, testes JUnit5 + Cucumber.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A relação com`office/`é bidirecional :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;office/&amp;lt;/code&amp;gt;→&amp;lt;code&amp;gt;foundry/&amp;lt;/code&amp;gt;: os dados pedagógicos são a matéria-prima que os plugins consomem.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;foundry/&amp;lt;/code&amp;gt;(empty)&amp;lt;code&amp;gt;office/&amp;lt;/code&amp;gt;: os plugins produzem dados que enriquecem`office/` — le `graph.json`do graphify-gradle, os decks slider compilados, os relatórios de build.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_próscontras_alinhamento_espacial_vs_alinhamento_por_prompt&amp;quot;&amp;gt;Prós/Contras : Alinhamento Espacial vs Alinhamento por Prompt&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Comparemos as duas abordagens.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_abordagem_clássica_alinhamento_por_pré_prompt&amp;quot;&amp;gt;Abordagem Clássica: Alinhamento por Pré-Prompt&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 20%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 80%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅ Pro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Contra&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Simples de implementar. Um bloco de texto no system prompt.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Frágil ao contexto longo&amp;lt;/strong&amp;gt; : a regra se perde após 80K tokens.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Funciona para regras gerais (tom, formato).&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Não verificável&amp;lt;/strong&amp;gt; : nada impede o LLM de violar a regra.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Não é necessário repensar a arquitetura do projeto.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Não transferível&amp;lt;/strong&amp;gt;: cada repositório redefine as regras.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Puramente declarativo&amp;lt;/strong&amp;gt;: o LLM sabe que ele &amp;lt;strong&amp;gt;deveria&amp;lt;/strong&amp;gt;, mas nada o impede.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_esta_abordagem_alinhamento_por_ontologia_espacial&amp;quot;&amp;gt;Esta Abordagem : Alinhamento por Ontologia Espacial&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 20%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 80%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅ Pro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;contra&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Resiliente a contextos longos&amp;lt;/strong&amp;gt; : a regra está no caminho do arquivo, não em um prompt distante.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Custo de infraestrutura inicial&amp;lt;/strong&amp;gt; : estruturar o workspace em círculos leva tempo.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Verificável mecanicamente&amp;lt;/strong&amp;gt;: um segredo em`foundry/&amp;lt;code&amp;gt;é detectável por`git check-ignore&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Exige disciplina&amp;lt;/strong&amp;gt; : um novo contribuidor deve entender os círculos.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Transferível&amp;lt;/strong&amp;gt; : um`AGENT.adoc`O padrão expõe os círculos a todo novo projeto.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Cobre apenas as restrições espaciais&amp;lt;/strong&amp;gt; — o estilo de código permanece no prompt.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Segurança por design&amp;lt;/strong&amp;gt; : um`git push`desde`foundry/&amp;lt;code&amp;gt;não pode expor`configuration/&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Consumível por agentes não-LLM&amp;lt;/strong&amp;gt; : um orquestrador Gradle pode rotar de acordo com o círculo.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O ganho principal não é defensivo (segurança, visibilidade) — ele é&amp;lt;strong&amp;gt;criativo&amp;lt;/strong&amp;gt;. Quando o LLM trabalha em um espaço estruturado por uma ontologia, ele pode fazer o que nenhum prompt lhe permite: &amp;lt;strong&amp;gt;observar o delta&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_álgebra_relacional_do_workspace_e_o_observável_delta&amp;quot;&amp;gt;Álgebra Relacional do Workspace e o Observável Delta&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando o LLM percorre`foundry/&amp;lt;code&amp;gt;, ele dispõe de um&amp;lt;strong&amp;gt;treliça de dados estruturados&amp;lt;/strong&amp;gt;: nós (plugins, arquivos, testes, dependências), arestas tipadas (`import&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;depends_on&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;generates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tests&amp;lt;/code&amp;gt;), relações de composição e ordem (`training-gradle`extrai o repositório`slider-gradle`gera os slides →`capsule-gradle`reproduz o vídeo).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta álgebra não está escrita em nenhum lugar em texto claro — mas ela é &amp;lt;strong&amp;gt;observável&amp;lt;/strong&amp;gt;. Cada`build.gradle.kts`expõe dependências. Cada`INDEX.adoc`expõe referências cruzadas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O LLM observa esta álgebra e detecta o&amp;lt;strong&amp;gt;delta&amp;lt;/strong&amp;gt;: a diferença entre o que o sistema já sabe produzir e o que a visão descreve.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A partir desse delta, ele pode definir alguns&amp;lt;strong&amp;gt;cartografias de especialistas de negócio&amp;lt;/strong&amp;gt;— CDA (Designer e desenvolvedor de aplicações, Kotlin/Gradle/JHipster) e FPA (Formador Profissional de Adultos, Pedagogie/Qualiopi/Bloom) — e identificar o que falta para cada especialista.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;Expert CDA (Kotlin/Gradle/JHipster)
  ├── Plugins : jhipster-gradle-plugins, plantuml-gradle,
  │   codebase-gradle
  ├── Delta : pas encore de SPG CDA formalisé,
  │   pas de fine-tuning expert CDA

Expert FPA (Pédagogie/Qualiopi/Bloom)
  ├── Plugins : training-gradle, slider-gradle,
  │   school-backoffice/forms, capsule-gradle
  ├── Matériel : 38 modules cours, SPG/SPD, taxonomies
  └── Delta : parser AsciiDoc→JSON à créer,
      orchestrateur à coder&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O LLM não precisa que se lhe diga o que fazer. O delta emerge da estrutura.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_vetor_composto_do_contexto_rag_pgvector_graphify&amp;quot;&amp;gt;O Vetor Composto do Contexto: RAG + pgvector + Graphify&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A álgebra relacional não é uma construção teórica. Ela é &amp;lt;strong&amp;gt;materializada&amp;lt;/strong&amp;gt; por três componentes que formam um&amp;lt;strong&amp;gt;vetor composto de contexto&amp;lt;/strong&amp;gt;para o LLM.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_componente_1_rag_langchain4j_postgresql_pgvector&amp;quot;&amp;gt;Componente 1 — RAG LangChain4j + PostgreSQL pgvector&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Já tenho LangChain4j em produção em dois plugins :`slider-gradle`(4 provedores LLM) e`plantuml-gradle`(7 fornecedores). Os embeddings ONNX (AllMiniLmL6V2) indexam os dados de`office/`e o código fonte de`foundry/`no PostgreSQL + pgvector&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este RAG opera em duas dimensões :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Dados de dimensão&amp;lt;/strong&amp;gt; : os documentos`office/`(SPG, artigos, formações, esquemas JSON)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Código de dimensão&amp;lt;/strong&amp;gt; : as codebases`foundry/`(fontes Kotlin, testes Cucumber, AGENT.adoc)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A interseção cobre tanto o &amp;lt;strong&amp;gt;o quê&amp;lt;/strong&amp;gt; (o domínio de negócio) quanto o &amp;lt;strong&amp;gt;como&amp;lt;/strong&amp;gt; (a implementação).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_componente_2_knowledge_graph_graphify&amp;quot;&amp;gt;Componente 2 — Knowledge Graph Graphify&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Graphify está integrado em`plantuml-gradle`(109 testes, 380/380 PASS) e produz um`graph.json`— um knowledge graph estruturado com nós, arestas e comunidades detectadas automaticamente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao contrário do RAG, que opera por similaridade vetorial (difuso), o knowledge graph opera por&amp;lt;strong&amp;gt;relações exatas&amp;lt;/strong&amp;gt;(determinista) :`graphify query`para as interrogações semânticas (~50 tokens),`graphify path`para navegar,`graphify explain`para explicar&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_componente_3_graphify_incremental_esparso_agregável_consumível&amp;quot;&amp;gt;Componente 3 — Graphify Incremental : Esparso, Agregável, Consumível&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta é a decisão arquitetônica-chave.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Graphify não deve viver em um único repositório. Ele deve viver de forma&amp;lt;strong&amp;gt;esparsa&amp;lt;/strong&amp;gt;em cada plugin:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Cada plugin&amp;lt;/strong&amp;gt; embarca uma tarefa Gradle`updateKnowledgeGraph`que chama Graphify&amp;lt;strong&amp;gt;sobre o seu próprio scope&amp;lt;/strong&amp;gt;e produz um`graph.json`local.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O script de build de`office/&amp;lt;code&amp;gt;&amp;lt;/strong&amp;gt; consome`graphify-gradle`com`rootDir = /home/cheroliv/workspace`e produz um`graph.json&amp;lt;/code&amp;gt; &amp;lt;strong&amp;gt;global&amp;lt;/strong&amp;gt;que agrega os grafos locais.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O RAG de cada plugin&amp;lt;/strong&amp;gt; injeta o`graph.json`global como&amp;lt;strong&amp;gt;filtro de contexto&amp;lt;/strong&amp;gt;para as consultas LLM.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;TÂCHE GRADLE (dans chaque plugin)
    ↓
graphify → graph.json (scope local)
    ↓
office/build.gradle.kts → graph.json (scope global)
    ↓
RAG pgvector (dans slider, plantuml, codebase...)
    ↓  ← injection du graph.json comme filtre
LLM (deepseek-v4-pro)
    ↓  ← observation algèbre relationnelle
    ↓  ← détection delta vs cartographies experts
PRIORISATION → prochaine tâche&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Resultado: o LLM não está procurando no vazio — ele navega em um espaço estruturado pelo knowledge graph. Uma consulta sobre gerar um diagrama consegue alcançar os nós relevantes do grafo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_classificação_automática_do_rgpd_o_llm_como_roteador&amp;quot;&amp;gt;Classificação Automática do RGPD : o LLM como Roteador&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A ontologia espacial não se contente apenas de alinhar o LLM — ela lhe dá uma&amp;lt;strong&amp;gt;grade de classificação GDPR&amp;lt;/strong&amp;gt;para encaminhar cada dado para sua zona legítima&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 12.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 37.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Critério&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Detecção LLM&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Ação&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nível máximo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Dado pessoal (nome, e-mail, IP)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;padrão`@`, IP, nomes próprios&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Anonimizar →`[OF_PILOTE]`ou router nível 2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Token / Segredo / Credencial&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;padrão`sk-&amp;amp;#8230;&amp;amp;#8203;&amp;lt;code&amp;gt;, `ghp_&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ya29&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Roteador →&amp;lt;code&amp;gt;configuration/&amp;lt;/code&amp;gt;. Referenciar por`${VAR}`no código.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;URL interna&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Contém`localhost`, IP privado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;anonimizar →&amp;lt;code&amp;gt;${API_URL}&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Dado pedagógico (SPG, aula)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Estrutura Bloom/Qualiopi&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;roteador`office/`(nível 2). Versão dupla se exportar.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Código fonte / teste&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Extensão`.kt`, &amp;lt;code&amp;gt;.kts`em`foundry/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;roteador →`foundry/`Verificar ausência de segredos&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;4&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No final da sessão, o LLM executa uma&amp;lt;strong&amp;gt;tarefa global&amp;lt;/strong&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Leitura do`.gitignore`raiz para identificar os artefatos efêmeros (a excluir do instantâneo) vs os artefatos estratégicos (a historizar)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Inventário de todos os arquivos`.adoc`da raiz`workspace/`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Filtragem : exclusão dos arquivos listados em`.gitignore`, inclusão de todos os outros&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Copia em`configuration/vision-archive/$DATE/&amp;lt;code&amp;gt;e atualização do symlink`latest/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Commit no repositório`configuration/`com uma mensagem estruturada&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Classificação automática do RGPD de cada arquivo modificado (todos os círculos)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Roteamento: dados`office/&amp;lt;code&amp;gt;→ commit privado, código`foundry/&amp;lt;/code&amp;gt;→&amp;lt;code&amp;gt;./gradlew check&amp;lt;/code&amp;gt;, configuração → commit&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Relatório estruturado com alertas RGPD e confirmação de arquivamento&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le `.gitignore`raiz não é um arquivo de configuração Git — é um&amp;lt;strong&amp;gt;arquivo de governança da historicização&amp;lt;/strong&amp;gt;. Define o esquema que o LLM consome para decidir quais arquivos do jardim secreto entram na biografia estratégica e quais são descartáveis.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O LLM não é mais um simples gerador de texto. Ele é o&amp;lt;strong&amp;gt;gerente de informação&amp;lt;/strong&amp;gt;do ecossistema — produtor, classificador, roteador.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_roadmap_do_asciidoc_ao_langgraph4j&amp;quot;&amp;gt;Roadmap : do AsciiDoc ao LangGraph4j&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Hoje, esta governança é &amp;lt;strong&amp;gt;determinista&amp;lt;/strong&amp;gt;: o LLM aplica um procedimento descrito em arquivos AsciiDoc. É a&amp;lt;strong&amp;gt;fase 1&amp;lt;/strong&amp;gt;— o prompt engineering com memória LLM&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;La &amp;lt;strong&amp;gt;fase 2&amp;lt;/strong&amp;gt;extrairá cada etapa do procedimento em uma&amp;lt;strong&amp;gt;tarefa Gradle tipada&amp;lt;/strong&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;./gradlew endSessionWorkspace    → snapshot vision-archive
./gradlew endSessionProject      → archive .agents/
./gradlew endSessionReport       → rapport multi-zones&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;La &amp;lt;strong&amp;gt;fase 3&amp;lt;/strong&amp;gt;modelará o processo de término da sessão como um&amp;lt;strong&amp;gt;grafo de estado&amp;lt;/strong&amp;gt;comhttps://github.com/langgraph4j/langgraph4j[LangGraph4j]:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;[Start] → [Inventaire fichiers modifiés]
       → [Classification RGPD] (nœud ONNX)
       → [Branchement par cercle]
            ├→ cercle 0 → snapshot → commit
            ├→ cercle 2 → anonymisation → commit
            ├→ cercle 4 → archive → commit
            └→ cercle 1 → commit configuration/
       → [Rapport] → [End]&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este grafo será versionado no CI/CD, executado pelo Gradle, configurado via os GitHub Secrets dos plugins. O LLM não precisará mais decidir o roteamento — o grafo fará isso.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_que_esta_arquitetura_resolve_e_o_que_ela_não_resolve&amp;quot;&amp;gt;O que Esta Arquitetura Resolve (e o que ela não Resolve)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock caution&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-caution&amp;quot; title=&amp;quot;🔒&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A ontologia espacial não cobre tudo. As convenções de estilo de código, a nomenclatura, as escolhas de design arquitetural — tudo isso permanece no prompt. O que a ontologia espacial resolve é a&amp;lt;strong&amp;gt;camada de segurança e de visibilidade&amp;lt;/strong&amp;gt;: onde cada byte deve viver, e quem pode vê-lo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está o que ela traz concretamente:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Não`git push`acidental de um segredo&amp;lt;/strong&amp;gt;: os segredos são em`configuration/&amp;lt;code&amp;gt;(cercle 1). Os projetos públicos estão em`foundry/&amp;lt;/code&amp;gt;(círculo 4). Nenhum caminho atravessa os dois.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Sem código de negócio nos dados&amp;lt;/strong&amp;gt;&amp;lt;code&amp;gt;office/`contém&amp;lt;/code&amp;gt;.adoc`, alguns YAML, alguns JSON Schemas — mas não há`.kt`. Le `build.gradle.kts`que lá vive é um script de consumo, não é código de negócio.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Sem pensamento estratégico exposto&amp;lt;/strong&amp;gt; : Os documentos de visão vivem no jardim secreto (raiz fora-CVS). Os snapshots estão em`configuration/`(cofre privado). Ninguém, mesmo num círculo de confiança alargado, lê a gênese da estratégia.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Auto-priorização&amp;lt;/strong&amp;gt; : o LLM observa o delta entre a álgebra relacional (o que existe) e os mapeamentos de especialistas (o que é necessário). A próxima tarefa de desenvolvimento surge desse delta.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_a_arquitetura_como_discurso&amp;quot;&amp;gt;Conclusão: a Arquitetura como Discurso&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não coloco manifesto político no rodapé do meu site. Não coloco regras éticas nos meus prompts. O alinhamento não está no texto — está no&amp;lt;strong&amp;gt;sistema de arquivos&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando um LLM trabalha neste espaço, ele não pode vazar um segredo (o caminho o impede). Ele não pode confundir um dado pedagógico com código-fonte (a zona física é diferente). Ele não pode esquecer uma regra de segurança de 80 000 tokens — porque a regra não está no prompt, ela está no`workspace/` → &amp;lt;code&amp;gt;configuration/&amp;lt;/code&amp;gt;→&amp;lt;code&amp;gt;office/&amp;lt;/code&amp;gt;→`foundry/`que cada leitura de arquivo seja reativa&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É o princípio do &amp;lt;strong&amp;gt;secure by design&amp;lt;/strong&amp;gt; aplicado ao alinhamento do agente: não pedir ao LLM para se lembrar das regras. Fazer com que a arquitetura torne o erro estruturalmente impossível.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E a propósito, isso dá ao LLM o que nenhum prompt pode lhe dar: a capacidade de&amp;lt;strong&amp;gt;perceber&amp;lt;/strong&amp;gt;onde ele está, o que existe, o que falta — e disso deduzir o que ele deve fazer.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_referências&amp;quot;&amp;gt;Referências&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Artigo sobre a governança do agente Eager/Lazy :&amp;lt;a href=&amp;quot;../2026/0108_gouvernance_agent_opencode_eager_lazy_post.html&amp;quot;&amp;gt;Governar um Agente IA com AsciiDoc&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Artigo sobre o mecanismo Hot/Warm/Cold :&amp;lt;a href=&amp;quot;../2026/0110_mecanisme_backup_contexte_agent_post.html&amp;quot;&amp;gt;Sliding Window e onda fria&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Artigo sobre a auditoria de contexto:&amp;lt;a href=&amp;quot;../2026/0111_audit_contexte_agent_post.html&amp;quot;&amp;gt;Quando Sua Própria Governança Torna-se o Problema&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Artigo sobre a comparação dos três LLMs :&amp;lt;a href=&amp;quot;../2026/0112_comparaison_kimi_glm_deepseek_long_contexte_plugin_gradle_opencode_post.html&amp;quot;&amp;gt;DeepSeek-V4-Pro, Kimi K2.6, GLM-5.1&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Migração Supabase → Firebase : Conectar um Formulário de Contato no Firestore Sem Backend</title>
            <link >https://cheroliv.com//blog/2026/0113_integration_firebase_contact_form_post.html</link>
            <pubDate>Wed, 29 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0113_integration_firebase_contact_form_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_cena_um_formulário_que_não_armazena_nada&amp;quot;&amp;gt;A cena : um formulário que não armazena nada&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_legado_supabase&amp;quot;&amp;gt;O legado Supabase&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_submissão_dupla_fantasma&amp;quot;&amp;gt;A submissão dupla fantasma&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_firebase_em_vez_de_supabase&amp;quot;&amp;gt;Por que Firebase em vez de Supabase?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fase_1_criar_o_projeto_firebase&amp;quot;&amp;gt;Fase 1: Criar o projeto Firebase&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_inicialização&amp;quot;&amp;gt;Inicialização&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_regras_de_segurança_firestore&amp;quot;&amp;gt;Regras de segurança Firestore&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fase_2_reescrever_o_javascript_de_submissão&amp;quot;&amp;gt;Fase 2: Reescrever o JavaScript de submissão&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_dependência_window_firebase&amp;quot;&amp;gt;A dependência `window.&amp;lt;em&amp;gt;FIREBASE&amp;lt;/em&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_novo_contact_js&amp;quot;&amp;gt;O novo `contact.js&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_phase_3_limpar_o_código_morto_supabase&amp;quot;&amp;gt;Phase 3 : Limpar o código morto Supabase&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fase_4_configurar_o_rodapé&amp;quot;&amp;gt;Fase 4 : Configurar o rodapé&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fase_5_arquitetura_final&amp;quot;&amp;gt;Fase 5 : Arquitetura final&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_essa_migração_diz_sobre_o_dogfooding&amp;quot;&amp;gt;O que essa migração diz sobre o dogfooding&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_resumo_das_modificações&amp;quot;&amp;gt;Resumo das modificações&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_próximos_passos_backlog&amp;quot;&amp;gt;Próximos passos (backlog)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referências&amp;quot;&amp;gt;Referências&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 12 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Durante meses, o formulário de contato deste site rodou em um mock JavaScript — uma promessa de 85% de sucesso, um Firestore falso, zero dados armazenados. O plano inicial previa um backend Supabase com Google Apps Script para as notificações por e‑mail. Abandonado. Hoje, eu conto a migração para o Firebase Firestore: criação do projeto, regras de segurança, reescrita do JS, limpeza do código morto Supabase. E por que essa escolha diz algo maior sobre a filosofia de desenvolvimento.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_cena_um_formulário_que_não_armazena_nada&amp;quot;&amp;gt;A cena : um formulário que não armazena nada&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este site é gerado pelo JBake, meu plugin Gradle`bakery`. É 100% estático—sem backend, sem banco de dados. Exceto que eu tenho um formulário de contato. A página`contact.html`existe, o HTML está pronto (campos nome, e-mail, telefone, assunto, mensagem, validação HTML5, honeypot anti-spam), os estilos Bootstrap estão no lugar. Visualmente, tudo está perfeito.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exceto que na submissão, nada acontece.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript hljs&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;const firebaseMock = new Promise((resolve, reject) =&amp;amp;gt; {
    setTimeout(() =&amp;amp;gt; {
        if (Math.random() &amp;amp;lt; 0.85) {
            resolve({ status: 201, message: &amp;#39;Message stored in Firestore.&amp;#39; });
        } else {
            reject({ status: 500, message: &amp;#39;Firestore write failed.&amp;#39; });
        }
    }, 1500);
});&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um mock. Uma promessa que finge. O utilizador vê um spinner, depois uma mensagem « Mensagem enviada com sucesso! ». Mas os dados vão para o vazio. Nenhuma mensagem é armazenada em lugar nenhum.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A situação é pior do que um formulário quebrado — é um formulário que mente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_legado_supabase&amp;quot;&amp;gt;O legado Supabase&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plano inicial, documentado em`content/draft/integration_formulaire_contact_supabase.adoc`, previa :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Uma base Supabase com tabela`contacts`e Row Level Security&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Uma RPC`handle_contact_form`lado do servidor&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Um trigger SQL chamando um webhook Google Apps Script&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Google Apps Script que envia um e-mail Gmail de notificação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O código JavaScript correspondente ainda existe em`script.js`. Há uma classe`SupabaseManager`que inicializa um cliente Supabase com variáveis globais`SUPABASE_URL` et &amp;lt;code&amp;gt;SUPABASE_KEY&amp;lt;/code&amp;gt;, e uma classe`ContactFormHandler`quem ouve o evento submit do formulário e chama`SupabaseManager.submitContactForm()`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Problema: Essas variáveis globais não são mais injetadas no rodapé. O`&amp;amp;lt;script src=&amp;quot;supabase-js&amp;quot;&amp;amp;gt;`foi removido. O código chama`supabase.createClient()`em uma variável`supabase`que não existe mais. Portanto:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;console.error : &amp;#39;Supabase client library (supabase-js) is not loaded.&amp;#39;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não apenas os dados não são armazenados, mas o código de submissão está morto.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_submissão_dupla_fantasma&amp;quot;&amp;gt;A submissão dupla fantasma&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para piorar as coisas, há uma&amp;lt;strong&amp;gt;concorrência silenciosa&amp;lt;/strong&amp;gt;entre dois handlers no mesmo formulário :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;`contact.js`Ouça o submit, chame o mock Firebase&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;script.js&amp;lt;/code&amp;gt;— via`ContactFormHandler`— escuta também o submit, chama`SupabaseManager`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os dois fazem`event.preventDefault() + event.stopPropagation()&amp;lt;code&amp;gt;. Como`contact.js`é carregado primeiro em`footer.thyme&amp;lt;/code&amp;gt;, seu handler é anexado primeiro. Ele bloqueia a propagação.`ContactFormHandler`nunca será acionado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não é nem mesmo um bug ativo — é um zumbi. Código que nunca tem a oportunidade de ser executado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-before&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE

title Estado Inicial — Formulário de Contato
rectangle &amp;quot;contact.thyme
(HTML Bootstrap, honeypot)&amp;quot; as FORM
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE

title Estado Inicial — Formulário de Contato
rectangle &amp;quot;contact.thyme
(HTML Bootstrap, honeypot)&amp;quot; as FORM
rectangle &amp;quot;footer.thyme\n(Firebase SDK com placeholder de config)&amp;quot; as FOOT
rectangle &amp;quot;contact.js\n(mock Firebase, 85% de sucesso)&amp;quot; as CONT
rectangle &amp;quot;script.js\n(SupabaseManager + ContactFormHandler, mortos)&amp;quot; as SCRIPT
rectangle &amp;quot;Usuário&amp;quot; as USER

USER -&amp;amp;gt; FORM : Soumission du formulaire
FORM -&amp;amp;gt; CONT : submit event (attaché en premier)
CONT -&amp;amp;gt; CONT : preventDefault + stopPropagation
CONT -&amp;amp;gt; CONT : mock Promise (1.5s, aléatoire)
note right of CONT
  ⚠️ Aucune donnée stockée
end note

FORM ..&amp;amp;gt; SCRIPT : submit event (bloqué par stopPropagation)
note right of SCRIPT : ❌ Jamais déclenché
note right of SCRIPT : ❌ supabase-js non chargé

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_firebase_em_vez_de_supabase&amp;quot;&amp;gt;Por que Firebase em vez de Supabase?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A decisão de migração é documentada em`AGENT.adoc`&amp;amp;lt;/think&amp;amp;gt; (nothing)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Firebase agora é escolhido pelos seguintes motivos: melhor plano gratuito, Firestore nativo, Cloud Functions integradas, ecossistema Google mais adequado. A implementação Supabase existente está marcada « ⚠️ Abandonado ».&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Além do plano gratuito, há uma razão arquitetônica. Este site vive no ecossistema do Google: o repositório alvo é`cheroliv.github.io`, o CNAME aponta para o GitHub Pages, o build Gradle faz push no GitHub via JGit. Adicionar um serviço Google (Firebase) em vez de um serviço terceirizado (Supabase) reduz a superfície de dispersão.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Firestore no modo nativo (não no modo Datastore) também está mais próximo do modelo mental de documento NoSQL que eu tenho em mente: coleções, documentos, campos tipados, timestamps do servidor, regras de segurança integradas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_fase_1_criar_o_projeto_firebase&amp;quot;&amp;gt;Fase 1: Criar o projeto Firebase&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_inicialização&amp;quot;&amp;gt;Inicialização&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Como a CLI do Firebase não está instalada na minha máquina, passo a usar o console da web:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ir parahttps://console.firebase.google.com/[Firebase Console]&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Criar um projeto`cheroliv-contact`(ou reutilizar um projeto existente)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ativar Firestore no modo nativo (não Datastore)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Criar um banco de dados na região`eur3`(Europe)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para um uso minimalista como o nosso (uma única coleção, gravação pública), o modo nativo é a escolha certa. Não é necessário ter regras Datastore complexas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_regras_de_segurança_firestore&amp;quot;&amp;gt;Regras de segurança Firestore&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O formulário é público — qualquer pessoa pode enviar uma mensagem. Mas eu quero limitar os abusos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript hljs&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;rules_version = &amp;#39;2&amp;#39;;

service cloud.firestore {
  match /databases/{database}/documents {

    match /contact_messages/{messageId} {
      // Lecture : admin uniquement (authentifié)
      allow read: if request.auth != null;

      // Écriture : publique, mais limitée
      allow create: if request.auth == null
        &amp;amp;amp;&amp;amp;amp; request.resource.data.name is string
        &amp;amp;amp;&amp;amp;amp; request.resource.data.name.size() &amp;amp;gt;= 1
        &amp;amp;amp;&amp;amp;amp; request.resource.data.name.size() &amp;amp;lt;= 100
        &amp;amp;amp;&amp;amp;amp; request.resource.data.email is string
        &amp;amp;amp;&amp;amp;amp; request.resource.data.email.matches(&amp;#39;.*@.*\\..*&amp;#39;)
        &amp;amp;amp;&amp;amp;amp; request.resource.data.email.size() &amp;amp;lt;= 254
        &amp;amp;amp;&amp;amp;amp; request.resource.data.subject is string
        &amp;amp;amp;&amp;amp;amp; request.resource.data.subject.size() &amp;amp;gt;= 3
        &amp;amp;amp;&amp;amp;amp; request.resource.data.subject.size() &amp;amp;lt;= 200
        &amp;amp;amp;&amp;amp;amp; request.resource.data.message is string
        &amp;amp;amp;&amp;amp;amp; request.resource.data.message.size() &amp;amp;gt;= 10
        &amp;amp;amp;&amp;amp;amp; request.resource.data.message.size() &amp;amp;lt;= 5000
        &amp;amp;amp;&amp;amp;amp; request.resource.data.created_at == request.time
        &amp;amp;amp;&amp;amp;amp; request.resource.data.user_agent is string
        &amp;amp;amp;&amp;amp;amp; request.resource.data.user_agent.size() &amp;amp;lt;= 500;
    }
  }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Pontos-chave:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;allow read&amp;lt;/code&amp;gt;— somente os usuários autenticados podem ler as mensagens (eu, via o console Firebase)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;allow create&amp;lt;/code&amp;gt;— qualquer pessoa pode criar um documento, mas com validação dos campos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Validação do lado do servidor: tamanhos mínimo/máximo, formato de e-mail,&amp;lt;code&amp;gt;created_at`deve corresponder a`request.time&amp;lt;/code&amp;gt;(anti-falsificação)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;`user_agent`é enviado para rastreabilidade (não crítico mas útil)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Estas regras são mais estritas do que um simples`allow write: if true;`. Eles impedem que um atacante injete payloads enormes ou campos malformados.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_fase_2_reescrever_o_javascript_de_submissão&amp;quot;&amp;gt;Fase 2: Reescrever o JavaScript de submissão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O contrato é simples:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ler os dados do formulário&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Verificar o honeypot (campo`hp_name`— se estiver preenchido, é um bot, simulamos um sucesso sem enviar nada)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Chamar`addDoc(window.&amp;lt;em&amp;gt;FIREBASE&amp;lt;/em&amp;gt;.collection(db, &amp;quot;contact_messages&amp;quot;), {&amp;amp;#8230;&amp;amp;#8203;})`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Mostrar sucesso ou erro&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_dependência_window_firebase&amp;quot;&amp;gt;A dependência `window.&amp;lt;em&amp;gt;FIREBASE&amp;lt;/em&amp;gt;&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em`footer.thyme`, um módulo de script inicializa o Firebase SDK e expõe um objeto global:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html hljs&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;script type=&amp;quot;module&amp;quot;&amp;amp;gt;
    import { initializeApp } from &amp;quot;https://www.gstatic.com/firebasejs/11.6.0/firebase-app.js&amp;quot;;
    import { getFirestore, collection, addDoc, serverTimestamp }
      from &amp;quot;https://www.gstatic.com/firebasejs/11.6.0/firebase-firestore.js&amp;quot;;

    const firebaseConfig = { /* valeurs réelles */ };
    const app = initializeApp(firebaseConfig);
    const db = getFirestore(app);

    window.__FIREBASE__ = { db, collection, addDoc, serverTimestamp };
&amp;amp;lt;/script&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os scripts do módulo são executados antes`DOMContentLoaded`, portanto`window.&amp;lt;em&amp;gt;FIREBASE&amp;lt;/em&amp;gt;`é garantido disponível quando o handler`contact.js`é disparado. Por precaução, adiciono mesmo um polling de 5 segundos caso o CDN esteja lento.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_novo_contact_js&amp;quot;&amp;gt;O novo `contact.js&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript hljs&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;document.addEventListener(&amp;#39;DOMContentLoaded&amp;#39;, function () {
    &amp;#39;use strict&amp;#39;;

    const form = document.getElementById(&amp;#39;contact-form&amp;#39;);
    if (!form) return;

    const submitButton = form.querySelector(&amp;#39;button[type=&amp;quot;submit&amp;quot;]&amp;#39;);
    const successMessage = document.getElementById(&amp;#39;contact-success-message&amp;#39;);
    const errorMessage = document.getElementById(&amp;#39;contact-error-message&amp;#39;);

    // Éléments de validation
    const nameInput = form.querySelector(&amp;#39;input[name=&amp;quot;name&amp;quot;]&amp;#39;);
    const emailInput = form.querySelector(&amp;#39;input[name=&amp;quot;email&amp;quot;]&amp;#39;);
    const phoneInput = form.querySelector(&amp;#39;input[name=&amp;quot;phone&amp;quot;]&amp;#39;);
    const subjectInput = form.querySelector(&amp;#39;input[name=&amp;quot;subject&amp;quot;]&amp;#39;);
    const messageInput = form.querySelector(&amp;#39;textarea[name=&amp;quot;message&amp;quot;]&amp;#39;);
    const honeypotInput = form.querySelector(&amp;#39;input[name=&amp;quot;hp_name&amp;quot;]&amp;#39;);

    /**
     * Attend que window.__FIREBASE__ soit disponible.
     * Timeout de 5 secondes — si le CDN Firebase est lent, on abandonne.
     */
    function waitForFirebase(timeoutMs = 5000) {
        return new Promise((resolve, reject) =&amp;amp;gt; {
            if (window.__FIREBASE__) {
                resolve(window.__FIREBASE__);
                return;
            }
            const start = Date.now();
            const interval = setInterval(() =&amp;amp;gt; {
                if (window.__FIREBASE__) {
                    clearInterval(interval);
                    resolve(window.__FIREBASE__);
                } else if (Date.now() - start &amp;amp;gt; timeoutMs) {
                    clearInterval(interval);
                    reject(new Error(&amp;#39;Firebase SDK non disponible après timeout&amp;#39;));
                }
            }, 100);
        });
    }

    // --- Validation (identique à l&amp;#39;existant) ---
    function validateForm() {
        nameInput.setCustomValidity(&amp;#39;&amp;#39;);
        emailInput.setCustomValidity(&amp;#39;&amp;#39;);
        if (phoneInput) phoneInput.setCustomValidity(&amp;#39;&amp;#39;);
        subjectInput.setCustomValidity(&amp;#39;&amp;#39;);
        messageInput.setCustomValidity(&amp;#39;&amp;#39;);

        if (nameInput.value.trim().length &amp;amp;lt; 1) {
            nameInput.setCustomValidity(&amp;#39;Veuillez saisir votre nom.&amp;#39;);
        }
        const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        if (!emailPattern.test(emailInput.value.trim())) {
            emailInput.setCustomValidity(&amp;#39;Veuillez saisir une adresse email valide.&amp;#39;);
        }
        if (phoneInput &amp;amp;amp;&amp;amp;amp; phoneInput.value.trim() !== &amp;#39;&amp;#39;) {
            const phonePattern = /^\d{10,15}$/;
            if (!phonePattern.test(phoneInput.value.trim())) {
                phoneInput.setCustomValidity(&amp;#39;Veuillez saisir un numéro valide (10 à 15 chiffres).&amp;#39;);
            }
        }
        if (subjectInput.value.trim().length &amp;amp;lt; 3) {
            subjectInput.setCustomValidity(&amp;#39;Veuillez saisir un sujet (3 caractères minimum).&amp;#39;);
        }
        if (messageInput.value.trim().length &amp;amp;lt; 10) {
            messageInput.setCustomValidity(&amp;#39;Veuillez saisir un message (10 caractères minimum).&amp;#39;);
        }

        form.classList.add(&amp;#39;was-validated&amp;#39;);
        return form.checkValidity();
    }

    // --- Handler de soumission ---
    form.addEventListener(&amp;#39;submit&amp;#39;, async function (event) {
        event.preventDefault();
        event.stopPropagation();

        if (!validateForm()) return;

        // Honeypot : si rempli, simuler un succès sans rien envoyer
        if (honeypotInput &amp;amp;amp;&amp;amp;amp; honeypotInput.value.trim() !== &amp;#39;&amp;#39;) {
            successMessage.style.display = &amp;#39;block&amp;#39;;
            form.reset();
            form.classList.remove(&amp;#39;was-validated&amp;#39;);
            return;
        }

        // UI : état d&amp;#39;envoi
        submitButton.disabled = true;
        submitButton.innerHTML = `
            &amp;amp;lt;span class=&amp;quot;spinner-border spinner-border-sm&amp;quot; role=&amp;quot;status&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;
            Envoi en cours...
        `;
        successMessage.style.display = &amp;#39;none&amp;#39;;
        errorMessage.style.display = &amp;#39;none&amp;#39;;

        try {
            const fb = await waitForFirebase();
            const messagesCollection = fb.collection(fb.db, &amp;#39;contact_messages&amp;#39;);

            await fb.addDoc(messagesCollection, {
                name: nameInput.value.trim(),
                email: emailInput.value.trim(),
                phone: phoneInput ? phoneInput.value.trim() : &amp;#39;&amp;#39;,
                subject: subjectInput.value.trim(),
                message: messageInput.value.trim(),
                created_at: fb.serverTimestamp(),
                user_agent: navigator.userAgent.substring(0, 500)
            });

            successMessage.style.display = &amp;#39;block&amp;#39;;
            form.reset();
            form.classList.remove(&amp;#39;was-validated&amp;#39;);

        } catch (error) {
            console.error(&amp;#39;Erreur Firestore:&amp;#39;, error);
            errorMessage.style.display = &amp;#39;block&amp;#39;;

        } finally {
            submitButton.disabled = false;
            submitButton.innerHTML = `
                &amp;amp;lt;i class=&amp;quot;bi bi-send me-2&amp;quot;&amp;amp;gt;&amp;amp;lt;/i&amp;amp;gt;
                Envoyer le Message
            `;
        }
    }, false);
});&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;As alterações em relação ao mock :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;waitForFirebase()&amp;lt;/code&amp;gt;— polling com timeout, robusto mesmo se o CDN for lento&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;honeypot&amp;lt;/code&amp;gt;— se o campo oculto`hp_name`está preenchido, simular um sucesso sem apelo Firestore. O bot acredita ter tido sucesso mas nada é armazenado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;addDoc(collection, {&amp;amp;#8230;&amp;amp;#8203;})&amp;lt;/code&amp;gt;— chamada verdadeira do Firestore com`serverTimestamp()` et &amp;lt;code&amp;gt;user_agent&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Gestão de erro com`try/catch`assíncrono&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Limpeza do`finally`(restauro do botão)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Por que`user_agent`? É opcional, mas útil para o diagnóstico. Se chegar uma mensagem estranha, saber se ela vem de um navegador de desktop, móvel, ou de um script curl ajuda na triagem.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_phase_3_limpar_o_código_morto_supabase&amp;quot;&amp;gt;Phase 3 : Limpar o código morto Supabase&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`script.js`contém 250 linhas de código morto :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;SupabaseManager&amp;lt;/code&amp;gt;(linhas 417-481) — 65 linhas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;ContactFormHandler&amp;lt;/code&amp;gt;(linhas 490-551) — 62 linhas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Bloco de inicialização (linhas 645-654) — 10 linhas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Total: ~140 linhas a excluir.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O bloco`DOMContentLoaded`cria um`SupabaseManager`então um`ContactFormHandler`relacionado ao formulário. Como explicado acima, esse código nunca é executado (bloqueado por`contact.js`), e mesmo se fosse executado, falharia (nenhum SDK Supabase carregado).)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eu apago:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;A classe`SupabaseManager`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;A classe`ContactFormHandler`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O bloco de inicialização em`DOMContentLoaded`(linhas 645-654)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le reste de &amp;lt;code&amp;gt;script.js`está intacto :`ThemeManager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ScrollToTopButton&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MobileMenuManager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SmoothScrollWithOffset&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;NavbarHeightUpdater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DynamicNavbarBreakpoint&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;CodeBlockManager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;TooltipManager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PhoneInputManager&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_fase_4_configurar_o_rodapé&amp;quot;&amp;gt;Fase 4 : Configurar o rodapé&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`footer.thyme`já tem o boilerplate Firebase mas com valores de placeholder. Substituo:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript hljs&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;const firebaseConfig = {
    apiKey: &amp;quot;REMPLACER_PAR_VOTRE_API_KEY&amp;quot;,
    authDomain: &amp;quot;REMPLACER_PAR_VOTRE_AUTH_DOMAIN&amp;quot;,
    projectId: &amp;quot;REMPLACER_PAR_VOTRE_PROJECT_ID&amp;quot;,
    storageBucket: &amp;quot;REMPLACER_PAR_VOTRE_STORAGE_BUCKET&amp;quot;,
    messagingSenderId: &amp;quot;REMPLACER_PAR_VOTRE_SENDER_ID&amp;quot;,
    appId: &amp;quot;REMPLACER_PAR_VOTRE_APP_ID&amp;quot;
};&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Pelos valores reais recuperados desde&amp;lt;strong&amp;gt;Configurações do Projeto &amp;amp;gt; Geral &amp;amp;gt; Seus aplicativos &amp;amp;gt; App da web&amp;lt;/strong&amp;gt;na console Firebase.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os valores são sensíveis (&amp;lt;code&amp;gt;apiKey`é pública por design no Firebase, mas prefiro não commita-los em claro). Eu os armazeno em`site.yml&amp;lt;/code&amp;gt;(já em`.gitignore`) e o plugin`bakery`os injeta no modelo via uma lógica a ser adicionada no lado do build.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Por enquanto eu os coloco diretamente em`footer.thyme`— o build`./gradlew serve`os carregará localmente. Na implantação, migrerei a injeção para`site.yml`ou para uma variável Gradle.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock warning&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-warning&amp;quot; title=&amp;quot;Warning&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;L&amp;#39;`apiKey`Firebase não é&amp;lt;strong&amp;gt;não&amp;lt;/strong&amp;gt;um segredo. Ela é pública por projeto. O que protege os seus dados, são os&amp;lt;strong&amp;gt;regras de segurança do Firestore&amp;lt;/strong&amp;gt;, não a chave de API. Não a coloque em um`.env`Carregado do lado do servidor — é destinado a ser exposto ao navegador.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_fase_5_arquitetura_final&amp;quot;&amp;gt;Fase 5 : Arquitetura final&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;diag-after&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 8) ]

@startuml
skinparam backgroundColor #FEFEFE

title Arquitetura Final — Formulário de Contato Firebase
actor Utilisateur as USER

package &amp;quot;Navegador&amp;quot; #E8F5E9 {
  rectangle &amp;quot;contact.thyme
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
skinparam backgroundColor #FEFEFE

title Arquitetura Final — Formulário de Contato Firebase
actor Utilisateur as USER

package &amp;quot;Navegador&amp;quot; #E8F5E9 {
  rectangle &amp;quot;contact.thyme
(HTML, honeypot)&amp;quot; as FORM
  rectangle &amp;quot;contact.js\n(validação + Firestore)&amp;quot; as CONT
  rectangle &amp;quot;footer.thyme\n(Inicialização do SDK Firebase)&amp;quot; as FOOT
}

cloud &amp;quot;Firebase&amp;quot; #BBDEFB {
  rectangle &amp;quot;Firestore\n(contact_messages)&amp;quot; as FS
}

FORM --&amp;amp;gt; CONT : submit event
CONT --&amp;amp;gt; FOOT : window.__FIREBASE__.addDoc()
FOOT --&amp;amp;gt; FS : Insert document\n(règles de sécurité validées)

note right of FS
  Firestore Rules :
  - create: public, validé
  - read: auth uniquement
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_que_essa_migração_diz_sobre_o_dogfooding&amp;quot;&amp;gt;O que essa migração diz sobre o dogfooding&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este site é gerado pelo meu próprio plugin Gradle`bakery`. O formulário de contato fica dentro do site. A migração Supabase → Firebase está documentada em`AGENT.adoc`, ela é discutida no backlog, ela é testada via`./gradlew serve`, e ela gera um artigo de blog (aquele que você está lendo).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É puro dogfooding. O site é o produto do plugin, o plugin é o produto do desenvolvedor, o desenvolvedor documenta o processo no próprio site.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O ciclo está fechado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O fato de ter arrastado um mock por meses (sessões inteiras onde o formulário mentia silenciosamente) fez-me perceber algo : o backlog de um site estático pessoal nunca é « acabado ». Sempre há uma US prioritária, sempre um artigo em rascunho, sempre uma seção comentada em um modelo. A disciplina não é terminar tudo — é terminar o que é visível pelo usuário.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um formulário de contato quebrado é pior do que não ter nenhum formulário. É uma promessa não cumprida.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_resumo_das_modificações&amp;quot;&amp;gt;Resumo das modificações&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6676%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;arquivo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;modificação&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Impacto&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;blog/2026/0113_*.adoc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Criação do artigo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Documentação&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;assets/js/contact.js&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Reescrita (mock → Firestore real)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Funcional&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;assets/js/script.js&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Exclusão SupabaseManager + ContactFormHandler + init bloco&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Limpeza&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;templates/footer.thyme&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Substituição do placeholder de config → valores reais&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;configuração&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_próximos_passos_backlog&amp;quot;&amp;gt;Próximos passos (backlog)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Email de notificação&amp;lt;/strong&amp;gt;: uma Cloud Function`onCreate`sobre`contact_messages`que envia um e-mail via SendGrid. O formulário armazena, mas eu não sou notificado. Prioridade média — as mensagens são visíveis na console do Firebase.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Limitação de taxa no lado do cliente&amp;lt;/strong&amp;gt;: Adicionar um timestamp localStorage para impedir envios múltiplos em rajada. O honeypot bloqueia os bots ingênuos, um rate limiter bloquearia os bots um pouco mais espertos.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Testes&amp;lt;/strong&amp;gt;: Um teste Playwright que envia o formulário e verifica se o documento aparece no Firestore. Por enquanto, estou testando manualmente via`./gradlew serve`.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_referências&amp;quot;&amp;gt;Referências&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://firebase.google.com/docs/firestore/security/get-started&amp;quot;&amp;gt;Documentação das regras de segurança do Firestore&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://firebase.google.com/docs/firestore/manage-data/add-data#add_a_document&amp;quot;&amp;gt;Documentação Firestore addDoc — Web v9 modular&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;/blog/2026/0108_gouvernance_agent_opencode_eager_lazy_post.html&amp;quot;&amp;gt;Série de governança do agente — Parte 1: Eager/Lazy&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;/blog/2026/0110_mecanisme_backup_contexte_agent_post.html&amp;quot;&amp;gt;Série agente de governança — Parte 2 : Hot/Warm/Cold&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;/blog/2026/0111_audit_contexte_agent_post.html&amp;quot;&amp;gt;Série governança agente — Parte 3 : Auditoria&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>DeepSeek-V4-Pro, Kimi K2.6, GLM-5.1: Três LLMs à prova de Vibe Coding Contexto Longo</title>
            <link >https://cheroliv.com//blog/2026/0112_comparaison_kimi_glm_deepseek_long_contexte_plugin_gradle_opencode_post.html</link>
            <pubDate>Tue, 28 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0112_comparaison_kimi_glm_deepseek_long_contexte_plugin_gradle_opencode_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_contexto_não_é_um_banco_de_ensaio_mas_um_canteiro_de_obras&amp;quot;&amp;gt;Contexto: não é um banco de ensaio, mas um canteiro de obras&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_meu_ambiente_de_teste&amp;quot;&amp;gt;Meu ambiente de teste&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_grade_de_avaliação&amp;quot;&amp;gt;A Grade de Avaliação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_rodada_1_kimi_k2_6_o_falso_início&amp;quot;&amp;gt;Rodada 1 : Kimi K2.6 — O Falso Início&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_sessão_9_a_melhora&amp;quot;&amp;gt;Sessão 9 : a melhora&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_sessão_10_o_naufrágio_silencioso&amp;quot;&amp;gt;Sessão 10: o naufrágio silencioso&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_round_2_glm_5_1_o_combatente_honorável&amp;quot;&amp;gt;Round 2 : GLM-5.1 — O Combatente Honorável&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_o_dsa_contra_o_mla_puro&amp;quot;&amp;gt;Arquitetura : o DSA contra o MLA puro&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_sessões_11_13_sólido_mas_frustrante&amp;quot;&amp;gt;Sessões 11-13 : sólido mas frustrante&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_round_3_deepseek_v4_pro_a_máquina_de_guerra&amp;quot;&amp;gt;Round 3: DeepSeek-V4-Pro — A Máquina de Guerra&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_csa_hca_o_duplo_filete&amp;quot;&amp;gt;Arquitetura: CSA + HCA, o duplo filete&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_sessões_1_8_alívio&amp;quot;&amp;gt;Sessões 1-8 : alívio&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_teste_decisivo_refactoring_de_100k_tokens&amp;quot;&amp;gt;O teste decisivo: refactoring de 100K tokens&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_encontro_face_a_face_métricas_comparativas&amp;quot;&amp;gt;Encontro Face a Face: Métricas Comparativas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_deepseek_v4_pro_ganha_em_desenvolvimento_de_software&amp;quot;&amp;gt;Por que DeepSeek-V4-Pro Ganha em Desenvolvimento de Software&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_arquitetura_csahca_é_adaptada_para_o_código&amp;quot;&amp;gt;A arquitetura CSA+HCA é adaptada para o código&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_2_o_contexto_eager_é_a_zona_de_conforto&amp;quot;&amp;gt;2. O contexto EAGER é a zona de conforto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_3_a_relação_qualidadelatência_é_ideal_para_o_fluxo&amp;quot;&amp;gt;3. A relação qualidade/latência é ideal para o fluxo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_lições_aprendidas_como_escolher_seu_llm_para_o_vibe_coding&amp;quot;&amp;gt;Lições Aprendidas: Como Escolher seu LLM para o Vibe Coding&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_e_a_governança_agent_em_tudo_isso&amp;quot;&amp;gt;E a Governança Agent em tudo isso?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fontes_técnicas&amp;quot;&amp;gt;Fontes Técnicas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão_a_escolha_do_artesão&amp;quot;&amp;gt;Conclusão: A Escolha do Artesão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 18 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;A guerra dos LLMs também se desenrola no terminal de um desenvolvedor. Não em benchmarks acadêmicos estéreis. Na vida real: um prompt de 30 000 tokens, uma governança de agente em AsciiDoc, plugins Gradle Kotlin DSL para depurar e sessões que se encadeiam por três semanas. Testei para você DeepSeek-V4-Pro, Kimi K2.6 e GLM-5.1. Aqui está o veredicto, provas técnicas a respaldar.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_contexto_não_é_um_banco_de_ensaio_mas_um_canteiro_de_obras&amp;quot;&amp;gt;Contexto: não é um banco de ensaio, mas um canteiro de obras&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Há três semanas, eu estava trabalhando em`codebase-gradle`, meu sistema meta-build que centraliza a configuração YAML de quatro projetos: um gerador de README PlantUML, um construtor de slides AsciiDoc, um site estático JBake e um chatbot LLM. O agente Opencode, governado pela minha metodologia de arquivos de agente em AsciiDoc, carregava ~30K tokens de contexto EAGER a cada início de sessão — regras absolutas, backlog, histórico das últimas 10 sessões.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É neste terreno que eu confrontei três modelos :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Kimi K2.6&amp;lt;/strong&amp;gt;(Moonshot AI, 1T parâmetros / 32B ativados, 256K contexto máximo, MLA) - &amp;lt;strong&amp;gt;GLM-5.1&amp;lt;/strong&amp;gt;(Zhipu AI/Tsinghua, 744B de parâmetros / DSA, contexto máximo de 200K) - &amp;lt;strong&amp;gt;DeepSeek-V4-Pro&amp;lt;/strong&amp;gt;(DeepSeek, 1.6T parâmetros / 49B ativados, 1M contexto máximo, CSA+HCA)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Todos servidos via Ollama em servidor cloud, todos em modo thinking (fase de reflexão ativada). O desafio: produzir código correto, manter a consistência em sessões longas e não alucinar quando o contexto ultrapassar 80K tokens.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_meu_ambiente_de_teste&amp;quot;&amp;gt;Meu ambiente de teste&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;diag-dev-env&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 10) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam wrapWidth 220

title Ambiente de Teste — Sessões Opencode × 3 LLMs
left to right direction

package &amp;quot;🖥️ Terminal do Desenvolvedor&amp;quot; #E8F5E9 {
  rectangle &amp;quot;AGENT.adoc
^^^^^
 Syntax Error? (Assumed diagram type: class)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam wrapWidth 220

title Ambiente de Teste — Sessões Opencode × 3 LLMs
left to right direction

package &amp;quot;🖥️ Terminal do Desenvolvedor&amp;quot; #E8F5E9 {
  rectangle &amp;quot;AGENT.adoc
(regras, backlog)&amp;quot; as AG
  rectangle &amp;quot;PROMPT_REPRISE\n(missão)&amp;quot; as PR
  rectangle &amp;quot;INDEX.adoc\n(roteiro)&amp;quot; as IDX
}

package &amp;quot;☁️ Ollama Nuvem&amp;quot; #BBDEFB {
  rectangle &amp;quot;DeepSeek-V4-Pro\n1.6T / CSA+HCA&amp;quot; as DV4
  rectangle &amp;quot;Kimi K2.6\n1T / MLA&amp;quot; as KIMI
  rectangle &amp;quot;GLM-5.1
744B / MLA+DSA&amp;quot; as GLMG
}

package &amp;quot;⚙️ Base de código Gradle&amp;quot; #FFF9C4 {
  folder &amp;quot;buildSrc/&amp;quot; {
    file &amp;quot;codebase.kt&amp;quot;
    file &amp;quot;readme.kt&amp;quot;
    file &amp;quot;site.kt&amp;quot;
    file &amp;quot;slider.kt&amp;quot;
    file &amp;quot;snapshot.kt&amp;quot;
  }
  file &amp;quot;build.gradle.kts
(947 linhas)&amp;quot;
  file &amp;quot;embeds.yml&amp;quot;
}

AG --&amp;amp;gt; DV4 : Sessions 1-8 ✅
AG --&amp;amp;gt; KIMI : Sessions 9-10 ⚠️
AG --&amp;amp;gt; GLMG : Sessions 11-13 ⚠️

DV4 --&amp;amp;gt; &amp;quot;base de código&amp;quot; : &amp;quot;TDD, refatoração,\ninstantâneo&amp;quot;
KIMI --&amp;amp;gt; &amp;quot;base de código&amp;quot; : &amp;quot;Degradação
a partir de 60K tokens&amp;quot;

note bottom of GLMG
  Meilleur que Kimi
  mais latence +
  DSA moins robuste
  que CSA+HCA
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cada sessão iniciava com aproximadamente 30K tokens de contexto EAGER:`AGENT.adoc`(287 linhas),&amp;lt;code&amp;gt;PROMPT_REPRISE.adoc&amp;lt;/code&amp;gt;(51 linhas),&amp;lt;code&amp;gt;.agents/INDEX.adoc&amp;lt;/code&amp;gt;(218 linhas)&amp;lt;code&amp;gt;LAZY_EAGER_ESSENTIALS.adoc&amp;lt;/code&amp;gt;(50 linhas). O contexto aumentava rapidamente com as trocas — uma sessão típica de 10 mensagens adicionava 15-20K tokens ao prompt cumulado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_grade_de_avaliação&amp;quot;&amp;gt;A Grade de Avaliação&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eu avaliei cada modelo em quatro eixos críticos para o desenvolvimento de software assistido.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Eixo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Critério concreto&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Coerência de longo contexto&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O agente se lembra das convenções decididas há 40 mensagens?&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Qualidade do código produzido&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O código compila de primeira? Respeita os padrões existentes?&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Raciocínio arquitetônico&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O agente compreende as relações entre os módulos sem que eu tenha que lhes reexplicar?&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Resistência às alucinações&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;A partir de quantos tokens o agente começa a inventar APIs ou classes inexistentes?&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E uma métrica sintética caseira : o&amp;lt;strong&amp;gt;coeficiente de retomada&amp;lt;/strong&amp;gt;(quanto tempo eu passo corrigindo o agente ao invés de codificar com ele)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_rodada_1_kimi_k2_6_o_falso_início&amp;quot;&amp;gt;Rodada 1 : Kimi K2.6 — O Falso Início&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Kimi K2.6 foi minha primeira escolha. Seus benchmarks no SWE-Bench Verified (80.2) e no Terminal-Bench 2.0 (66.7) são excelentes. Sua arquitetura MLA promete boa eficiência em sequências longas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_sessão_9_a_melhora&amp;quot;&amp;gt;Sessão 9 : a melhora&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Primeira sessão com Kimi. Tarefa: implementar o método`resolveActiveKey()&amp;lt;code&amp;gt;em`codebase.kt&amp;lt;/code&amp;gt;— uma função de resolução de chave API com fallback CLI. O contexto tem 30K tokens, o Kimi raciocina rápido, produz código limpo com gerenciamento de expiração das chaves.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;fun resolveActiveKey(
    cfg: CodebaseConfiguration,
    logger: Logger,
    cliProvider: String? = null,
    cliAccount: String? = null,
    cliKey: String? = null
): NamedApiKey? {
    // Résolution provider → compte → clé avec CLI override
    // Kimi a parfaitement compris la chaîne de priorité
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O código compila. Os 7 casos de teste passam. Estou otimista.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_sessão_10_o_naufrágio_silencioso&amp;quot;&amp;gt;Sessão 10: o naufrágio silencioso&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Segunda sessão. O contexto sobe para ~90K tokens com as trocas. Peço ao Kimi para acrescentar um mecanismo de snapshot AsciiDoc que anonimiza os segredos antes da escrita.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É aqui que isso escorrega. Kimi começa a inventar classes que não existem. Ele me propõe`AnonymizedObjectMapper`— uma classe fictiva. Ele confunde`ReadmeYmlAnonymizer` et &amp;lt;code&amp;gt;CodebaseYmlAnonymizer&amp;lt;/code&amp;gt;. Ele me sugere importar`com.fasterxml.jackson.anonymize.*`— um pacote que nunca existiu.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Pior : na sua fase de reflexão, vejo que ele constrói raciocínios sobre premissas falsas. Ele &amp;quot;se lembra&amp;quot; que`GitConfig`tem um campo`anonymizedToken`— não, é`resolvedToken()&amp;lt;code&amp;gt;. Ele atribui a`SiteYmlAnonymizer`um método`maskSupabaseCredentials()&amp;lt;/code&amp;gt;— que não existe.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ Não era um bug. Era uma dissolução progressiva da coerência. Como se cada token adicionado ao contexto diluísse um pouco mais a memória dos primeiros 30 000. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Paro o Kimi após duas sessões. O diagnóstico é claro: o MLA comprime bem o cache KV, mas sem um mecanismo de seleção esparsa de granulação fina, a atenção se dilui mecanicamente além de 60K tokens. Cada token &amp;quot;vê&amp;quot; cada vez pior o contexto distante — e começa a preencher as lacunas com ruído.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_round_2_glm_5_1_o_combatente_honorável&amp;quot;&amp;gt;Round 2 : GLM-5.1 — O Combatente Honorável&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;GLM-5.1 chega com uma arquitetura diferente: MLA para o modelo base, seguido de um &amp;lt;strong&amp;gt;continued pre-training&amp;lt;/strong&amp;gt; com DSA (DeepSeek Sparse Attention) — um indexador leve que seleciona dinamicamente os top-2048 tokens pertinentes de todo o histórico.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_arquitetura_o_dsa_contra_o_mla_puro&amp;quot;&amp;gt;Arquitetura : o DSA contra o MLA puro&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A diferença é fundamental. Onde Kimi comprime o histórico em um espaço latente único (e perde gradualmente a capacidade de discriminar a informação relevante), o GLM acrescenta um indexador pós-treinamento que faz uma seleção sparse explícita.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-mla-vs-dsa&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 9) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Arquiteturas de Atenção — MLA vs MLA+DSA
left to right direction

rectangle &amp;quot;**Kimi K2.6 — MLA puro**&amp;quot; as MLA #FFCDD2 {
  rectangle &amp;quot;KV Cache
^^^^^
 Syntax Error? (Assumed diagram type: class)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Arquiteturas de Atenção — MLA vs MLA+DSA
left to right direction

rectangle &amp;quot;**Kimi K2.6 — MLA puro**&amp;quot; as MLA #FFCDD2 {
  rectangle &amp;quot;KV Cache
completo&amp;quot; as KV1
  rectangle &amp;quot;Compressão
latente&amp;quot; as CL1
  rectangle &amp;quot;Decodificação
espaço latente&amp;quot; as DL1
  KV1 --&amp;amp;gt; CL1
  CL1 --&amp;amp;gt; DL1
  note bottom of DL1
    ⚠️ Au-delà de 60K tokens :
    perte de discrimination
  end note
}

rectangle &amp;quot;GLM-5.1 — MLA + DSA&amp;quot; as DSA #C8E6C9 {
  rectangle &amp;quot;KV Cache
completo&amp;quot; as KV2
  rectangle &amp;quot;Compressão\nlatente (MLA)&amp;quot; as CL2
  rectangle &amp;quot;Indexador leve
(DSA, top-k=2048)&amp;quot; as IL
  rectangle &amp;quot;Decodificação
dos tokens selecionados&amp;quot; as DL2
  KV2 --&amp;amp;gt; CL2
  CL2 --&amp;amp;gt; IL
  IL --&amp;amp;gt; DL2
  note bottom of DL2
    ✅ &amp;quot;Sem perdas por construção.&amp;quot;
    Sélection explicite
    des tokens pertinents
  end note
}

MLA --&amp;amp;gt; DSA : &amp;quot;Gain : seleção esparsa
que evita a diluição&amp;quot;

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O relatório técnico diz explicitamente: DSA é &amp;lt;em&amp;gt;&amp;quot;lossless by construction&amp;quot;&amp;lt;/em&amp;gt; — ao contrário das alternativas como SWA (busca por padrão), Gated DeltaNet ou SimpleGDN, que perdem até 5,69 pontos no RULER@128K.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_sessões_11_13_sólido_mas_frustrante&amp;quot;&amp;gt;Sessões 11-13 : sólido mas frustrante&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;GLM-5.1 mantém melhor a distância. Na sessão 11 (~60K tokens), ele permanece coerente. Ele produz um`SnapshotManager`funcional com gestão correta dos quatro anonimizadores&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas a latência é um problema. As fases de reflexão DSA são mais pesadas que o MLA puro — o indexador precisa rescanear o histórico a cada passo. Uma resposta que levava 8 segundos com Kimi passa a levar 15 com GLM. Em uma sessão de 30 mensagens, isso se faz sentir.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E então há os erros sutis. GLM não delira como o Kimi — mas ele faz erros de &amp;lt;strong&amp;gt;nomeação&amp;lt;/strong&amp;gt;. Ele chama`toAnonymizedYaml()&amp;lt;code&amp;gt;o método que deveria ser chamado`anonymize()&amp;lt;/code&amp;gt;. Ele inverte`loadReadmeConfiguration()` et `loadCodebaseConfiguration()`no`renderFileSection()`Não são alucinações, são confusões de superfície — mas em produção, uma confusão de superfície pode quebrar um build.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Tive três sessões com o GLM. É melhor que o Kimi, sem contestação. Mas três sessões de correção manual em detalhes de nomeação, isso cansa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_round_3_deepseek_v4_pro_a_máquina_de_guerra&amp;quot;&amp;gt;Round 3: DeepSeek-V4-Pro — A Máquina de Guerra&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;DeepSeek-V4-Pro chega com a arquitetura mais ambiciosa das três: um sistema híbrido que combina dois mecanismos de atenção complementares.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_arquitetura_csa_hca_o_duplo_filete&amp;quot;&amp;gt;Arquitetura: CSA + HCA, o duplo filete&amp;lt;/h3&amp;gt;
&amp;lt;div id=&amp;quot;diag-csa-hca&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 9) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam wrapWidth 180

title DeepSeek-V4-Pro — Arquitetura Híbrida CSA + HCA
left to right direction

rectangle &amp;quot;Cache KV
^^^^^
 Syntax Error? (Assumed diagram type: class)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam wrapWidth 180

title DeepSeek-V4-Pro — Arquitetura Híbrida CSA + HCA
left to right direction

rectangle &amp;quot;Cache KV
1M tokens&amp;quot; as KV #E3F2FD

rectangle &amp;quot;CSA
Atenção Esparsa Comprimida&amp;quot; as CSA #C8E6C9 {
  rectangle &amp;quot;Compressão
m tokens → 1&amp;quot; as CC
  rectangle &amp;quot;Atenção Esparsa
(DSA, top-k)&amp;quot; as SA
  CC --&amp;amp;gt; SA

  note bottom of SA
    Attention locale fine
    + sélection sparse
  end note
}

rectangle &amp;quot;HCA
Atenção Altamente Comprimida&amp;quot; as HCA #BBDEFB {
  rectangle &amp;quot;Compressão extrema\nm&amp;#39; &amp;amp;gt;&amp;amp;gt; m → 1&amp;quot; as ECC
  rectangle &amp;quot;Atenção densa\nresidual&amp;quot; as EDA
  ECC --&amp;amp;gt; EDA

  note bottom of EDA
    Contexte global
    + connexions longue distance
  end note
}

rectangle &amp;quot;Fusão\nhíbrida&amp;quot; as FUSION #FFF9C4
rectangle &amp;quot;Decodificação
coerente&amp;quot; as DEC #FFE0B2

KV --&amp;amp;gt; CSA : Précision locale
KV --&amp;amp;gt; HCA : Vision globale

CSA --&amp;amp;gt; FUSION
HCA --&amp;amp;gt; FUSION
FUSION --&amp;amp;gt; DEC

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Dois níveis de compressão, duas granularidades de atenção:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;CSA&amp;lt;/strong&amp;gt;: comprime o cache KV todos os`m`tokens, então aplica uma atenção esparsa (DeepSeek Sparse Attention) — apenas o top-k das entradas comprimidas é consultado. Preciso, local, eficiente. - &amp;lt;strong&amp;gt;HCA&amp;lt;/strong&amp;gt;: compressão extrema (fator`m&amp;#39;`muito maior que`m`), mas atenção densa sobre o resíduo comprimido — mantenha as conexões globais sem explosão quadrática.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Resultado: em 1M de tokens, DeepSeek-V4-Pro apenas consome&amp;lt;strong&amp;gt;27% dos FLOPs de inferência&amp;lt;/strong&amp;gt; et &amp;lt;strong&amp;gt;10% do tamanho do cache KV&amp;lt;/strong&amp;gt;em comparação ao DeepSeek-V3.2. O modelo anterior.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E principalmente, o relatório técnico (Figura 9) anuncia: &amp;lt;em&amp;gt;&amp;quot;O desempenho de recuperação permanece altamente estável dentro de uma janela de contexto de 128K.&amp;quot;&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_sessões_1_8_alívio&amp;quot;&amp;gt;Sessões 1-8 : alívio&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eu passei oito sessões com DeepSeek-V4-Pro em`codebase-gradle`. Oito sessões sem uma única alucinação, sem uma única classe inventada, sem uma única confusão de nomenclatura.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sessão 1 : implementação de`CodebaseYmlConfig` et &amp;lt;code&amp;gt;CodebaseYmlAnonymizer&amp;lt;/code&amp;gt;. 350 linhas de Kotlin, testes inline, tudo compila. Sessão 4 : adição do`SnapshotManager`com visualização em árvore, coleta de arquivos, renderização AsciiDoc por arquivo. 279 linhas, zero erros. Sessão 7 : depuração do`renderFileSection()`que precisava gerenciar quatro tipos de anonimizadores diferentes sem ambiguidade de resolução. Resolvido em três mensagens.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A latência é maior que a do Kimi — aproximadamente 10-12 segundos por resposta no modo de pensamento. Mas a taxa de correção é quase nula. Não estou perdendo meu tempo corrigindo os erros do agente. Eu codifico &amp;lt;strong&amp;gt;com&amp;lt;/strong&amp;gt; ele.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_teste_decisivo_refactoring_de_100k_tokens&amp;quot;&amp;gt;O teste decisivo: refactoring de 100K tokens&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Na sessão 8, o contexto cumulado ultrapassa 100K tokens. Peço um refactoring pesado: extrair as quatro tarefas de verificação inline do`build.gradle.kts`para arquivos de teste JUnit5 em`buildSrc/src/test/`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O agente propõe um plano em três fases :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Criar as classes de teste com migração dos casos existentes&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Adicionar as dependências JUnit5 e Kotest em`buildSrc/build.gradle.kts`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Remover o código inline de`build.gradle.kts`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plano está correto. A execução é limpa. Ele identifica até um caso de borda que eu tinha perdido: as dependências Jackson duplicadas entre`buildscript {}` et `buildSrc/build.gradle.kts`que devem ser unificadas durante a migração.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;100K tokens de contexto, e o agente se lembra que`CodebaseYmlAnonymizer.TOKEN_MASK`é`&amp;quot;&amp;lt;strong&amp;gt;*&amp;lt;/strong&amp;gt;&amp;quot;`, que`GitConfig.resolvedToken()&amp;lt;code&amp;gt;é uma função de extensão definida em`readme.kt&amp;lt;/code&amp;gt;, e que`SnapshotManager.PRUNED_DIRS`exclui`build`, &amp;lt;code&amp;gt;.gradle&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;.git&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É isso, a diferença.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_encontro_face_a_face_métricas_comparativas&amp;quot;&amp;gt;Encontro Face a Face: Métricas Comparativas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Critério&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Kimi K2.6&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;GLM-5.1&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;DeepSeek-V4-Pro&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Contexto máximo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;256K&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;200K&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1M&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Mecanismo atenção&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;MLA puro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;MLA + DSA&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;CSA + HCA híbrido&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Parâmetros totais&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1T&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;744B&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1,6T&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Parâmetros ativados&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;32B&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;não publicado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;49B&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Limiar de degradação observado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;(No output, as no French text was provided for translation)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;~120K tokens&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;~128K tokens&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Comportamento além&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Alucinações massivas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Confusões superficiais&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;degradação lenta e progressiva&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Estabilidade NIAH&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;não publicado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;100% @128K (DSA)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;estável até 128K (Figura 9)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;MRCR @128K&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;não publicado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;não publicado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;superior ao Gemini 3.1 Pro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sessões résultatsantes avant abandono&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;8 (e continua)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;tempo de correção / tempo de código&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;60%&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;30%&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;amp;lt;5%&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Latência média (modo think)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;8 s&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;15 s&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;11 s&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Coeficiente de confiança*&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2/10&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;6/10&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;9/10&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;*Coeficiente de confiança = medida subjetiva da minha capacidade de pegar o código do agente e o commit sem revisão linha por linha.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-radar&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 17) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Radar Comparativo — 3 LLMs em Desenvolvimento de Software Assistido
legend right
  |= Couleur |= Modèle |
  | &amp;amp;lt;#FF5252&amp;amp;gt; | Kimi K2.6 |
  | &amp;amp;lt;#FFC107&amp;amp;gt; | GLM-5.1 |
  | &amp;amp;lt;#4CAF50&amp;amp;gt; | DeepSeek-V4-Pro |
endlegend

rectangle &amp;quot; &amp;quot; as space #FFFFFF

rectangle &amp;quot;Coerência\n80K+ tokens&amp;quot; as coh #F5F5F5
rectangle &amp;quot;Qualidade
código&amp;quot; as qual #F5F5F5
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Radar Comparativo — 3 LLMs em Desenvolvimento de Software Assistido
legend right
  |= Couleur |= Modèle |
  | &amp;amp;lt;#FF5252&amp;amp;gt; | Kimi K2.6 |
  | &amp;amp;lt;#FFC107&amp;amp;gt; | GLM-5.1 |
  | &amp;amp;lt;#4CAF50&amp;amp;gt; | DeepSeek-V4-Pro |
endlegend

rectangle &amp;quot; &amp;quot; as space #FFFFFF

rectangle &amp;quot;Coerência\n80K+ tokens&amp;quot; as coh #F5F5F5
rectangle &amp;quot;Qualidade
código&amp;quot; as qual #F5F5F5
rectangle &amp;quot;Raciocínio
arquitetural&amp;quot; as arch #F5F5F5
rectangle &amp;quot;Resistência
alucinações&amp;quot; as hall #F5F5F5
rectangle &amp;quot;Velocidade
(experiência de desenvolvimento)&amp;quot; as speed #F5F5F5
rectangle &amp;quot;Conhecimento
Gradle/Kotlin&amp;quot; as kg #F5F5F5
rectangle &amp;quot;Taxa de
correção&amp;quot; as corr #F5F5F5

coh --&amp;amp;gt; qual
qual --&amp;amp;gt; arch
arch --&amp;amp;gt; hall
hall --&amp;amp;gt; speed
speed --&amp;amp;gt; kg
kg --&amp;amp;gt; corr

note top of coh
  Kimi      ████░░░░░░ 4/10
  GLM       ██████░░░░ 6/10
  DeepSeek  █████████░ 9/10
end note

note top of qual
  Kimi      ███████░░░ 7/10 (sous 60K)
  GLM       ████████░░ 8/10
  DeepSeek  █████████░ 9/10
end note

note top of arch
  Kimi      █████░░░░░ 5/10
  GLM       ███████░░░ 7/10
  DeepSeek  █████████░ 9/10
end note

note top of hall
  Kimi      ██████░░░░ 6/10 → ██░░░░░░░░ 2/10 (&amp;amp;gt; 60K)
  GLM       ███████░░░ 7/10
  DeepSeek  █████████░ 9/10
end note

note top of speed
  Kimi      █████████░ 9/10
  GLM       ██████░░░░ 6/10
  DeepSeek  █████░░░░░ 5/10
end note

note top of kg
  Kimi      ███████░░░ 7/10
  GLM       ███████░░░ 7/10
  DeepSeek  █████████░ 9/10
end note

note top of corr
  Kimi      ██░░░░░░░░ 2/10 (beaucoup de corrections)
  GLM       █████░░░░░ 5/10
  DeepSeek  ██████████ 10/10 (presque rien à corriger)
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_deepseek_v4_pro_ganha_em_desenvolvimento_de_software&amp;quot;&amp;gt;Por que DeepSeek-V4-Pro Ganha em Desenvolvimento de Software&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A superioridade do DeepSeek-V4-Pro não se deve a um único fator — é uma convergência :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_arquitetura_csahca_é_adaptada_para_o_código&amp;quot;&amp;gt;A arquitetura CSA+HCA é adaptada para o código&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O desenvolvimento de software assistido é um caso de uso &amp;lt;strong&amp;gt;extremo&amp;lt;/strong&amp;gt; para a atenção de longo contexto. Você precisa de: - Precisão local (esta classe herda de quê? este método é definido onde?) →&amp;lt;strong&amp;gt;CSA&amp;lt;/strong&amp;gt; - Visão global (por que este módulo existe ? como os quatro anonimizadores interagem ?) →&amp;lt;strong&amp;gt;HCA&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Kimi com MLA sozinho trata a precisão local, mas perde a visão global além de 60K. GLM com DSA melhora a visão global, mas permanece mono-nível. DeepSeek combina os dois explicitamente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_2_o_contexto_eager_é_a_zona_de_conforto&amp;quot;&amp;gt;2. O contexto EAGER é a zona de conforto&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Meu sistema de governança carrega ~30K tokens de regras e de backlog na inicialização. Com uma janela estável até 128K, o DeepSeek tem ~100K tokens de margem para as trocas da sessão. É de 3 a 4 vezes mais do que o Kimi consegue lidar sem degradação.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;(Empty output) A janela de 128K de estabilidade perfeita corresponde exatamente às minhas necessidades : 30K de EAGER + 70K de trocas = uma sessão produtiva de 20-30 mensagens sem nunca sair da zona verde. &amp;lt;em&amp;gt;__&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_3_a_relação_qualidadelatência_é_ideal_para_o_fluxo&amp;quot;&amp;gt;3. A relação qualidade/latência é ideal para o fluxo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sim, DeepSeek-V4-Pro é mais lento que o Kimi (11s vs 8s). Mas o tempo &amp;lt;strong&amp;gt;total&amp;lt;/strong&amp;gt; de uma tarefa é bem menor porque eu não gasto 20 minutos corrigindo as alucinações do agente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O desenvolvedor não mede a latência de uma resposta. Ele mede o tempo entre &amp;quot;eu faço a pergunta&amp;quot; e &amp;quot;o código está no meu repositório e funciona&amp;quot;. Nesta métrica, o DeepSeek-V4-Pro é o mais rápido dos três.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_lições_aprendidas_como_escolher_seu_llm_para_o_vibe_coding&amp;quot;&amp;gt;Lições Aprendidas: Como Escolher seu LLM para o Vibe Coding&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Além da classificação pontual, esta experiência me ensinou a avaliar um LLM para o desenvolvimento de software assistido em critérios que não constam de nenhum benchmark:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Olha a arquitetura, não o tamanho do contexto anunciado&amp;lt;/strong&amp;gt;— Um modelo que anuncia 256K de contexto mas tem apenas um MLA terminará como Kimi: teoricamente capaz, praticamente inutilizável além de 60K.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Teste no TEU contexto, não em um benchmark genérico&amp;lt;/strong&amp;gt;— Meu prompt inicial de 30K tokens AsciiDoc com regras absolutas e backlog não tem nada a ver com as questões de HLE ou AIME.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A latência não é a inimiga se a qualidade acompanha&amp;lt;/strong&amp;gt;— Um modelo lento que produz código correto é mais rápido do que um modelo rápido que produz código errado.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Cuidado com os modelos sem relatório técnico público&amp;lt;/strong&amp;gt;— Se a equipe não documentar sua arquitetura de atenção, é porque ela não confia em seu desempenho em longo contexto.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-decision-tree&amp;quot; class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-eef823d5be6fd380ecbee716465cd526.svg&amp;quot; alt=&amp;quot;Arbre de décision pour choisir un LLM pour le développement logiciel assisté&amp;quot; width=&amp;quot;827&amp;quot; height=&amp;quot;461&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_e_a_governança_agent_em_tudo_isso&amp;quot;&amp;gt;E a Governança Agent em tudo isso?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta experiência valida um ponto que defendo desde meu artigo sobre a governança Eager/Lazy :&amp;lt;strong&amp;gt;a qualidade do LLM e a qualidade da governança são multiplicativas, não aditivas&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com o Kimi K2.6, minha governança estava impecável — mas o modelo diluía a informação. Resultado: governança perfeita × alucinação = zero.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com o DeepSeek-V4-Pro, a governança EAGER (30K tokens de regras) + LAZY (arquivos de sessão, referências técnicas) + Hot/Warm/Cold (backup rotativo) forma um ecossistema onde cada camada amplifica a outra. O agente tem as regras à vista (EAGER), pode consultar o histórico (LAZY), e o contexto nunca fica saturado (rotação de backup).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ Um bom LLM sem governança, é um motor de Ferrari sem volante. Uma boa governança sem bom LLM, é um volante sem motor. DeepSeek-V4-Pro + minha governança agente = a primeira vez que tenho a sensação de dirigir. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O mecanismo de backup rotativo (rotação a cada 10 sessões ou &amp;amp;gt;500 linhas EAGER) faz todo sentido com um modelo que suporta 128K de contexto: a sliding window de 10 sessões ativas mantém o contexto atualizado sem jamais ultrapassar a zona de degradação.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_fontes_técnicas&amp;quot;&amp;gt;Fontes Técnicas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cruzei minha experiência empírica com os relatórios técnicos oficiais para validar minhas observações :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;DeepSeek-V4 Technical Report&amp;lt;/strong&amp;gt; — PDF obtido desdehttps://huggingface.co/deepseek-ai/DeepSeek-V4-Flash[HuggingFace]. Figura 9 : &amp;quot;Retrieval performance remains highly stable within a 128K context window. While a performance degradation becomes visible beyond the 128K mark, the model&amp;amp;#8217;s retrieval capabilities at 1M tokens remain remarkably strong.&amp;quot; Arquitetura CSA+HCA documentada secção 2.3.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;GLM-5 Relatório Técnico&amp;lt;/strong&amp;gt; —https://arxiv.org/abs/2602.15763[arXiv:2602.15763]. DSA introduzido via pré-treinamento contínuo, &amp;quot;lossless by construction&amp;quot;. Contexto máximo 202.752 tokens. Tabelas 3/5/6 documentam o desempenho de longo contexto em comparação com alternativas (SWA, Gated DeltaNet, SimpleGDN).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Kimi K2.6 Model Card&amp;lt;/strong&amp;gt; —https://huggingface.co/moonshotai/Kimi-K2.6[HuggingFace]. MLA, 256K contexto máximo. Estratégia de discard-all além do limiar nas tarefas agenteicas (confirmando implicitamente o limite prático do MLA em longo contexto).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Kimi K2 Relatório Técnico&amp;lt;/strong&amp;gt; —https://arxiv.org/abs/2507.20534[arXiv:2507.20534]. Arquitetura MoE, otimizador MuonClip, desempenhos agentivos (HLE, BrowseComp, Terminal-Bench).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O ponto mais revelador: em sua própria avaliação, Moonshot aplica uma estratégia de &amp;lt;code&amp;gt;discard-all&amp;lt;/code&amp;gt; (supressão do contexto antigo) assim que a janela de contexto é ultrapassada. Isso confirma exatamente o que observei: o MLA sozinho não consegue manter a coerência em toda a janela de 256K. A arquitetura não acompanha.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_a_escolha_do_artesão&amp;quot;&amp;gt;Conclusão: A Escolha do Artesão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Depois de três semanas e treze sessões de desenvolvimento real, o veredicto é irrecorrível :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 8.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 8.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 8.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 8.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 8.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 8.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 8.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 8.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 8.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 8.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 8.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 8.3337%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Kimi K2.6&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;GLM-5.1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;DeepSeek-V4-Pro&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Excelente menos de 60K&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sólido até 120K&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Domine em toda parte&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Inutilizável além&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Confusões superficiais&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Estável até 128K&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2 sessões, abandonado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;3 sessões, abandonado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;8 sessões, adotado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;DeepSeek-V4-Pro tornou-se meu LLM padrão para todas as sessões de desenvolvimento de software assistido com Opencode. Não porque é o mais recente. Não porque tem os melhores benchmarks acadêmicos. Mas porque, na vida real de um desenvolvedor que está enviando plugins Gradle Kotlin DSL com um contexto de agente de 30K tokens, é o único que não me trai quando o contexto se expande.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O CSA+HCA é um &amp;lt;strong&amp;gt;game changer&amp;lt;/strong&amp;gt; arquitetural. A compressão de nível duplo + esparsificação não é um detalhe de implementação — é o que faz a diferença entre um assistente de código e um colega confiável.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ Escolhi o DeepSeek-V4-Pro porque é o único dos três que transforma minha governança de agente de uma restrição defensiva (&amp;quot;como evitar que o agente esqueça?&amp;quot;) em uma vantagem ofensiva (&amp;quot;o que podemos construir agora que o agente se lembra de tudo?&amp;quot;). &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;*Este artigo é o resultado de 13 sessões de desenvolvimento real no projeto`codebase-gradle`, documentadas em`.agents/sessions/`de acordo com a minha metodologia de governança agente Eager/Lazy/Hot/Warm/Cold. As fontes técnicas citadas são acessíveis publicamente no HuggingFace e arXiv.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Auditoria de Contexto Agente: Quando Sua Própria Governança se torna o Problema — e Como Repará-la sem perder nada</title>
            <link >https://cheroliv.com//blog/2026/0111_audit_contexte_agent_post.html</link>
            <pubDate>Mon, 27 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0111_audit_contexte_agent_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_cena_sessão_051_algo_está_errado&amp;quot;&amp;gt;A cena: sessão 051, algo está errado&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_auditoria_analisar_as_627_linhas&amp;quot;&amp;gt;A auditoria: analisar as 627 linhas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_medo_de_perder_a_engenharia&amp;quot;&amp;gt;O medo de perder a engenharia&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_split_enciclopédico&amp;quot;&amp;gt;A solução : split enciclopédico&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_conteúdo_do_ficheiro_essentials&amp;quot;&amp;gt;O conteúdo do ficheiro ESSENTIALS&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_resultado_50_de_contexto_eager&amp;quot;&amp;gt;O resultado: -50% de contexto EAGER&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_a_pasta_se_chama_encyclopedies&amp;quot;&amp;gt;Por que a pasta se chama `encyclopedies/&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_continuação_lógica_da_série&amp;quot;&amp;gt;A continuação lógica da série&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_eu_teria_feito_diferente&amp;quot;&amp;gt;O que eu teria feito diferente&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_guia_para_auditar_seu_próprio_contexto&amp;quot;&amp;gt;O guia para auditar seu próprio contexto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_uma_governança_viva&amp;quot;&amp;gt;Uma governança viva&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_links&amp;quot;&amp;gt;Links&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 14 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Você passou semanas construindo uma governança de agente impecável. Eager/Lazy, procedimento de fim de sessão, backups rotativos. O sistema está funcionando. Então, um dia, o agente fica lento. As respostas ficam diluídas. Você mede: 1414 linhas carregadas automaticamente. E o pior é que o culpado não é o código, nem o backlog, nem os arquivos de sessão. O culpado é o arquivo que documenta seu método.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;tic&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_cena_sessão_051_algo_está_errado&amp;quot;&amp;gt;A cena: sessão 051, algo está errado&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;30 de abril de 2026, 16h00. Estou em plena sessão sobre`magic-stick`, meu projeto de build da ISO Linux live. O agente Opencode carregou automaticamente meus arquivos Eager como sempre —&amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PROMPT_REPRISE.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.agents/INDEX.adoc&amp;lt;/code&amp;gt;. Tudo está normal.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas as respostas são mornas. O agente leva dois segundos a mais para raciocinar. Ele esquece um detalhe que tinha sob o nariz três mensagens antes. Não é um crash nem um erro — é uma degradação lenta, do tipo que não se percebe imediatamente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eu já tinha vivido isso na sessão 048, quando eu tinha descoberto que os arquivos de governança pesavam 5200 linhas acumuladas. Eu então tinha conceptualizado o mecanismo Hot/Warm/Cold com sua sliding window de 10 sessões, suas ondas frias idênticas, seu sensor com dois gatilhos. O problema estava resolvido. Em teoria.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas aqui, estamos na sessão 051. A rotação de backup ocorreu — os arquivos EAGER passaram de 2087 para 500 linhas. Porém, o contexto ainda está pesado. Algo me escapa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Abro um terminal e digito :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;wc -l AGENT.adoc AGENT_MODUS_OPERANDI.adoc PROMPT_REPRISE.adoc \
  .agents/INDEX.adoc .agents/SESSIONS_HISTORY.adoc \
  .agents/archives/COMPLETED_TASKS_ARCHIVE_2026-04.adoc&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;  287 AGENT.adoc
  627 AGENT_MODUS_OPERANDI.adoc
   51 PROMPT_REPRISE.adoc
  218 .agents/INDEX.adoc
   18 .agents/SESSIONS_HISTORY.adoc
  213 .agents/archives/COMPLETED_TASKS_ARCHIVE_2026-04.adoc
 1414 total&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;1414 linhas. A rotação de backup cortou bem em INDEX, SESSIONS_HISTORY e COMPLETED_TASKS — eles estão limpos. Mas há um elefante na sala que eu não tinha visto:&amp;lt;strong&amp;gt;627 linhas&amp;lt;/strong&amp;gt;em um único arquivo.&amp;lt;code&amp;gt;AGENT_MODUS_OPERANDI.adoc&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este arquivo, esse é o que escrevi na sessão 1 para documentar a estratégia Eager/Lazy. É o manual do método. E ele se tornou, sozinho, 44% do contexto EAGER.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-before-audit&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 9) ]

@startuml
skinparam backgroundColor #FEFEFE

title Contexto EAGER — 1414 linhas
rectangle &amp;quot;AGENT_MODUS_OPERANDI\n**627 linhas (44%)**&amp;quot; as MOD #FFCDD2
rectangle &amp;quot;AGENT.adoc\n287 linhas (20%)&amp;quot; as AG #BBDEFB
rectangle &amp;quot;INDEX.adoc\n218 linhas (15%)&amp;quot; as IDX #C8E6C9
rectangle &amp;quot;COMPLETED_TASKS
213 linhas (15%)&amp;quot; as ARC #FFF9C4
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE

title Contexto EAGER — 1414 linhas
rectangle &amp;quot;AGENT_MODUS_OPERANDI\n**627 linhas (44%)**&amp;quot; as MOD #FFCDD2
rectangle &amp;quot;AGENT.adoc\n287 linhas (20%)&amp;quot; as AG #BBDEFB
rectangle &amp;quot;INDEX.adoc\n218 linhas (15%)&amp;quot; as IDX #C8E6C9
rectangle &amp;quot;COMPLETED_TASKS
213 linhas (15%)&amp;quot; as ARC #FFF9C4
rectangle &amp;quot;PROMPT_REPRISE
51 linhas (4%)&amp;quot; as PRO #E1BEE7
rectangle &amp;quot;SESSIONS_HISTORY
18 linhas (1%)&amp;quot; as HIS #FFE0B2

note bottom of MOD
  Le fichier qui documente
  la méthode est devenu
  le plus gros poste de dépense
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A ironia é total. O arquivo projetado para &amp;lt;strong&amp;gt;economizar&amp;lt;/strong&amp;gt; de contexto tornou-se o principal &amp;lt;strong&amp;gt;consumidor&amp;lt;/strong&amp;gt; de contexto. É como se o manual de uso do seu carro pesasse mais que o motor.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_auditoria_analisar_as_627_linhas&amp;quot;&amp;gt;A auditoria: analisar as 627 linhas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Decido fazer uma auditoria seção por seção. Não para remover — para &amp;lt;strong&amp;gt;compreender&amp;lt;/strong&amp;gt; o que merece estar em EAGER e o que poderia viver em outro lugar sem perda de conhecimento.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eis a estrutura exata de`AGENT_MODUS_OPERANDI.adoc`e o que eu encontro:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2727%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.2739%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Seção&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Linhas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Conteúdo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Já presente em&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;P1 — Visão geral&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;64&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Problema resolvido, princípios fundamentais, analogia painel de instrumentos vs manual&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;—&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;P2 — Estrutura dos ficheiros&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;97&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Arborescência, política de carregamento, quando carregar o quê&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;—&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;P3 — Regras absolutas&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;34&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Git proibido, comandos destrutivos proibidos, segredos proibidos&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;P4 — Ciclo de vida da sessão&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;133&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Modelo abertura, regras de trabalho, 6 etapas fim de sessão, checklist&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt;§Fim sessão +&amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;P5 — Métricas e limites&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;49&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sessão ideal (15-30 min, 1-3 ficheiros), sinais de alerta&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;—&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;P6 — Tipos de sessões&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;72&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tabela de detecção, formato da proposta, exceções&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;—&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;P7 — Melhoria contínua&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;39&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Métricas de acompanhamento, revisão semanal&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;—&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;P8 — Checklist inicialização&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;13&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;novo projeto Bootstrap&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;—&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;P9 — Referências&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;30&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Arquivos de referência, recursos externos&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt;(arquivos EAGER/LAZY)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Anexos A+B&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;42&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Glossário, histórico de versões&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;—&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O veredicto é irrecorrível:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Duplicatas integrais&amp;lt;/strong&amp;gt;(167 linhas): P3, P4, P9 — tudo já está dentro`AGENT.adoc` ou &amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt;, frequentemente palavra por palavra&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Nunca consultado em prática&amp;lt;/strong&amp;gt;(215 linhas) : P5, P6, P7, P8, Anexos — da meta-governança que nunca foi utilizada em nenhuma sessão&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Conhecimento único&amp;lt;/strong&amp;gt;(161 linhas) : P1 e P2 — o vocabulário LAZY/EAGER, a analogia, a política de carregamento&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em 627 linhas,&amp;lt;strong&amp;gt;382 são barulho&amp;lt;/strong&amp;gt;. E essas 382 linhas são carregadas &amp;lt;strong&amp;gt;a cada início de sessão&amp;lt;/strong&amp;gt;, consumidas pelo agente, diluindo sua atenção mesmo antes de eu dizer olá.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-audit-result&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 8) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Auditoria de AGENT_MODUS_OPERANDI.adoc — 627 linhas
left to right direction

rectangle &amp;quot;🟡 **Duplicados**
^^^^^
 Syntax Error? (Assumed diagram type: class)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Auditoria de AGENT_MODUS_OPERANDI.adoc — 627 linhas
left to right direction

rectangle &amp;quot;🟡 **Duplicados**
167 linhas (27%)&amp;quot; as DUP #FFF9C4 {
  card &amp;quot;P3 — Regras absolutas
(34 linhas)&amp;quot; as D1
  card &amp;quot;P4 — Ciclo de vida\n(133 linhas)&amp;quot; as D2
  card &amp;quot;P9 — Referências\n(30 linhas)&amp;quot; as D3
}

rectangle &amp;quot;�🔴 **Nunca consultado**
215 linhas (34%)&amp;quot; as JAM #FFCDD2 {
  card &amp;quot;P5 — Métricas\n(49 linhas)&amp;quot; as J1
  card &amp;quot;P6 — Detecção
(72 linhas)&amp;quot; as J2
  card &amp;quot;P7 — Melhoria
(39 linhas)&amp;quot; as J3
  card &amp;quot;P8 — Bootstrap
(13 linhas)&amp;quot; as J4
  card &amp;quot;Anexos — Glossário
(42 linhas)&amp;quot; as J5
}

rectangle &amp;quot;�🟢 **Conhecimento único**
161 linhas (26%)&amp;quot; as UNI #C8E6C9 {
  card &amp;quot;P1 — Visão geral
(64 linhas)&amp;quot; as U1
  card &amp;quot;P2 — Estrutura dos arquivos
(97 linhas)&amp;quot; as U2
}

note bottom of UNI
  C&amp;#39;est ÇA qu&amp;#39;il faut garder en EAGER.
  Le reste → cold storage.
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este diagrama é a chave de tudo. Os duplicados (amarelo) são puro ruído — o agente os lê duas vezes, em dois arquivos diferentes. O nunca consultado (vermelho) é documentação morta — escrito com cuidado, nunca usado. Só o verde contém conhecimento que o agente não pode encontrar em outro lugar.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_medo_de_perder_a_engenharia&amp;quot;&amp;gt;O medo de perder a engenharia&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Neste momento, tenho a evidência diante de mim: devo reduzir`AGENT_MODUS_OPERANDI.adoc`. Mas eu hesito.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este arquivo, eu escrevi à mão. Cada seção é o resultado de uma lição aprendida em uma sessão real. A parte P3 nasceu de um`rm -rf`acidental em`bakery-plugin`que me custou tokens reais do Firebase. A parte P4 é o resultado de quinze sessões em que eu esquecia de arquivar e perdia o fio. O procedimento em 6 etapas não saiu de um livro — saiu da dor.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Apagar essas seções seria jogar fora o histórico da minha própria engenharia. As lições que as produziram, as sessões durante as quais as descobri, os erros que nunca mais quero cometer. Não é texto — é experiência cristalizada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É aqui que formulo o princípio que vai orientar a solução:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ &amp;lt;strong&amp;gt;Nunca excluir. Sempre relocalizar.&amp;lt;/strong&amp;gt;O conhecimento não precisa desaparecer do projeto. Ele apenas precisa não ser carregado automaticamente quando não for necessário. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_split_enciclopédico&amp;quot;&amp;gt;A solução : split enciclopédico&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O modelo que proponho é simples e se inspira na forma como a Wikipédia cresceu: quando um artigo fica muito longo, não o cortamos — criamos um artigo detalhado e mantemos um resumo no artigo principal.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;para`AGENT_MODUS_OPERANDI.adoc`, isso resulta :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Extrair&amp;lt;/strong&amp;gt;as 161 linhas de conhecimento único (P1 + P2) em um novo arquivo`LAZY_EAGER_ESSENTIALS.adoc`— compactado em ~50 linhas, estritamente EAGER&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;renomear&amp;lt;/strong&amp;gt; &amp;lt;code&amp;gt;AGENT_MODUS_OPERANDI.adoc&amp;lt;/code&amp;gt; en &amp;lt;code&amp;gt;.agents/encyclopedies/LAZY_EAGER_ENCYCLOPEDIE.adoc&amp;lt;/code&amp;gt;— o arquivo original de 627 linhas, preservado &amp;lt;strong&amp;gt;intact&amp;lt;/strong&amp;gt;, em armazenamento a frio&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Nunca carregar&amp;lt;/strong&amp;gt;o arquivo enciclopédia automaticamente — ele está lá para o ser humano, para a destilação futura, para o agente que será invocado daqui a seis meses com um modelo melhor&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-split-solution&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 8) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Split Enciclopédico — AGENT_MODUS_OPERANDI
left to right direction

rectangle &amp;quot;**AGENT_MODUS_OPERANDI.adoc**
^^^^^
 Syntax Error? (Assumed diagram type: class)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Split Enciclopédico — AGENT_MODUS_OPERANDI
left to right direction

rectangle &amp;quot;**AGENT_MODUS_OPERANDI.adoc**
627 linhas
(antes do split)&amp;quot; as BEFORE #FFCDD2 {
}

rectangle &amp;quot; &amp;quot; as ARROW1
rectangle &amp;quot; &amp;quot; as ARROW2

rectangle &amp;quot;**LAZY_EAGER_ESSENTIALS.adoc**
**~50 linhas — EAGER**&amp;quot; as ESS #C8E6C9 {
  card &amp;quot;P1 condensada
(15 linhas)&amp;quot; as E1
  card &amp;quot;P2 condensada\n(35 linhas)&amp;quot; as E2
}

rectangle &amp;quot;.agents/encyclopedies/**\n**LAZY_EAGER_ENCYCLOPEDIE.adoc**\n627 linhas — COLD&amp;quot; as ENC #BBDEFB {
  card &amp;quot;P1 para Anexos
(integrais, intactos)&amp;quot; as C1
}

BEFORE -[#4CAF50]-&amp;amp;gt; ESS : &amp;quot;Extração de
o conhecimento crítico&amp;quot;
BEFORE -[#2196F3]-&amp;amp;gt; ENC : &amp;quot;Preservação integral
da engenharia&amp;quot;

note bottom of ESS
  Chargé automatiquement
  en début de session
  → L&amp;#39;agent comprend le vocabulaire
end note

note bottom of ENC
  Jamais chargé automatiquement
  Consultable par l&amp;#39;humain
  Matériau brut pour distillation future
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O nome`encyclopedies/`Não é insignificante. Uma enciclopédia não é um dossiê de arquivos — é uma coleção de conhecimento organizada, consultável mas não portátil. Não se lê a Encyclopédie Universalis no metrô. Guardamo-la na biblioteca, e vamos lá quando temos uma pergunta específica.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É exatamente o papel desta pasta: uma biblioteca de referência fria, estruturada, exaustiva — que o agente não toca automaticamente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_conteúdo_do_ficheiro_essentials&amp;quot;&amp;gt;O conteúdo do ficheiro ESSENTIALS&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está como o novo arquivo se parece concretamente`LAZY_EAGER_ESSENTIALS.adoc`, extraído e compactado a partir de P1 e P2 :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;= Stratégie LAZY/EAGER — Principes Essentiels

[abstract]
Ce fichier définit la stratégie de gestion du contexte agent.
Chargé automatiquement (EAGER) en début de session.

== Principes

|===
| EAGER | LAZY
| Tableau de bord | Manuel du propriétaire
| Chargé automatiquement | Chargé sur demande
| &amp;amp;lt;= 100 lignes, &amp;amp;lt;= 10k tokens | Illimité, détaillé
| Règles absolues, mission courante | Archives, historique, références
|===

== Politique de Chargement

|===
| Fichier | Type | Quand charger
| PROMPT_REPRISE.adoc | EAGER | Début session (auto)
| *_ESSENTIALS.adoc | EAGER | Début session si EPIC active
| .agents/INDEX.adoc | EAGER | Début session (auto)
| *_REFERENCE.adoc | LAZY | Sur besoin (détails architecture)
| .agents/sessions/N-*.adoc | LAZY | Sur demande (détails session)
| .agents/encyclopedies/*.adoc | COLD | Jamais auto (humain seulement)
|===

== Comment l&amp;#39;Agent Sait Quoi Charger

Début session → PROMPT_REPRISE + INDEX + ESSENTIALS actifs.
Besoin de détails → charger les *_REFERENCE et sessions/ en LAZY.
Connaissance froide → encyclopedies/, jamais automatique.&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cinquenta linhas. É isso que o agente precisa para entender a mecânica. Tudo o mais — o histórico das lições, as analogias detalhadas, os procedimentos passo a passo, os anexos — vive na enciclopédia.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E o mais importante:&amp;lt;strong&amp;gt;nada foi excluído&amp;lt;/strong&amp;gt;. Os 627 linhas de engenharia ainda estão lá, em`.agents/encyclopedies/LAZY_EAGER_ENCYCLOPEDIE.adoc`. Elas estão apenas guardadas na biblioteca, em vez de no balcão de trabalho.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_resultado_50_de_contexto_eager&amp;quot;&amp;gt;O resultado: -50% de contexto EAGER&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes do split :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt; 287 AGENT.adoc
 627 AGENT_MODUS_OPERANDI.adoc
  51 PROMPT_REPRISE.adoc
 218 .agents/INDEX.adoc
  18 .agents/SESSIONS_HISTORY.adoc
 213 .agents/archives/COMPLETED_TASKS_ARCHIVE_2026-04.adoc
1414 total&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Após o split :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt; 287 AGENT.adoc
  50 LAZY_EAGER_ESSENTIALS.adoc       ← remplace 627 lignes
  51 PROMPT_REPRISE.adoc
 218 .agents/INDEX.adoc
  18 .agents/SESSIONS_HISTORY.adoc
 213 .agents/archives/COMPLETED_TASKS_ARCHIVE_2026-04.adoc
 837 total                              ← -41%&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-after-audit&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE

title Contexto EAGER após split enciclopédico — 837 linhas (-41%)
rectangle &amp;quot;AGENT.adoc
287 linhas (34%)&amp;quot; as AG #BBDEFB
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE

title Contexto EAGER após split enciclopédico — 837 linhas (-41%)
rectangle &amp;quot;AGENT.adoc
287 linhas (34%)&amp;quot; as AG #BBDEFB
rectangle &amp;quot;INDEX.adoc
218 linhas (26%)&amp;quot; as IDX #C8E6C9
rectangle &amp;quot;COMPLETED_TASKS\n213 linhas (25%)&amp;quot; as ARC #FFF9C4
rectangle &amp;quot;PROMPT_REPRISE
51 linhas (6%)&amp;quot; as PRO #E1BEE7
rectangle &amp;quot;ESSENTIALS
50 linhas (6%)&amp;quot; as ESS #A5D6A7
rectangle &amp;quot;SESSIONS_HISTORY\n18 linhas (2%)&amp;quot; as HIS #FFE0B2

note bottom of ESS
  De 627 → 50 lignes
  577 lignes d&amp;#39;ingénierie
  préservées en cold storage

  Rien de perdu. Tout de relocalisé.
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O maior consumidor (AGENT_MODUS_OPERANDI, 627 linhas) foi substituído por um arquivo de 50 linhas. O ganho é imediato: 577 linhas de contexto liberadas. O agente respira.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E dentro`.agents/encyclopedies/`, o arquivo original de 627 linhas espera. Intacto. Com todas as suas seções — incluindo aquelas que são duplicatas, incluindo aquelas que nunca foram usadas. Porque um dia, um modelo melhor ou um cientista de dados humano quererá cruzar esses ângulos, essas redundâncias assumidas, essas lições aprendidas. E nesse dia, a matéria-prima estará lá.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_a_pasta_se_chama_encyclopedies&amp;quot;&amp;gt;Por que a pasta se chama `encyclopedies/&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A escolha do nome não é cosmética. Ele codifica a filosofia do mecanismo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um &amp;lt;strong&amp;gt;arquivo&amp;lt;/strong&amp;gt;.&amp;lt;code&amp;gt;archives/&amp;lt;/code&amp;gt;) contém dados históricos organizados cronologicamente — como os COMPLETED_TASKS mensais. Um &amp;lt;strong&amp;gt;backup&amp;lt;/strong&amp;gt; (&amp;lt;code&amp;gt;backup/&amp;lt;/code&amp;gt;) é um snapshot com carimbo de data/hora — uma cópia de segurança de um estado passado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Uma &amp;lt;strong&amp;gt;enciclopédia&amp;lt;/strong&amp;gt;, é outra coisa. É uma coleção de conhecimento temático, estruturada por assunto, exaustiva mas não linear. Não se lê uma enciclopédia do início ao fim. início ao fim. Mergulha-se nela para responder a uma pergunta específica.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É exatamente o contrato deste dossiê :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Dossiê&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Função&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Acesso&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Granularidade&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;archives/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Dados históricos (tarefas concluídas por mês)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;LAZY, estruturado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Cronológico&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;backup/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Snapshots frios (ondas de 10 sessões)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;FRIO, cópia integral&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;cronológico&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;encyclopedies/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Conhecimento temático exaustivo (metodologia, padrões)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;FRIO, nunca automático&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Temática&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta distinção evita o fourre-tout. Cada arquivo sabe onde deve viver de acordo com &amp;lt;strong&amp;gt;o que contém&amp;lt;/strong&amp;gt;, não de acordo com &amp;lt;strong&amp;gt;quando foi criado&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_continuação_lógica_da_série&amp;quot;&amp;gt;A continuação lógica da série&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se você leu os dois primeiros artigos, eis como as três peças se encaixam:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-trilogie&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 9) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam wrapWidth 220

title Trilogia da Governança do Agente
left to right direction

package &amp;quot;�📖 **Artigo 0108**
^^^^^
 Syntax Error? (Assumed diagram type: class)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam wrapWidth 220

title Trilogia da Governança do Agente
left to right direction

package &amp;quot;�📖 **Artigo 0108**
Estratégia Eager/Lazy&amp;quot; as A1 #E8F5E9 {
  card &amp;quot;agente de memória
entre sessões&amp;quot; as M1
  card &amp;quot;Arquivos EAGER\n(auto-carregados)&amp;quot; as M2
  card &amp;quot;Arquivos LAZY\n(sob demanda)&amp;quot; as M3
  card &amp;quot;Procedimento de 6 etapas&amp;quot; as M4
}

package &amp;quot;🧊 **Artigo 0110**
Mecanismo Hot/Warm/Cold&amp;quot; as A2 #FFF9C4 {
  card &amp;quot;Envelhecimento
do contexto&amp;quot; as V1
  card &amp;quot;Janela deslizante
10 sessões&amp;quot; as V2
  card &amp;quot;Onda fria
idêntica&amp;quot; as V3
  card &amp;quot;Sensor de
dois gatilhos&amp;quot; as V4
}

package &amp;quot;🔍 **Artigo 0111**\nAuditoria + Solução Enciclopédica&amp;quot; as A3 #BBDEFB {
  card &amp;quot;Auditoria dos ficheiros\nEAGER&amp;quot; as E1
  card &amp;quot;Identificação\ndo desperdício&amp;quot; as E2
  card &amp;quot;Dividir
ESSENTIALS/enciclopédia&amp;quot; as E3
  card &amp;quot;Preservação
da engenharia&amp;quot; as E4
}

A1 --&amp;amp;gt; A2 : &amp;quot;A memória cresce
→ é preciso um mecanismo
de envelhecimento&amp;quot;
A2 --&amp;amp;gt; A3 : &amp;quot;O envelhecimento não é suficiente
→ é necessário auditar o que
merece estar em EAGER&amp;quot;

note bottom of A3
  ✅ Les trois couches sont en place
  sur 6 projets actifs
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;0108&amp;lt;/strong&amp;gt;responde a : «Como dar uma memória ao agente entre duas sessões?&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;0110&amp;lt;/strong&amp;gt;responde a : « Como impedir que essa memória afogue o agente ?&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;0111&amp;lt;/strong&amp;gt;responde a: « E se o problema não for o tamanho dos arquivos, mas o que temos &amp;lt;strong&amp;gt;escolhido&amp;lt;/strong&amp;gt; de colocar em EAGER ?&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O primeiro artigo construiu a estrutura. O segundo adicionou o mecanismo de envelhecimento. O terceiro audita o conteúdo — e descobre que a própria metodologia se tornou o problema.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_que_eu_teria_feito_diferente&amp;quot;&amp;gt;O que eu teria feito diferente&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Retrospectivamente, vejo o erro de projeto inicial.`AGENT_MODUS_OPERANDI.adoc`a foi criado como um documento único — um manifesto. Essa foi a abordagem correta para formalizar o pensamento. Mas uma vez que o pensamento foi formalizado, o documento deveria ter sido dividido imediatamente: o essencial em EAGER, o exaustivo em cold.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não o fiz porque estava orgulhoso do documento. 627 linhas de engenharia pura, escritas à mão, cada seção fruto de uma lição de sessão. Era minha obra. E, como todo autor, tive dificuldade em cortá-lo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A lição:&amp;lt;strong&amp;gt;Não é porque um documento é bom que ele deve ser carregado automaticamente&amp;lt;/strong&amp;gt;. A qualidade do conteúdo não tem qualquer relação com sua relevância para o contexto imediato do agente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Hoje, a regra é simples: todo documento com mais de 100 linhas no contexto EAGER é um suspeito. Ele merece uma auditoria. Não uma condenação — uma auditoria. E a questão nunca é « devemos exclu‑lo? » mas « devemos carregá‑lo a cada sessão?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_guia_para_auditar_seu_próprio_contexto&amp;quot;&amp;gt;O guia para auditar seu próprio contexto&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se você seguiu os dois primeiros artigos e implementou sua própria governança Eager/Lazy, aqui está um procedimento de auditoria em cinco etapas:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Medir&amp;lt;/strong&amp;gt;:`wc -l`em todos os seus arquivos EAGER. O total deveria ser menos de 1000 linhas.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Identificar o maior&amp;lt;/strong&amp;gt;: o arquivo que pesa mais de 20% do total é o suspeito número um.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Auditar seção por seção&amp;lt;/strong&amp;gt;: para cada seção, pergunte-se « Essa informação já está em outro lugar? O agente já leu isso em outro arquivo? Isso foi usado nas últimas 5 sessões?&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Classificador&amp;lt;/strong&amp;gt;: duplicado, nunca usado, conhecimento único.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Divisor ou relocalizar&amp;lt;/strong&amp;gt;: o que é único e crítico → ESSENTIALS compactado. Tudo o resto →&amp;lt;code&amp;gt;encyclopedies/&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-audit-checklist&amp;quot; class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-95d11b67afdc5242df4fd25cd1d42827.svg&amp;quot; alt=&amp;quot;Procédure d&amp;amp;#8217;audit en 5 étapes&amp;quot; width=&amp;quot;533&amp;quot; height=&amp;quot;741&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este procedimento leva 15 minutos. Em um projeto de 50 sessões, ele economiza centenas de tokens por sessão futura. O ROI é imediato.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_uma_governança_viva&amp;quot;&amp;gt;Uma governança viva&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O que esta série de três artigos me ensinou é que a governança de agentes não é um produto final. É un&amp;lt;strong&amp;gt;organismo vivo&amp;lt;/strong&amp;gt;. Ela cresce com o projeto. Ela pega doenças de crescimento. Ela precisa de check-ups regulares.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sessão 048 revelou que os arquivos estão inflando. Sessão 051 revelou que a própria metodologia está inflando. Sessão 060 provavelmente revelará outra coisa. É normal. É saudável. Uma governança que nunca se questiona é uma governança morta.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os três mecanismos implementados — Eager/Lazy, Hot/Warm/Cold, split enciclopédico — formam um sistema de defesa em profundidade contra a saturação do contexto. Nenhum é suficiente por si só. Juntos, eles se complementam :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ansioso/Preguiçoso&amp;lt;/strong&amp;gt;estrutura a informação &amp;lt;strong&amp;gt;por disponibilidade&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Quente/Morno/Frio&amp;lt;/strong&amp;gt;estrutura a informação &amp;lt;strong&amp;gt;pela frescura&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Auditoria enciclopédica&amp;lt;/strong&amp;gt;Estrutura a informação &amp;lt;strong&amp;gt;por densidade&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-defense-in-depth&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam nodeBackgroundColor #E3F2FD

title Defesa em Profundidade contra a Saturação do Contexto
node &amp;quot;**Contexto Agente**
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE
skinparam nodeBackgroundColor #E3F2FD

title Defesa em Profundidade contra a Saturação do Contexto
node &amp;quot;**Contexto Agente**
~800 linhas
saudável&amp;quot; as CTX #C8E6C9

node &amp;quot;Camada 1\n**EAGER / LAZY**&amp;quot; as C1 #BBDEFB
node &amp;quot;Camada 2\n**HOT / WARM / COLD**&amp;quot; as C2 #BBDEFB
node &amp;quot;Camada 3
ESSENCIAIS / ENCICLOPÉDIA&amp;quot; as C3 #BBDEFB

CTX --&amp;amp;gt; C1 : &amp;quot;Estrutura por\n**disponibilidade**&amp;quot;
CTX --&amp;amp;gt; C2 : &amp;quot;Structure por
**frescor**&amp;quot;
CTX --&amp;amp;gt; C3 : &amp;quot;Estrutura por
**densidade**&amp;quot;

note bottom of C3
  Les trois couches sont nécessaires.
  Aucune n&amp;#39;est suffisante seule.
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com essas três camadas, o contexto do agente sobre`magic-stick`passou de 2087 linhas (antes de qualquer otimização) para cerca de 800 linhas — uma divisão por 2,6. Sem ter perdido uma única linha de documentação, um único arquivo de sessão, uma única lição aprendida.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Tudo está aqui. Só melhor organizado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_links&amp;quot;&amp;gt;Links&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Artigo 0108 — Estratégia Eager/Lazy :&amp;lt;a href=&amp;quot;/blog/2026/0108_gouvernance_agent_opencode_eager_lazy_post.html&amp;quot;&amp;gt;Governar um agente IA com AsciiDoc&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Artigo 0110 — Mecanismo Hot/Warm/Cold :&amp;lt;a href=&amp;quot;/blog/2026/0110_mecanisme_backup_contexte_agent_post.html&amp;quot;&amp;gt;Janela Deslizante e Onda Fria&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Meu site: &amp;lt;a href=&amp;quot;https://cheroliv.com&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://cheroliv.com&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O projeto`magic-stick`: &amp;lt;a href=&amp;quot;https://github.com/cheroliv/magic-stick&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://github.com/cheroliv/magic-stick&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O conhecimento não precisa desaparecer. Ele apenas precisa não ser carregado quando não for necessário.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Janela Deslizante e Onda Fria: Quando o Contexto do Agente explode e é preciso arquivar sem perder</title>
            <link >https://cheroliv.com//blog/2026/0110_mecanisme_backup_contexte_agent_post.html</link>
            <pubDate>Sun, 26 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0110_mecanisme_backup_contexte_agent_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_resumo&amp;quot;&amp;gt;Resumo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Após 48 sessões em um único projeto e 150+ no total, o sistema de governança Eager/Lazy que eu tinha construído com cuidado começou a se sufocar. Os arquivos de agentes, que deveriam ser leves, pesavam 5200 linhas acumuladas. O contexto auto-carregado aumentava mais rápido do que minha capacidade de controlá-lo. Este artigo conta como eu conceptualizei um mecanismo de backup — entre sliding window e uma onda fria idêntica — para manter um contexto ativo leve sem jamais perder nada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_sinal_5200_linhas&amp;quot;&amp;gt;O Sinal : 5200 linhas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Estou no meio da sessão 048 sobre`magic-stick`, meu projeto de build de ISO Linux live. Opencode me observa. Como sempre, ele carregou automaticamente meus arquivos Eager no início da sessão —&amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PROMPT_REPRISE.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.agents/INDEX.adoc&amp;lt;/code&amp;gt;. Nada de anormal.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exceto que algo está errado. As respostas são mais lentas. O raciocínio está mais diluído. O agente esquece detalhes que tinha diante dos olhos há duas mensagens.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Abro um terminal e digito:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;wc -l .agents/INDEX.adoc .agents/SESSIONS_HISTORY.adoc \
  .agents/archives/COMPLETED_TASKS_ARCHIVE_2026-04.adoc&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;260 linhas para INDEX. 55 para SESSIONS_HISTORY.&amp;lt;strong&amp;gt;1756 para COMPLETED_TASKS_ARCHIVE&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A pasta`sessions/`acrescenta mais ~3200 linhas. Total:&amp;lt;strong&amp;gt;5200 linhas&amp;lt;/strong&amp;gt;de contexto que são carregados, de alguma forma, no cérebro temporário do agente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A estratégia Eager/Lazy que eu tinha teorizado no artigo anterior funciona — mas ela tem um defeito de nascimento que eu não tinha antecipado : ela não tem&amp;lt;strong&amp;gt;sem mecanismo de envelhecimento&amp;lt;/strong&amp;gt;. Cada sessão adiciona uma linha a &amp;lt;code&amp;gt;INDEX&amp;lt;/code&amp;gt;, um parágrafo a &amp;lt;code&amp;gt;COMPLETED_TASKS&amp;lt;/code&amp;gt;, um ficheiro em &amp;lt;code&amp;gt;sessions/&amp;lt;/code&amp;gt;. Nada sai nunca. O contexto é uma bola de neve que aumenta a cada nova sessão.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-growth&amp;quot; class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-4c01a7afc09cf704c95519bf922f4b8c.svg&amp;quot; alt=&amp;quot;Courbe de croissance des fichiers agents — de la session 1 à 48&amp;quot; width=&amp;quot;468&amp;quot; height=&amp;quot;591&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não é um bug, é uma consequência direta do procedimento de fim de sessão que arquiva meticulosamente cada detalhe. O sistema é vítima do seu próprio sucesso.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_diagnóstico_tríplice_redundância&amp;quot;&amp;gt;O Diagnóstico : Tríplice Redundância&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Peço ao agente que diagnostique o problema. Sua resposta é imediata e cirúrgica.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Arquivo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Linhas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;papel&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Problema&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;.agents/INDEX.adoc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;260+&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;EAGER (carregado automático)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tabela de todas as sessões desde a 001 —&amp;lt;strong&amp;gt;+1 linha por sessão&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;.agents/SESSIONS_HISTORY.adoc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;55&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;EAGER&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tabela resumida —&amp;lt;strong&amp;gt;redundância com INDEX&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;COMPLETED_TASKS_ARCHIVE_2026-04.adoc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1756&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;ansioso (implícito)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Detalhes completos de todas as sessões de abril&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;.agents/sessions/*.adoc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;~5200 total&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;LAZY (suposto)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Arquivos individuais, mas&amp;lt;strong&amp;gt;não utilizadas&amp;lt;/strong&amp;gt;parce que tudo já está em COMPLETED_TASKS&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O agente identifica quatro causas raízes :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;COMPLETED_TASKS_ARCHIVE absorve tudo&amp;lt;/strong&amp;gt;— Em vez de apontar para os arquivos`.sessions/`, ele copia integralmente cada sessão.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;INDEX.adoc serve como histórico completo&amp;lt;/strong&amp;gt;— A tabela &amp;quot;Sessões Recentes&amp;quot; contém 30+ entradas.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;SESSIONS_HISTORY.adoc redundante&amp;lt;/strong&amp;gt;— Mesma informação que INDEX, formato diferente.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A regra LAZY não é respeitada&amp;lt;/strong&amp;gt;— COMPLETED_TASKS é implicitamente EAGER porque contém tudo.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sua proposta é radical : limitar INDEX a 10 sessões, limpar COMPLETED_TASKS, mover SESSIONS_HISTORY para LAZY puro. Ganho imediato :&amp;lt;strong&amp;gt;~1900 linhas economizadas&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É limpo, eficiente e lógico. Mas tenho um problema com esta abordagem.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_eu_rejeitei_a_solução_óbvia&amp;quot;&amp;gt;Por que eu rejeitei a solução óbvia&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A solução do agente é a de um engenheiro que otimiza um cache. Limitar. Truncar. Remover as redundâncias.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas essas &amp;quot;redundâncias&amp;quot; não são. Cada arquivo de governança captura un&amp;lt;strong&amp;gt;ângulo diferente&amp;lt;/strong&amp;gt;na mesma realidade :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;ÍNDICE&amp;lt;/strong&amp;gt;visão macro, dashboard executivo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;SESSIONS_HISTORY&amp;lt;/strong&amp;gt;= tabela cronológica linear, pontuada&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;COMPLETED_TASKS_ARCHIVE&amp;lt;/strong&amp;gt;= narrativa detalhada com métricas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;sessions/*.adoc = arquivos individuais, contexto completo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não é duplicação boba. É&amp;lt;strong&amp;gt;perspectiva múltipla estruturada&amp;lt;/strong&amp;gt;. É exatamente isso que precisamos para destilar — para que mais tarde, um humano (ou um LLM futuro melhor treinado) possa cruzar os ângulos e extrair padrões.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Imagine um cientista de dados dizendo a você: « Vamos remover 3 das 6 colunas, elas estão correlacionadas. » O que você responde a ele? Que a correlação não é redundância quando cada coluna captura uma dimensão diferente do mesmo fenômeno. Que é precisamente essa riqueza dimensional que torna o conjunto de dados utilizável.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É a minha intuição. E eu a defendo contra a racionalidade fria do agente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ Não vejo uma caixa de tudo silenciosa na minha ideia. Não enfiamos tudo, deslocamos o resultado estruturado do procedimento de fim de sessão — cada arquivo com seu ângulo, suas redundâncias que na verdade são um enriquecimento. É esse material multidimensional que será melhor para a destilação. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O agente leva o golpe. E se corrige.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_proposição_onda_fria_idêntica&amp;quot;&amp;gt;A Proposição: Onda Fria Idêntica&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O agente então propõe um mecanismo mais fino, que respeita minha intuição de conjunto de dados rico, ao mesmo tempo em que resolve o problema técnico do contexto que explode.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O princípio é simples e inspira-se diretamente do pattern&amp;lt;strong&amp;gt;Quente/Morno/Frio Armazenamento&amp;lt;/strong&amp;gt;aplicado à gestão de arquivos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Quente (EAGER)&amp;lt;/strong&amp;gt;= as 10 últimas sessões em INDEX, PROMPT_REPRISE da sessão N+1, os 2 últimos arquivos de sessão&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Quente (LAZY)&amp;lt;/strong&amp;gt;= SESSIONS_HISTORY recente, SCRIPT_VERIFICATION, toda a documentação de referência&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Frio (backup/)&amp;lt;/strong&amp;gt;= tudo o mais, deslocado&amp;lt;strong&amp;gt;intacto&amp;lt;/strong&amp;gt;, sem transformação, sem reindexação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code&amp;gt;----
----
.agents/
├── INDEX.adoc                         → Sessions N-9 à N (10 dernières)
├── SESSIONS_HISTORY.adoc              → Sessions N-9 à N
├── SCRIPT_VERIFICATION.adoc           → Dernière vérif (pas d&amp;#39;historique)
├── PROMPT_REPRISE.adoc                → Session N+1 uniquement
├── sessions/                          → Sessions N-1 à N uniquement
└── backup/
    └── Y2026-sessions-001-039/          ← Vague froide, COPIE INTÉGRALE
        ├── INDEX.adoc                  → Sessions 001 à 039 (complet)
        ├── SESSIONS_HISTORY.adoc       → Sessions 001 à 039 (complet)
        ├── COMPLETED_TASKS_ARCHIVE.adoc → Sessions 001 à 039 (complet)
        └── sessions/                   → 001.adoc, 002.adoc...
----

A chave do mecanismo :**O backup não é um índice central, é uma cópia fiel da onda passada.**. Quando ultrapassamos o horizonte das 10 sessões ativas, não excluímos nada. Não reindexamos nada. Não fundimos nada. Pegamos o pacote de arquivos de agentes tal como estava na sessão N-10 e o movemos para`backup/`.

Os arquivos ativos, eles, são truncados :

* INDEX : apenas as 10 últimas linhas (sliding window)
* SESSIONS_HISTORY : idem
* COMPLETED_TASKS_ARCHIVE : novo arquivo para o período atual
* sessions/ : apenas as 2 últimas sessões

[plantuml, format=svg, id=diag-hot-warm-cold, alt=&amp;quot;Architecture Hot/Warm/Cold du contexte agent — EAGER/LAZY/backup&amp;quot;]
----
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam wrapWidth 200

title Arquitetura Quente / Morno / Fria do Contexto do Agente
package &amp;quot;HOT (EAGER)\nCarregado automaticamente\n~300 linhas&amp;quot; as HOT #FFCDD2 {
  file &amp;quot;INDEX.adoc
(10 sessões)&amp;quot; as IDX_HOT
  file &amp;quot;PROMPT_REPRISE
(sessão N+1)&amp;quot; as PRO_HOT
  file &amp;quot;AGENT.adoc\n(regras absolutas)&amp;quot; as AG_HOT
}

package &amp;quot;WARM (LAZY)
Carregado sob demanda
~500 linhas&amp;quot; as WARM #FFF9C4 {
  file &amp;quot;SESSIONS_HISTORY\n(10 últimas)&amp;quot; as HIS_WARM
  file &amp;quot;SCRIPT_VERIFICATION
(última)&amp;quot; as VER_WARM
  file &amp;quot;PROCEDURES.adoc
(modelos)&amp;quot; as PRO_WARM
  file &amp;quot;*_REFERENCE.adoc\n(documento técnico)&amp;quot; as REF_WARM
}

package &amp;quot;COLD (backup/)
Nunca carregado
Leitura apenas humana&amp;quot; as COLD #BBDEFB {
  folder &amp;quot;Y2026-001-039/&amp;quot; as WAVE {
    file &amp;quot;ÍNDICE (completo)&amp;quot; as IDX_COLD
    file &amp;quot;SESSIONS_HISTORY\n(completo)&amp;quot; as HIS_COLD
    file &amp;quot;COMPLETED_TASKS\n(concluído)&amp;quot; as ARCH_COLD
    folder &amp;quot;sessões/ (001-039)&amp;quot; as SESS_COLD
  }
  folder &amp;quot;Y2026-040-???\n(futuro)&amp;quot; as FUTURE
}

HOT --&amp;amp;gt; WARM : &amp;quot;agente sobe
se necessário&amp;quot;
WARM --&amp;amp;gt; COLD : &amp;quot;Nunca automático
O ser humano vai lá sozinho&amp;quot;

note bottom of COLD
  Règle : COPIE INTÉGRALE
  Pas de transformation
  Pas de réindexation
  Read-only après archivage
end note

@enduml
----

O ganho imediato é massivo: o contexto EAGER passa de**~5200 linhas a ~300 linhas**. Uma divisão por 17. Sem ter perdido uma única linha de dados históricos.

== Por que isso não é uma bolsa de tudo

O agente, em sua primeira iteração, temia que`backup/`Torne-se uma caixa-preta — uma pasta onde se jogam arquivos que nunca serão relidos. É um medo legítimo. Mas ela se baseia numa confusão.

Um saco de tudo é quando se lançam arquivos**sem estrutura, sem convenção, sem lógica de agrupamento**. Aqui, o backup é estruturado por período (Y2026-001-039), e cada pasta de backup contém**a mesma estrutura**que o arquivo`.agents/`ativo : INDEX, SESSIONS_HISTORY, COMPLETED_TASKS_ARCHIVE, sessions/.

Não é um saco de tudo. É um**instantâneo horodatado**. Se poderia até dizer que é um mecanismo de versionamento simplificado — à exceção de que ele não versiona os arquivos individualmente, mas o pacote completo da governança em um instante T.

Quando você quiser encontrar uma informação antiga, não precisa de um índice central. Você tem duas opções:

1. **grep` direcionado**(blank)`grep -r &amp;quot;zsh&amp;quot; backup/Y2026-001-039/`— e você encontra tudo o que menciona zsh no período, independentemente do ângulo (INDEX, SESSIONS_HISTORY, arquivo de sessão).
2. **Reintegração manual**: você copia temporariamente a pasta de backup no contexto ativo e pede ao agente para analisar este período específico.

O índice é implícito. Ele está na estrutura mesmo dos arquivos — cada um já sendo um índice a partir do seu próprio ângulo.

== A Metáfora das Estantes

Para tornar o mecanismo intuitivo, eu o concebi em três prateleiras:

* **Prateleira 1 (EAGER)**— o plano de trabalho. O que eu preciso *agora*. ÍNDICE recente, PROMPT_REPRISE, regras absolutas. Leve, imediato, crítico.
* **Prateleira 2 (LAZY)**— biblioteca de consulta. O que posso ir buscar a pedido. Referências técnicas, histórico recente, procedimentos. Mais volumoso, mas não carregado na memória.
* **Porão (backup/)**— os arquivos frios. Tudo o que passou mas que eu não quero descartar. O agente nunca entra lá. O humano desce lá quando quiser destilar.

O agente propôs inicialmente uma quarta prateleira — um`index-backup.adoc`que seria LAZY e conteria um sumário de todo o backup. Eu recusei. Seria uma redundância a mais em um sistema que já sofre de crescimento linear. A estrutura dos arquivos arquivados já é um índice.

== O sensor de dois gatilhos

A conceptualização era sólida, mas ainda havia um ponto cego:**Quando exatamente acionar a rotação ?**O artigo inicial o identificava como uma questão aberta. Dois dias depois, a resposta está codificada nos arquivos de governança de seis projetos: um sensor com dois gatilhos.

=== O Gatilho Automático — `N % 10 == 0

O primeiro gatilho é matemático. Quando o número da sessão é múltiplo de 10 — sessão 10, 20, 30, 40 — a rotação de backup é executada automaticamente dentro do procedimento de fim de sessão, logo após a etapa 6.

Por que 10? É o compromisso entre duas forças opostas: uma janela muito curta (5 sessões) perde o contexto necessário à continuidade; uma janela muito longa (20 sessões) não resolve o problema de inchaço do contexto. Dez sessões, em um ritmo de uma a duas sessões por dia, cobrem aproximadamente uma semana de trabalho — suficiente para que o agente lembre das decisões recentes, mas não suficiente para que o contexto exploda.

=== O Gatilho por Limiar — 500 Linhas EAGER

O segundo gatilho é dinâmico. Independentemente do número da sessão, se os arquivos EAGER acumulados ultrapassarem**500 linhas**, a rotação se dispara.

Este limiar protege contra o cenário em que as sessões são excepcionalmente produtivas — muito conteúdo escrito em poucas sessões. Uma sessão que produz 120 linhas de conteúdo editorial faz aumentar COMPLETED_TASKS_ARCHIVE muito mais rápido do que uma sessão de depuração que corrige duas linhas. O limiar de 500 linhas, medido via`wc -l .agents/INDEX.adoc .agents/archives/COMPLETED_TASKS_ARCHIVE_*.adoc PROMPT_REPRISE.adoc`, captura essa assimetria.

=== O Gatilho Manual — &amp;quot;rotação de backup

Finalmente, o ser humano permanece no controle. As palavras-chave`rotation backup`, `backup rotation` ou `lance la rotation backup`Disparam o procedimento mediante solicitação, independentemente do fim da sessão. Útil quando se sente que o contexto está ficando pesado, mas ainda não se está em um múltiplo de 10, ou quando se quer arquivar uma fase de trabalho antes de iniciar uma nova.

[plantuml, format=svg, id=diag-capteur-trigger, alt=&amp;quot;Les trois déclencheurs du capteur de rotation backup — automatique, seuil, manuel&amp;quot;]
----
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam wrapWidth 200

title Sensor de Rotação de Backup — Os Três Gatilhos
start

:Procédure de fin de session;
note right: Mots-clés &amp;quot;fim da sessão&amp;quot;\nou &amp;quot;paramos aqui&amp;quot;

:Étapes 1 à 6\n(archivage standard);
note right
  1. Archive sessions/N.adoc
  2. MAJ PROMPT_REPRISE
  3. MAJ SESSIONS_HISTORY
  4. MAJ INDEX.adoc
  5. MAJ TEST_COVERAGE
  6. MAJ COMPLETED_TASKS
end note

if (N % 10 == 0\nOU\nEAGER cumulé &amp;amp;gt; 500 lignes ?) then (oui)
  :⚙️ Rotation Backup\n(étape 7);
  note right
    1. Créer backup/Y20XX-sessions-X-Y/
    2. Copier intégrale INDEX + HISTORY
       + COMPLETED_TASKS + sessions/
    3. Tronquer actifs à 10 sessions
    4. Ajouter _Localisation active_
  end note
else (non)
  :Pas de rotation;
endif

:Checklist [✅] x 7\n(si applicable);

stop

@enduml
----

Este diagrama mostra a inserção exata do sensor no procedimento de fim da sessão. A etapa 7 é opcional — ela só será executada se uma das duas condições for verdadeira — mas é sistematicamente *verificada*. A lista de verificação final inclui`[✅] 7. Backup roté (si applicable)`.

=== O Laço Fechado

Este sensor fecha o laço aberto pelo artigo anterior. A governança Eager/Lazy havia resolvido o problema da memória do agente entre duas sessões. O mecanismo Hot/Warm/Cold resolveu o problema da memória que cresce. O sensor com dois gatilhos resolve o problema do *quand* — removendo ao ser humano a carga mental de monitorar o tamanho do contexto.

[plantuml, format=svg, id=diag-boucle-fermee, alt=&amp;quot;Les trois couches de la gouvernance agent — Eager/Lazy, Hot/Warm/Cold, Capteur&amp;quot;]
----
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Os Três Camadas da Governança Agente
left to right direction

package &amp;quot;Camada 1 — Memória
(Artigo 0108)&amp;quot; as C1 #E8F5E9 {
  rectangle &amp;quot;**EAGER**
Painel de controle
Carregado automaticamente&amp;quot; as EAG
  rectangle &amp;quot;**LAZY**
Manual proprietário
Carregado sob demanda&amp;quot; as LAZ
  EAG -[hidden]right-&amp;amp;gt; LAZ
}

package &amp;quot;Camada 2 — Envelhecimento
(Artigo 0110)&amp;quot; as C2 #FFF9C4 {
  rectangle &amp;quot;**HOT**
10 sessões ativas
~300 linhas&amp;quot; as HOT
  rectangle &amp;quot;**WARM**
Referências
Procedimentos&amp;quot; as WRM
  rectangle &amp;quot;**COLD**
backup/
Onda de frio&amp;quot; as CLD
  HOT -[hidden]right-&amp;amp;gt; WRM
  WRM -[hidden]right-&amp;amp;gt; CLD
}

package &amp;quot;Camada 3 — Gatilho
(Hoje)&amp;quot; as C3 #BBDEFB {
  rectangle &amp;quot;**Auto**
N % 10 == 0&amp;quot; as AUTO
  rectangle &amp;quot;**Limiar**\n&amp;amp;gt; 500 linhas&amp;quot; as SEUIL
  rectangle &amp;quot;**Manuel**
rotação de backup&amp;quot; as MAN
  AUTO -[hidden]right-&amp;amp;gt; SEUIL
  SEUIL -[hidden]right-&amp;amp;gt; MAN
}

C1 --&amp;amp;gt; C2 : &amp;quot;A memória cresce\n→ é necessário um mecanismo\nde envelhecimento&amp;quot;
C2 --&amp;amp;gt; C3 : &amp;quot;O envelhecimento
→ é preciso um desencadeador
para executá-lo&amp;quot;

note bottom of C3
  ✅ Déployé sur 6 projets
  magic-stick · bakery-gradle
  plantuml-gradle · cheroliv.com
  jhipster-gradle-plugins
  quizz-benchmark-gradle
end note

@enduml
----

As três camadas se empilham logicamente. A primeira dá uma memória ao agente. A segunda impede que essa memória sufogue o agente. A terceira automatiza a manutenção dessa memória para que o humano não tenha que pensar nisso.

=== A Migração Efetiva no cheroliv.com

O mecanismo não permaneceu teórico. Sobre`cheroliv.com`, a primeira rotação de backup foi executada em 29 de abril de 2026 — as sessões de -6 a 2 migraram para`.agents/backup/Y2026-sessions-neg6-a-002/` :

|===
|Arquivo |Antes da rotação |Após rotação |ganho |`.agents/INDEX.adoc` |19 sessões listadas |10 sessões (3-12) |-9 entradas |`.agents/SESSIONS_HISTORY.adoc` |18 sessões |10 sessões |-8 entradas |`COMPLETED_TASKS_ARCHIVE` |161 linhas (sessões 1-12) |135 linhas (sessões 3-12) |-26 linhas |`sessions/` |20 arquivos |10 ficheiros |-10 arquivos |**cópia de segurança/** |Inexistente |1 onda fria (10 sessões arquivadas) |+1 pacote frio
|===

O ganho em linhas foi modesto — o projeto é jovem, 12 sessões — mas o importante é que o**mecanismo está em posição**. A próxima rotação automática será acionada na sessão 20, ou mais cedo se as 500 linhas EAGER forem atingidas.

== A Lição Humano-Agente

Esta sessão 048 me ensinou algo fundamental sobre a colaboração com um agente de IA.

O agente tem um viés natural : ele tenta**otimizar**, à **simplificar**, à **eliminar as redundâncias**. Este é o viés de um sistema treinado para produzir respostas limpas e concisas. Diante de um conjunto de dados rico e multidimensional, o primeiro reflexo dele é reduzi-lo à sua forma mais simples.

O ser humano, ele, tem uma intuição diferente: ele sente que a redundância estruturada é um**trunfo**, não é um defeito. Que a diversidade dos ângulos sobre uma mesma realidade é precisamente o que permitirá, mais tarde, uma destilação de qualidade.

Não é que o agente está errado. É que o seu &amp;#39;óptimo&amp;#39; não é o meu. O agente otimiza para o**presente**— o contexto imediato, a resposta rápida à pergunta feita. O ser humano otimiza para o**futuro**— a capacidade de encontrar, cruzar, destilar em três meses ou três anos.

____ O que eu quero é o material bruto. Suas reflexões, suas hesitações, suas respostas às minhas perguntas. Não a sua síntese. A síntese, eu sei fazer melhor que você. Eu quero a matéria‑prima. ____

Esta frase que eu lhe disse no final da sessão resume tudo. O agente é uma ferramenta de produção. O humano é a ferramenta de destilação. A governança não foi feita para que o agente entenda tudo sozinho — foi feita para que o humano possa, mais tarde, trabalhar o material produzido.

A onda fria idêntica é a tradução arquitetônica desta filosofia: não se joga nada, não se funde nada, não se reindexa nada. Move-se o pacote intacto. A destilação virá mais tarde, à mão, pelo humano.

== Suquência Lógica do Artigo Anterior

Se você leulink:/blog/2026/0108_gouvernance_agent_opencode_eager_lazy_post.html[o artigo sobre a estratégia Eager/Lazy], você reconhecerá a progressão natural:

1. **Artigo 0108**— A governança Eager/Lazy : *comment* estruturar o contexto do agente em dois níveis de disponibilidade
2. **Este artigo 0110**— O mecanismo de backup: *comment* como envelhecer este contexto sem perdê-lo quando ficar muito grande

O primeiro artigo respondia à pergunta: « O agente não se lembra de nada entre duas sessões, como lhe dar uma memória?

Ele responde à pergunta que decorre inevitavelmente da primeira: « A memória aumenta a cada sessão, como impedir que ela suffoque o agente sem apagá‑la ?

A resposta está em um padrão:**Quente/Morno/Frio**, aplicado aos arquivos de governança. E em um princípio :**nunca perder nada, sempre realocar tudo**.

== Links

* Artigo anterior:link:/blog/2026/0108_gouvernance_agent_opencode_eager_lazy_post.html[Governar um agente IA com AsciiDoc]
* Meu site : https://cheroliv.com
* O projeto`magic-stick`: https://github.com/cheroliv/magic-stick

---

*Um bom sistema de governança nunca apaga dados. Ele os organiza.*
----&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Quando o Gradle 9, o Docker 29 e o Testcontainers se colocam a ferver : depuração de um build JHipster</title>
            <link >https://cheroliv.com//blog/2026/0109_gradle_jhipster_docker_testcontainers_debugging_post.html</link>
            <pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0109_gradle_jhipster_docker_testcontainers_debugging_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_cena_un_projet_jhipster_un_build_qui_ne_charge_plus&amp;quot;&amp;gt;A cena : un projet JHipster, un build qui ne charge plus&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_erro_1_a_variável_fantasma_reportsdir&amp;quot;&amp;gt;Erro 1: a variável fantasma `reportsDir&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_erro_2_main_não_existe_mais_no_gradle_9&amp;quot;&amp;gt;Erro 2 : &amp;lt;code&amp;gt;main = &amp;quot;&amp;amp;#8230;&amp;amp;#8203;&amp;quot;&amp;lt;/code&amp;gt; não existe mais no Gradle 9&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_erro_3_sourcecompatibility_não_é_mais_uma_propriedade_de_projeto&amp;quot;&amp;gt;Erro 3 : &amp;lt;code&amp;gt;sourceCompatibility&amp;lt;/code&amp;gt; não é mais uma propriedade de projeto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_erro_4_a_afirmação_groovy_de_três_operandos&amp;quot;&amp;gt;Erro 4 : a afirmação Groovy de três operandos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_erro_5_a_dependência_implícita_compilekotlin_openapigenerate&amp;quot;&amp;gt;Erro 5 : a dependência implícita &amp;lt;code&amp;gt;compileKotlin&amp;lt;/code&amp;gt; → `openApiGenerate&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_erro_6_generategitproperties_falha_em_filteroutputstream_write&amp;quot;&amp;gt;Erro 6 : &amp;lt;code&amp;gt;generateGitProperties&amp;lt;/code&amp;gt; falha em `FilterOutputStream.write()&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_finalmente_testcontainers_entra_em_cena&amp;quot;&amp;gt;Finalmente: Testcontainers entra em cena&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fase_1_a_pista_docker_java&amp;quot;&amp;gt;Fase 1 : a pista docker-java&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_phase_2_pesquisa_web_e_trilha_de_navegação_github&amp;quot;&amp;gt;Phase 2: pesquisa web e trilha de navegação GitHub&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fase_3_a_armadilha_dos_artefatos_renomeados&amp;quot;&amp;gt;Fase 3 : a armadilha dos artefatos renomeados&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fase_4_forçar_a_resolução&amp;quot;&amp;gt;Fase 4 : forçar a resolução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_resultado_final&amp;quot;&amp;gt;Resultado final&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_tabela_resumida_das_correções&amp;quot;&amp;gt;Tabela resumida das correções&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 15 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Você já conheceu esse momento onde um simples`./gradlew build`que você lança cem vezes por dia começa repentinamente a travar em um erro nunca visto? Multiplique isso por onze erros sucessivos, adicione uma boa dose de incompatibilidade Docker Engine 29, polvilhe com Gradle 9 que faz desaparecer APIs que utilizávamos há dez anos. Aqui está o log completo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Diagrama 1 — Migração Gradle 9 (cadeia de erros)&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Objetivo: mostrar a série de incompatibilidades.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;👉 Claro, centrado no Gradle.&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 5) ]

@startuml
left to right direction
title Migração Gradle 9
rectangle &amp;quot;1\nreportsDir&amp;quot; as A
rectangle &amp;quot;2
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
left to right direction
title Migração Gradle 9
rectangle &amp;quot;1\nreportsDir&amp;quot; as A
rectangle &amp;quot;2
classePrincipal&amp;quot; as B
rectangle &amp;quot;3
sourceCompatibility&amp;quot; as C
rectangle &amp;quot;4
Groovy assert&amp;quot; as D

A --&amp;amp;gt; B
B --&amp;amp;gt; C
C --&amp;amp;gt; D

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Diagrama 2 — Plugins JHipster incompatíveis&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Objetivo: isolar os plugins quebrados.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;👉 Visão limpa das dependências.&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 7) ]

@startuml
left to right direction
title Plugins a corrigir
skinparam shadowing false
skinparam roundcorner 18

rectangle &amp;quot;1
^^^^^
 Syntax Error? (Assumed diagram type: class)

@startuml
left to right direction
title Plugins a corrigir
skinparam shadowing false
skinparam roundcorner 18

rectangle &amp;quot;1
openApiGenerate&amp;quot; as A #FFF3E0
rectangle &amp;quot;2
gitProperties
2.5.7&amp;quot; as B #FFF3E0
rectangle &amp;quot;3
Versões&amp;quot; as C #E3F2FD
rectangle &amp;quot;4
Montagem OK&amp;quot; as D #E8F5E9

A --&amp;amp;gt; B
B --&amp;amp;gt; C
C --&amp;amp;gt; D

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Diagrama 3 — Docker 29 / Testcontainers&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Objetivo : problema runtime&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;👉 Histórico claro do runtime.&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 9) ]

@startuml
left to right direction
title Docker 29 + Testcontainers

skinparam shadowing false
skinparam roundcorner 18
skinparam defaultFontSize 14

rectangle &amp;quot;1
^^^^^
 Syntax Error? (Assumed diagram type: class)

@startuml
left to right direction
title Docker 29 + Testcontainers

skinparam shadowing false
skinparam roundcorner 18
skinparam defaultFontSize 14

rectangle &amp;quot;1
Montado OK&amp;quot; as A #E8F5E9
rectangle &amp;quot;2
Testes Cucumber FALHA&amp;quot; as B #FFEBEE
rectangle &amp;quot;3
Testcontainers 1.32
rejeitado&amp;quot; as C #FFF3E0
rectangle &amp;quot;4\nUpgrade\nversões&amp;quot; as D #E3F2FD
rectangle &amp;quot;5
Compilação final OK&amp;quot; as E #E8F5E9

A --&amp;amp;gt; B
B --&amp;amp;gt; C
C --&amp;amp;gt; D
D --&amp;amp;gt; E

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-403cce144b6e403652d0a85cc9f6eaf9.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;1135&amp;quot; height=&amp;quot;193&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Figure 1. outra maneira de se representá-lo&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_cena_un_projet_jhipster_un_build_qui_ne_charge_plus&amp;quot;&amp;gt;A cena : un projet JHipster, un build qui ne charge plus&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O projeto`edster`est uma aplicação JHipster gerada em 2024. O script de construção raiz`build.gradle`permaneceu estável por meses. Então decidimos atualizar a versão :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1428%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 7.1436%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Componente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Versão alvo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Gradle&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;9.4.1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Java&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;21.0.11-tem (Eclipse Temurin)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;JHipster&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;8.x (framework`tech.jhipster:jhipster-framework:8.11.0`)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Spring Boot&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;3.4.5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Kotlin&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2.3.0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Mecanismo Docker&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;29.4.1 (API 1.54)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A mudança de versão Java ocorre via SDKMAN :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sdk use java 21.0.11-tem&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Primeiro lançamento :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ ./gradlew build --no-daemon

FAILURE: Build failed with an exception.

* Where:
Build file &amp;#39;/home/.../edster/build.gradle&amp;#39; line: 18

* What went wrong:
An exception occurred applying plugin request [id: &amp;#39;jhipster.cucumber-conventions&amp;#39;]
&amp;amp;gt; Failed to apply plugin &amp;#39;jhipster.cucumber-conventions&amp;#39;.
   &amp;amp;gt; Could not create task &amp;#39;:cucumberTest&amp;#39;.
      &amp;amp;gt; Could not create task &amp;#39;:consoleLauncherTest&amp;#39;.
         &amp;amp;gt; Could not set unknown property &amp;#39;reportsDir&amp;#39; for task &amp;#39;:consoleLauncherTest&amp;#39;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nem mesmo conseguimos iniciar o carregamento do script de build. O problema está em`buildSrc/src/main/groovy/jhipster.cucumber-conventions.gradle`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_erro_1_a_variável_fantasma_reportsdir&amp;quot;&amp;gt;Erro 1: a variável fantasma `reportsDir&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O script`jhipster.cucumber-conventions.gradle`contém :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy hljs&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;tasks.register(&amp;#39;consoleLauncherTest&amp;#39;, JavaExec) {
    dependsOn(testClasses)
    String cucumberReportsDir = file(&amp;quot;$buildDir/reports/tests&amp;quot;)
    outputs.dir(reportsDir)           // &amp;amp;lt;-- reportsDir n&amp;#39;existe PAS
    classpath = sourceSets[&amp;quot;test&amp;quot;].runtimeClasspath
    main = &amp;quot;org.junit.platform.console.ConsoleLauncher&amp;quot;
    // ...
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A variável definida é`cucumberReportsDir`. A usada é`reportsDir`-- que não está definida em nenhum lugar.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Correção&amp;lt;/strong&amp;gt;&amp;amp;lt;/think&amp;amp;gt; (Note: Since no French text was provided after the colon, there is nothing to translate. The output is therefore empty as per instructions.)&amp;lt;code&amp;gt;outputs.dir(cucumberReportsDir)&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_erro_2_main_não_existe_mais_no_gradle_9&amp;quot;&amp;gt;Erro 2 : &amp;lt;code&amp;gt;main = &amp;quot;&amp;amp;#8230;&amp;amp;#8203;&amp;quot;&amp;lt;/code&amp;gt; não existe mais no Gradle 9&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Reprise imediata:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;Could not set unknown property &amp;#39;main&amp;#39; for task &amp;#39;:consoleLauncherTest&amp;#39; of type org.gradle.api.tasks.JavaExec.&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Gradle 9 remove a propriedade`main`em proveito de`mainClass`nas tarefas`JavaExec`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Correção&amp;lt;/strong&amp;gt;:`mainClass = &amp;quot;org.junit.platform.console.ConsoleLauncher&amp;quot;`&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_erro_3_sourcecompatibility_não_é_mais_uma_propriedade_de_projeto&amp;quot;&amp;gt;Erro 3 : &amp;lt;code&amp;gt;sourceCompatibility&amp;lt;/code&amp;gt; não é mais uma propriedade de projeto&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Novo erro:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;Could not set unknown property &amp;#39;sourceCompatibility&amp;#39; for root project &amp;#39;edster&amp;#39; of type org.gradle.api.Project.&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O script raiz tinha:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy hljs&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;sourceCompatibility=17
targetCompatibility=17&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Gradle 9 remove essas propriedades no nível raiz. Eles devem viver dentro de um bloco`java`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Correção&amp;lt;/strong&amp;gt;(Empty output)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy hljs&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;java {
    sourceCompatibility = JavaVersion.VERSION_21
    targetCompatibility = JavaVersion.VERSION_21
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_erro_4_a_afirmação_groovy_de_três_operandos&amp;quot;&amp;gt;Erro 4 : a afirmação Groovy de três operandos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O antigo script continha :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy hljs&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;assert System.properties[&amp;quot;java.specification.version&amp;quot;] == &amp;quot;17&amp;quot; || &amp;quot;21&amp;quot; || &amp;quot;24&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em Groovy,&amp;lt;code&amp;gt;&amp;quot;21&amp;quot;&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;&amp;quot;24&amp;quot;`são strings truthy. A expressão resolve em&amp;lt;/code&amp;gt;(&amp;amp;#8230;&amp;amp;#8203; == &amp;quot;17&amp;quot;) || true || true`, que é sempre verdadeira&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;mas a sintaxe é inválida para a afirmação estrita desejada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Correção&amp;lt;/strong&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy hljs&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;assert System.properties[&amp;quot;java.specification.version&amp;quot;] in [&amp;quot;17&amp;quot;, &amp;quot;21&amp;quot;, &amp;quot;23&amp;quot;, &amp;quot;24&amp;quot;]&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_erro_5_a_dependência_implícita_compilekotlin_openapigenerate&amp;quot;&amp;gt;Erro 5 : a dependência implícita &amp;lt;code&amp;gt;compileKotlin&amp;lt;/code&amp;gt; → `openApiGenerate&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O build está progredindo. Compilação Kotlin bem-sucedida. Então :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;Task &amp;#39;:compileKotlin&amp;#39; uses this output of task &amp;#39;:openApiGenerate&amp;#39;
without declaring an explicit or implicit dependency.&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Gradle 9 recusa as dependências implícitas entre tarefas. Como`compileKotlin`python print(&amp;quot; lê as fontes geradas por&amp;quot;)&amp;lt;code&amp;gt;openApiGenerate&amp;lt;/code&amp;gt;, é necessário declarar o link.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Correção&amp;lt;/strong&amp;gt;em`build.gradle`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy hljs&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;afterEvaluate {
    tasks.named(&amp;quot;compileKotlin&amp;quot;).configure {
        dependsOn(tasks.named(&amp;quot;openApiGenerate&amp;quot;))
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le `afterEvaluate`é necessário porque`openApiGenerate`é uma extensão (plugin OpenAPI Generator) e não uma tarefa diretamente acessível durante a fase de configuração.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_erro_6_generategitproperties_falha_em_filteroutputstream_write&amp;quot;&amp;gt;Erro 6 : &amp;lt;code&amp;gt;generateGitProperties&amp;lt;/code&amp;gt; falha em `FilterOutputStream.write()&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A compilação Java passa. Os recursos são processados. Então :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;&amp;amp;gt; Task :generateGitProperties FAILED

No signature of method: java.io.FilterOutputStream.write() is applicable for argument types: (Integer) values: [103]&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plugin`gradle-git-properties`version 2.5.0 é incompatível com Java 21 / Gradle 9. Um bug interno tenta chamar`write(int)` por Groovy em um fluxo que ele fechou&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;correção&amp;lt;/strong&amp;gt;em`gradle.properties`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-properties hljs&amp;quot; data-lang=&amp;quot;properties&amp;quot;&amp;gt;gitPropertiesPluginVersion=2.5.7&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_finalmente_testcontainers_entra_em_cena&amp;quot;&amp;gt;Finalmente: Testcontainers entra em cena&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Até aqui, era depuração de script de build pura. Cada erro era uma incompatibilidade do Gradle 9 ou do Java 21 nos scripts de build. Após as seis correções acima, o`./gradlew assemble`passa com sucesso.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas`./gradlew build`executa também os testes, e os testes Cucumber usam o Testcontainers para iniciar um PostgreSQL efêmero.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;Could not find a valid Docker environment.

EnvironmentAndSystemPropertyClientProviderStrategy: failed with exception BadRequestException
(Status 400: {&amp;quot;message&amp;quot;:&amp;quot;client version 1.32 is too old. Minimum supported API version is 1.40&amp;quot;}
UnixSocketClientProviderStrategy: failed with exception BadRequestException
(Status 400: {&amp;quot;message&amp;quot;:&amp;quot;client version 1.32 is too old. Minimum supported API version is 1.40&amp;quot;}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Testcontainers é incapaz de comunicar com o Docker. Duas estratégias diferentes (variáveis de ambiente + soquete Unix) falham no mesmo erro.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-docker-handshake&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 5) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false

actor &amp;quot;Testcontainers
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false

actor &amp;quot;Testcontainers
1.20.6&amp;quot; as TC
participant &amp;quot;docker-java
3.4.1&amp;quot; as DJ
participant &amp;quot;Docker Engine
29.4.1&amp;quot; as DE

TC -&amp;amp;gt; DJ : new DockerClient()
DJ -&amp;amp;gt; DE : GET /_ping\nUser-Agent: docker-java/3.4.1\nAPI-Version: 1.32
DE --&amp;amp;gt; DJ : HTTP 400 Bad Request\nclient version 1.32 is too old.\nMinimum supported API version is 1.40
DJ --&amp;amp;gt; TC : BadRequestException
TC --&amp;amp;gt; TC : Could not find a valid\nDocker environment

note right of DE
  Docker Engine 29.x impose
  API minimum = 1.40
  (défaut ancien : 1.24)
end note

note left of DJ
  docker-java 3.4.1 envoie
  hardcoded API version 1.32
  dans l&amp;#39;en-tête HTTP
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_fase_1_a_pista_docker_java&amp;quot;&amp;gt;Fase 1 : a pista docker-java&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Primeiro reflexo: o cliente Docker Java usado pelo Testcontainers envia a versão da API`1.32`no handshake HTTP, mas o Docker Engine 29.4.1 exige um mínimo de`1.40`O problema está do lado do cliente, não do daemon.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Verificação da versão do docker-java resolvida pelo Testcontainers 1.20.6 :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ ./gradlew dependencies --configuration testRuntimeClasspath | grep docker-java

+--- com.github.docker-java:docker-java-api:3.4.1
+--- com.github.docker-java:docker-java-transport:3.4.1&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;docker-java 3.4.1, datando de Testcontainers 1.20.6. A última versão pública do docker-java é 3.5.1. Vamos tentar forçar a atualização da versão via`resolutionStrategy`no bloco`configurations` de &amp;lt;code&amp;gt;build.gradle&amp;lt;/code&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy hljs&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;configurations {
    all {
        resolutionStrategy.eachDependency { details -&amp;amp;gt;
            if (details.requested.group == &amp;quot;com.github.docker-java&amp;quot;
                &amp;amp;amp;&amp;amp;amp; details.requested.name.startsWith(&amp;quot;docker-java&amp;quot;)) {
                details.useVersion(&amp;quot;3.5.1&amp;quot;)
            }
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Reexecução do build. Mesmo erro:`client version 1.32 is too old`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Verificação do cache Gradle: os JARs foram baixados novamente, mas o erro persiste. docker-java 3.5.1 ainda envia`1.32`. Esta versão, portanto, não é suficiente para Docker Engine 29.4.1.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ação de purificação&amp;lt;/strong&amp;gt;: limpar completamente o cache do Gradle, só para ter certeza :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;rm -rf ~/.gradle/caches
rm -rf /path/to/project/.gradle&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Recarga completa após limpeza. Mesmo erro.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_phase_2_pesquisa_web_e_trilha_de_navegação_github&amp;quot;&amp;gt;Phase 2: pesquisa web e trilha de navegação GitHub&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;docker-java 3.5.1 é insuficiente. Só resta procurar se alguém já encontrou este problema exato.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Pesquisa direcionada nos issues testcontainers-java e docker-java :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;site:github.com/testcontainers &amp;quot;client version 1.32 is too old&amp;quot;
site:github.com/docker-java &amp;quot;client version 1.32&amp;quot; Docker Engine 29&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Primeiros resultados imediatos :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/testcontainers/testcontainers-java/issues/11210&amp;quot;&amp;gt;testcontainers-java#11210&amp;lt;/a&amp;gt;—&amp;lt;code&amp;gt;[Bug]: client version 1.32 is too old. Minimum supported API version is 1.44&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/testcontainers/testcontainers-java/issues/11212&amp;quot;&amp;gt;testcontainers-java#11212&amp;lt;/a&amp;gt;—&amp;lt;code&amp;gt;[Bug]: Docker 29.0.0 could not find a valid Docker environment&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/testcontainers/testcontainers-java/issues/11491&amp;quot;&amp;gt;testcontainers-java#11491&amp;lt;/a&amp;gt;—&amp;lt;code&amp;gt;[Bug]: Incompatibility in Ubuntu Github runners with Docker Engine 29.1.*&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O caminho de navegação está claro: o Docker Engine 29.x elevou sua versão mínima da API para`1.24`(defeito antigo) à`1.40`. Testcontainers 1.20.x utiliza o docker-java 3.4.x que negocia a versão da API enviando`1.32`. O daemon recusa educadamente mas firmemente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No problema #11491, um mantenedor responde :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta resposta contém&amp;lt;strong&amp;gt;duas informações críticas&amp;lt;/strong&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Testcontainers 2.0.3+ resolve o problema&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Os módulos foram&amp;lt;strong&amp;gt;renomeados com o prefixo `testcontainers-&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_fase_3_a_armadilha_dos_artefatos_renomeados&amp;quot;&amp;gt;Fase 3 : a armadilha dos artefatos renomeados&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A renomeação é bruta. No Testcontainers 2.x, nenhum dos nomes antigos funciona mais sob esse nome:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 10%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nome antigo (1.x)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Novo nome (2.x)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;org.testcontainers:postgresql&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;org.testcontainers:testcontainers-postgresql&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;org.testcontainers:jdbc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;org.testcontainers:testcontainers-jdbc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;org.testcontainers:junit-jupiter&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;org.testcontainers:testcontainers-junit-jupiter&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;org.testcontainers:testcontainers&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;inalterado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Primeira tentativa: aplicar a BOM Testcontainers 2.0.5 e os novos nomes de artefatos em`build.gradle`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy hljs&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;dependencies {
    testImplementation platform(&amp;quot;org.testcontainers:testcontainers-bom:2.0.5&amp;quot;)
    testImplementation &amp;quot;org.testcontainers:testcontainers-postgresql&amp;quot;
    testImplementation &amp;quot;org.testcontainers:testcontainers-jdbc&amp;quot;
    testImplementation &amp;quot;org.testcontainers:testcontainers-junit-jupiter&amp;quot;
    testImplementation &amp;quot;org.testcontainers:testcontainers&amp;quot;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;./gradlew build&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;FAILURE: Could not find org.testcontainers:jdbc:2.0.5.
Could not find org.testcontainers:junit-jupiter:2.0.5.&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O BOM de testcontainers-bom não basta.&amp;lt;/strong&amp;gt;Por quê? Porque o Spring Boot 3.4.5 expõe seu próprio BOM de gerenciamento de dependências.&amp;lt;code&amp;gt;spring-boot-dependencies&amp;lt;/code&amp;gt;) que&amp;lt;strong&amp;gt;ganha&amp;lt;/strong&amp;gt;no BOM do Testcontainers na resolução transitiva. Spring Boot corrige`testcontainers` à &amp;lt;code&amp;gt;1.20.6&amp;lt;/code&amp;gt;, e portanto todos os artefatos que não estão explicitamente no BOM do Spring Boot (como os novos`testcontainers-*&amp;lt;code&amp;gt;) resolvem para os antigos nomes`1.20.6&amp;lt;/code&amp;gt;-- que já não existem nesta versão.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao inspecionar o`dependencies --configuration testRuntimeClasspath`, encontra-se :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;+--- org.testcontainers:testcontainers -&amp;amp;gt; 1.20.6 (*)
|    \--- org.testcontainers:testcontainers:2.0.5 -&amp;amp;gt; 1.20.6&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le &amp;lt;code&amp;gt;&amp;amp;#8594;`indica a substituição : Testcontainers 2.0.5 é &amp;lt;strong&amp;gt;requerido&amp;lt;/strong&amp;gt;, mas Spring Boot força`1.20.6&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-bom-war&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 8) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle

left to right direction

component &amp;quot;build.gradle
^^^^^
 Syntax Error? (Assumed diagram type: class)

@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle

left to right direction

component &amp;quot;build.gradle
(consumidor)&amp;quot; as BG #E3F2FD {
    rectangle &amp;quot;pedido\n`testcontainers-bom:2.0.5&amp;quot; as REQ
    rectangle &amp;quot;pedido
`testcontainers-postgresql&amp;quot; as REQ2
}

component &amp;quot;Maven Central&amp;quot; as MC #FFF3E0

component &amp;quot;Declarações&amp;quot; as DEC #E8F5E9 {
    rectangle &amp;quot;testcontainers-bom:2.0.5`
corrige 2.0.5&amp;quot; as BOM2
    rectangle &amp;quot;spring-boot-dependencies:3.4.5`\nfixa 1.20.6&amp;quot; as BOM1
}

component &amp;quot;Resolução Gradle&amp;quot; as RES #FFEBEE {
    rectangle &amp;quot;Versão vencedora\n`testcontainers:1.20.6&amp;quot; as WIN #EF9A9A
}

BG --&amp;amp;gt; DEC : &amp;quot;analisa as BOMs&amp;quot;
BOM2 --&amp;amp;gt; RES : &amp;quot;propõe 2.0.5&amp;quot;
BOM1 --&amp;amp;gt; RES : &amp;quot;propõe 1.20.6&amp;quot;
WIN -&amp;amp;gt; MC : &amp;quot;baixe\ntestcontainers:1.20.6&amp;quot;

note bottom of WIN
  Spring Boot BOM prime car
  le plugin spring-boot applique
  ses dépendances de gestion
  après la résolution du consommateur
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_fase_4_forçar_a_resolução&amp;quot;&amp;gt;Fase 4 : forçar a resolução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A estratégia se torna dupla:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Forçar o docker-java para sua versão 3.7.1 (testada como compatível com Docker Engine 29)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Force o Testcontainers core para 2.0.5 contornando o BOM do Spring Boot&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-resolution-override&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 9) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle

left to right direction

component &amp;quot;build.gradle\n(com resolutionStrategy)&amp;quot; as BG #E8F5E9 {
    rectangle &amp;quot;BOM Testcontainers
^^^^^
 Syntax Error? (Assumed diagram type: class)

@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle

left to right direction

component &amp;quot;build.gradle\n(com resolutionStrategy)&amp;quot; as BG #E8F5E9 {
    rectangle &amp;quot;BOM Testcontainers
`2.0.5&amp;quot; as BOM2
    rectangle &amp;quot;resolutionStrategy\n.eachDependency&amp;quot; as RS #66BB6A
}

component &amp;quot;Conflito resolvido&amp;quot; as RESOLVED #E3F2FD {
    rectangle &amp;quot;docker-java\n`3.7.1` (forçado)&amp;quot; as DJ
    rectangle &amp;quot;testcontainers
`2.0.5` (forçado)&amp;quot; as TC #81C784
    rectangle &amp;quot;testcontainers-postgresql
`2.0.5&amp;quot; as TPG
    rectangle &amp;quot;testcontainers-jdbc
`2.0.5&amp;quot; as TJ
    rectangle &amp;quot;testcontainers-junit-jupiter
`2.0.5&amp;quot; as TJJ
}

component &amp;quot;BOM Spring Boot\n(implícito, sobrescrito)&amp;quot; as BOM1 #FFEBEE

component &amp;quot;Maven Central&amp;quot; as MC #FFF3E0

BG --&amp;amp;gt; RESOLVED : &amp;quot;BOM Testcontainers 2.0.5\npassa diretamente&amp;quot;
RS -&amp;amp;gt; DJ : &amp;quot;força 3.7.1&amp;quot;
RS -&amp;amp;gt; TC : &amp;quot;força 2.0.5&amp;quot;
RESOLVED --&amp;amp;gt; MC : &amp;quot;Baixe os JARs\ncompatíveis com Docker 29&amp;quot;
BOM1 -[#gray,dashed]-&amp;amp;gt; RESOLVED : &amp;quot;ignorado no testcontainers\net docker-java&amp;quot;

note bottom of RS
  Le `eachDependency` intercepte
  la résolution Gradle AVANT
  que le BOM Spring Boot ne gagne.
  C&amp;#39;est une intervention chirurgicale.
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Correção final&amp;lt;/strong&amp;gt;em`build.gradle`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy hljs&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;configurations {
    all {
        resolutionStrategy.eachDependency { details -&amp;amp;gt;
            if (details.requested.group == &amp;quot;com.github.docker-java&amp;quot;
                &amp;amp;amp;&amp;amp;amp; details.requested.name.startsWith(&amp;quot;docker-java&amp;quot;)) {
                details.useVersion(&amp;quot;3.7.1&amp;quot;)
            }
            if (details.requested.group == &amp;quot;org.testcontainers&amp;quot;
                &amp;amp;amp;&amp;amp;amp; details.requested.name == &amp;quot;testcontainers&amp;quot;) {
                details.useVersion(&amp;quot;2.0.5&amp;quot;)
            }
        }
    }
}

dependencies {
    testImplementation platform(&amp;quot;org.testcontainers:testcontainers-bom:2.0.5&amp;quot;)
    testImplementation &amp;quot;org.testcontainers:testcontainers-jdbc&amp;quot;
    testImplementation &amp;quot;org.testcontainers:testcontainers-junit-jupiter&amp;quot;
    testImplementation &amp;quot;org.testcontainers:testcontainers-postgresql&amp;quot;
    testImplementation &amp;quot;org.testcontainers:testcontainers&amp;quot;
    // ... autres dépendances Spring Boot
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le `resolutionStrategy.eachDependency`é a única maneira de contornar o BOM`spring-boot-dependencies`gerenciado pelo plugin Spring Boot. O teste de igualdade sobre`details.requested.name == &amp;quot;testcontainers&amp;quot;`é voluntariamente restritivo: forçamos apenas o módulo core. Os módulos`testcontainers-*`Seguem o BOM do Testcontainers 2.0.5 que declaramos explicitamente&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_resultado_final&amp;quot;&amp;gt;Resultado final&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ ./gradlew build --no-daemon

&amp;amp;gt; Task :consoleLauncherTest
[2 containers found]
[2 containers started]
[2 containers successful]

BUILD SUCCESSFUL in 1m 16s&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Testcontainers 2.0.5 + docker-java 3.7.1 finalmente conseguem criar seus containers PostgreSQL via Docker Engine 29.4.1. O erro de negócio final (HTTP 500 no teste Cucumber) é um problema de aplicação totalmente independente do script de build.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_tabela_resumida_das_correções&amp;quot;&amp;gt;Tabela resumida das correções&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;#&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Arquivo&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Problema&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;correção&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;buildSrc/src/main/groovy/jhipster.cucumber-conventions.gradle&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Variável`reportsDir`inexistente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;outputs.dir(cucumberReportsDir)&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;buildSrc/src/main/groovy/jhipster.cucumber-conventions.gradle&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`main`obsoleto Gradle 9&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;mainClass&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;build.gradle&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`sourceCompatibility`top-level proibido Gradle 9&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Bloco`java { sourceCompatibility = JavaVersion.VERSION_21 }`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;4&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;build.gradle&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Asserção Groovy sintaticamente inválida&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;assert &amp;amp;#8230;&amp;amp;#8203; in [&amp;quot;17&amp;quot;, &amp;quot;21&amp;quot;, &amp;quot;23&amp;quot;, &amp;quot;24&amp;quot;]&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;build.gradle&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Dependência implícita`compileKotlin`→&amp;lt;code&amp;gt;openApiGenerate&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;afterEvaluate { tasks.named(&amp;quot;compileKotlin&amp;quot;).dependsOn(&amp;quot;openApiGenerate&amp;quot;) }&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;6&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;gradle.properties&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`gitPropertiesPluginVersion`incompatível Java 21&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;2.5.7&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;7&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Cache Gradle&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;docker-java 3.5.1 testado, resolvido mas insuficiente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Limpeza + passagem docker-java 3.7.1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;8&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;build.gradle&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Testcontainers 1.20.6 incompatível com Docker Engine 29&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Forçamento Testcontainers 2.0.5 + prefix`testcontainers-*`+ resolutionStrategy vs Spring Boot BOM&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se o seu build JHipster/Gradle falhar após uma atualização de versão para Gradle 9 + Docker Engine 29, os erros se dividem em duas categorias :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Erros de script de build&amp;lt;/strong&amp;gt;(as 6 primeiras) : O Gradle 9 remove propriedades e sintaxes que vinham funcionando há anos (&amp;lt;code&amp;gt;sourceCompatibility=&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;main =&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;reportsDir&amp;lt;/code&amp;gt;). São migrações mecânicas quando se conhece a nova API.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Erros de tempo de execução Docker&amp;lt;/strong&amp;gt;(as 2 últimas): Docker Engine 29.4.1 exclui clientes API &amp;amp;lt; 1.40. Testcontainers 1.20.6 (imposto pelo Spring Boot 3.4.5) usa docker-java 3.4.1 que envia 1.32. Somente Testcontainers 2.0.5 + docker-java 3.7.1 resolve o problema, com a renomeação dos artefatos e a imposição de versão via`resolutionStrategy.eachDependency`como único meio de contornar o BOM implícito do Spring Boot.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O script de build Gradle na raiz do projeto está agora limpo.`./gradlew build`passa para o Java 21, Gradle 9.4.1, Spring Boot 3.4.5 e Docker Engine 29.4.1.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Gouverner un Agent IA avec du AsciiDoc : Ma Stratégie Eager/Lazy pour des Sessions Opencode sans Fuite de Contexte</title>
            <link >https://cheroliv.com//blog/2026/0108_gouvernance_agent_opencode_eager_lazy_post.html</link>
            <pubDate>Fri, 24 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0108_gouvernance_agent_opencode_eager_lazy_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_résumé&amp;quot;&amp;gt;Résumé&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quand on travaille avec un agent IA comme Opencode sur des projets complexes sur plusieurs sessions, on fait face à un problème fondamental : &amp;lt;strong&amp;gt;la fuite de contexte&amp;lt;/strong&amp;gt;. L&amp;amp;#8217;agent ne se souvient pas de la session précédente. Tout ce qu&amp;amp;#8217;on lui a expliqué&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;l&amp;amp;#8217;architecture, les conventions, l&amp;amp;#8217;état du backlog&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;est perdu. Reconstruire ce contexte à chaque session est coûteux, lent et source d&amp;amp;#8217;erreur.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cet article expose la stratégie artisanale que j&amp;amp;#8217;ai construite pour résoudre ce problème : un système de gouvernance persistant basé sur des fichiers AsciiDoc, avec une dichotomie &amp;lt;strong&amp;gt;Eager/Lazy&amp;lt;/strong&amp;gt; pour optimiser la consommation du token de contexte, et une &amp;lt;strong&amp;gt;procédure de fin de session incontournable&amp;lt;/strong&amp;gt; pour assurer la continuité.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_la_scène_lundi_21_avril_9h00&amp;quot;&amp;gt;La Scène : Lundi 21 Avril, 9h00&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Je rouvre Opencode pour reprendre mon plugin Gradle &amp;lt;code&amp;gt;plantuml-plugin&amp;lt;/code&amp;gt;. Hier soir, j&amp;amp;#8217;ai passé trois heures à discuter avec l&amp;amp;#8217;agent de l&amp;amp;#8217;architecture du pool de clés API&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;rotation round-robin, gestion des quotas, fallback automatique. Ce matin, l&amp;amp;#8217;agent me regarde avec des yeux de poisson rouge.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;Bonjour, je suis votre assistant Opencode. Comment puis-je vous aider aujourd&amp;amp;#8217;hui ? &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Pas de&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;Ah oui, le pool de clés API, on en était à la structure YAML. Pas de&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;Attention, &amp;lt;code&amp;gt;PlantumlManager&amp;lt;/code&amp;gt; est un objet Kotlin singleton, pas une classe. Pas de&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;Non, on a décidé hier que &amp;lt;code&amp;gt;SyntaxValidationResult&amp;lt;/code&amp;gt; restait une sealed class nested dans &amp;lt;code&amp;gt;PlantumlService&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Tout est à refaire. Ou plutôt : tout est à réexpliquer. Je vais passer les vingt premières minutes de ma session à reconstituer un contexte que l&amp;amp;#8217;agent a déjà eu entre les mains hier. Vingt minutes de tokens brûlés. Vingt minutes où je pourrais coder, mais où je fais du pédagogique obligatoire.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ce n&amp;amp;#8217;est pas un bug d&amp;amp;#8217;Opencode. C&amp;amp;#8217;est la nature même des LLM conversationnels : entre deux sessions, la mémoire de travail est &amp;lt;strong&amp;gt;entièrement effacée&amp;lt;/strong&amp;gt;. L&amp;amp;#8217;agent ne se souvient pas de la mission précédente, des décisions prises, des pièges identifiés, du code qu&amp;amp;#8217;on a écrit ensemble.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;J&amp;amp;#8217;ai vécu ça des dizaines de fois. Sur quatre projets simultanés. Avec des sessions qui s&amp;amp;#8217;enchaînent sur des semaines. J&amp;amp;#8217;ai calculé : en moyenne, &amp;lt;strong&amp;gt;30 à 40 % du temps de session&amp;lt;/strong&amp;gt; était consacré à recontextualiser l&amp;amp;#8217;agent. À la session 87 du projet &amp;lt;code&amp;gt;plantuml-plugin&amp;lt;/code&amp;gt;, j&amp;amp;#8217;ai craqué. Je ne pouvais plus me permettre de réexpliquer pour la dixième fois que &amp;lt;code&amp;gt;AttemptEntry&amp;lt;/code&amp;gt; est une data class top-level dans &amp;lt;code&amp;gt;DiagramProcessor.kt&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Il me fallait un système. Pas un hack. Une vraie gouvernance.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_la_genèse_du_chaos_à_la_méthode&amp;quot;&amp;gt;La Genèse : Du Chaos à la Méthode&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_les_premières_sessions_lâge_des_ténèbres&amp;quot;&amp;gt;Les Premières Sessions : L&amp;amp;#8217;Âge des Ténèbres&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mon premier projet avec Opencode, &amp;lt;code&amp;gt;plantuml-plugin&amp;lt;/code&amp;gt;, a démarré sans aucune gouvernance. Je pose une question, l&amp;amp;#8217;agent répond, on itère, la session se termine, et le lendemain on repart de zéro. C&amp;amp;#8217;était la session 1, puis la 2, puis la 3&amp;amp;#8230;&amp;amp;#8203; jusqu&amp;amp;#8217;à la session 62 où je réalise que j&amp;amp;#8217;ai perdu des heures cumulées à réexpliquer la même architecture.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;À la session 62, les chiffres sont là : &amp;lt;strong&amp;gt;198 tests unitaires passent&amp;lt;/strong&amp;gt;, &amp;lt;strong&amp;gt;42 tests fonctionnels validés&amp;lt;/strong&amp;gt;, le plugin fonctionne. Mais le coût cognitif est insupportable. Chaque nouvelle session commence par un monologue de vingt minutes sur la structure du projet.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_lépisode_du_site_yml_détruit_session_2_bakery_plugin&amp;quot;&amp;gt;L&amp;amp;#8217;Épisode du site.yml Détruit (Session 2, bakery-plugin)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;La méthode naît aussi d&amp;amp;#8217;une catastrophe. Sur le projet &amp;lt;code&amp;gt;bakery-gradle&amp;lt;/code&amp;gt;, à la session 2, je demande à l&amp;amp;#8217;agent de modifier le fichier &amp;lt;code&amp;gt;site.yml&amp;lt;/code&amp;gt;. L&amp;amp;#8217;agent, sans vérifier si le fichier est versionné, fait un &amp;lt;code&amp;gt;Write&amp;lt;/code&amp;gt; complet qui écrase le contenu. Résultat : les tokens réels (clés API Firebase, secrets de déploiement) sont remplacés par des placeholders factices. Le fichier n&amp;amp;#8217;était pas dans git&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;il était dans &amp;lt;code&amp;gt;.gitignore&amp;lt;/code&amp;gt; pour protéger les secrets.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sans sauvegarde. Sans &amp;lt;code&amp;gt;git restore&amp;lt;/code&amp;gt; possible. Je suis bloqué. Il me faut reconstruire manuellement le fichier de configuration, retrouver les tokens dans mes gestionnaires de mots de passe, tout recoller.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;C&amp;amp;#8217;est de cette frustration que naît la &amp;lt;strong&amp;gt;Règle Absolue 1b&amp;lt;/strong&amp;gt; :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ &amp;lt;strong&amp;gt;NE JAMAIS écraser&amp;lt;/strong&amp;gt; un fichier config avec un &amp;lt;code&amp;gt;Write&amp;lt;/code&amp;gt; complet quand un &amp;lt;code&amp;gt;Edit&amp;lt;/code&amp;gt; partiel suffit. &amp;lt;strong&amp;gt;NE JAMAIS remplacer&amp;lt;/strong&amp;gt; des valeurs sensibles par des valeurs factices. &amp;lt;strong&amp;gt;Vérifier &amp;lt;code&amp;gt;git check-ignore&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;git ls-files&amp;lt;/code&amp;gt;&amp;lt;/strong&amp;gt; avant toute modification. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cette règle, aujourd&amp;amp;#8217;hui gravée dans le marbre de tous mes fichiers &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt; sur quatre projets, est née d&amp;amp;#8217;une erreur réelle qui m&amp;amp;#8217;a coûté une heure de travail manuel.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_la_migration_markdown_asciidoc_session_1_cheroliv_com&amp;quot;&amp;gt;La Migration Markdown → AsciiDoc (Session 1, cheroliv.com)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le 25 avril 2026, sur &amp;lt;code&amp;gt;cheroliv.com&amp;lt;/code&amp;gt;, je prends une décision radicale : convertir l&amp;amp;#8217;intégralité de la gouvernance de Markdown vers AsciiDoc. Ce n&amp;amp;#8217;est pas esthétique. C&amp;amp;#8217;est fonctionnel. AsciiDoc offre une structure sémantique que les LLM lisent mieux : sections hiérarchiques, tableaux typés, admonitions (&amp;lt;code&amp;gt;NOTE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;WARNING&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;CAUTION&amp;lt;/code&amp;gt;), attributs de document lisibles par la machine.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;La session 1 de &amp;lt;code&amp;gt;cheroliv.com&amp;lt;/code&amp;gt; formalise la structure :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Conversion de &amp;lt;code&amp;gt;AGENTS.md&amp;lt;/code&amp;gt; en &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Création des agents spécialisés : &amp;lt;code&amp;gt;CODER.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SCRUM_MASTER.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PLANTUML_DESIGNER.adoc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Création de la structure Eager/Lazy : &amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SESSIONS_HISTORY.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;AGENT_SESSION_MANAGER.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SESSION_CHECKLIST.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PROCEDURES.adoc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Un seul commit : &amp;lt;code&amp;gt;90975e9 refactor: migrate agent governance from Markdown to AsciiDoc&amp;lt;/code&amp;gt;. Et le site continue de fonctionner.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-timeline&amp;quot; class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-1ffbad829836ed4bec6c66e6b3038145.svg&amp;quot; alt=&amp;quot;Timeline de croissance des sessions sur les 4 projets&amp;quot; width=&amp;quot;2373&amp;quot; height=&amp;quot;290&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;La timeline ci-dessus illustre la progression réelle. Le point de bascule est la session 87 : c&amp;amp;#8217;est là que la frustration du recontextualisation répétée dépasse le seuil de tolérance, et que la méthode Eager/Lazy cesse d&amp;amp;#8217;être une idée pour devenir une obligation.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_la_stratégie_eagerlazy_en_profondeur&amp;quot;&amp;gt;La Stratégie : Eager/Lazy en Profondeur&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_philosophie_cache_informatique_appliqué_à_la_cognition&amp;quot;&amp;gt;Philosophie : Cache Informatique Appliqué à la Cognition&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mon approche s&amp;amp;#8217;inspire directement de la gestion de cache informatique. Tout ce qui est &amp;lt;strong&amp;gt;critique et fréquemment utilisé&amp;lt;/strong&amp;gt; doit être immédiatement accessible (&amp;lt;strong&amp;gt;Eager&amp;lt;/strong&amp;gt;). Tout ce qui est &amp;lt;strong&amp;gt;contextuel ou volumineux&amp;lt;/strong&amp;gt; doit être chargé sur demande (&amp;lt;strong&amp;gt;Lazy&amp;lt;/strong&amp;gt;).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.6676%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Eager (Tableau de Bord)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Lazy (Manuel du Propriétaire)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Taille&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;amp;lt; 100 lignes, &amp;amp;lt; 10k tokens&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Illimité, détaillé&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Chargement&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Auto, en début de session&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sur demande de l&amp;amp;#8217;agent&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Contenu&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Règles absolues, mission courante, état critique&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Archives de sessions, historique complet, procédures détaillées, références techniques&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Rôle&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Orienter immédiatement l&amp;amp;#8217;agent&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Répondre aux questions de contexte profond&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_les_fichiers_eager_le_tableau_de_bord&amp;quot;&amp;gt;Les Fichiers Eager : Le Tableau de Bord&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ces fichiers vivent à la racine de chaque projet et sont chargés automatiquement par l&amp;amp;#8217;agent au début de chaque session. Ils forment le &amp;lt;strong&amp;gt;tableau de bord&amp;lt;/strong&amp;gt;&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;information critique, immédiatement accessible.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-eager-lazy&amp;quot; class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-05c08d0c2b5bf285c4c1fee60314a0e7.svg&amp;quot; alt=&amp;quot;Architecture Eager/Lazy des fichiers de gouvernance&amp;quot; width=&amp;quot;1769&amp;quot; height=&amp;quot;5760&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt;&amp;lt;/strong&amp;gt;&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;Le fichier maître. Sur &amp;lt;code&amp;gt;cheroliv.com&amp;lt;/code&amp;gt;, il fait 200 lignes et contient :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Les règles absolues du projet (pas de commit sans permission, pas de &amp;lt;code&amp;gt;rm&amp;lt;/code&amp;gt; sans confirmation)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;La structure du projet et les conventions de code&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Les commandes essentielles (&amp;lt;code&amp;gt;./gradlew serve&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;./gradlew test&amp;lt;/code&amp;gt;)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Les épopées et le backlog produit (user stories priorisées)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Les critères de qualité transverses (accessibilité, responsive, compatibilité)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sur &amp;lt;code&amp;gt;bakery-plugin&amp;lt;/code&amp;gt;, la Règle 0 est différente : &amp;lt;strong&amp;gt;&amp;lt;code&amp;gt;./gradlew -q publishToMavenLocal&amp;lt;/code&amp;gt; obligatoire après chaque modification du code source&amp;lt;/strong&amp;gt;. Parce que tester le plugin sans republier le JAR local m&amp;amp;#8217;a fait perdre une heure à débugger un code qui n&amp;amp;#8217;était pas encore empaqueté.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;code&amp;gt;PROMPT_REPRISE.adoc&amp;lt;/code&amp;gt;&amp;lt;/strong&amp;gt;&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;La mission de la session en cours. Mis à jour à chaque fin de session, il contient :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Le numéro de session et la mission prioritaire&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Le résumé de la session précédente (ce qui a été fait, ce qui reste à faire)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Les critères d&amp;amp;#8217;acceptation de la session courante&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Les rappels techniques spécifiques&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;code&amp;gt;.agents/INDEX.adoc&amp;lt;/code&amp;gt;&amp;lt;/strong&amp;gt;&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;Le point d&amp;amp;#8217;entrée. Il résume les règles absolues, les sessions récentes, et surtout le &amp;lt;strong&amp;gt;portefeuille de projets&amp;lt;/strong&amp;gt; gérés avec la même méthodologie. À ce jour, cinq projets y figurent :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code&amp;gt;----
| magic-stick    | Session 23 | SCRIPT_VERIFICATION.adoc | 2026-04-27 |
| bakery-gradle  | Session 11 | TEST_COVERAGE_ANALYSIS   | 2026-04-27 |
| cheroliv.com   | Session 9  | TEST_COVERAGE_ANALYSIS   | 2026-04-27 |
| plantuml-gradle| Session 133| TEST_COVERAGE_ANALYSIS   | 2026-04-23 |
| jhipster-gradle-plugins | Session 1 | TEST_COVERAGE_ANALYSIS | 2026-04-28 |
----

**`*_ESSENTIALS.adoc`** -- Un ajout récent (Session 109, plantuml-plugin) pour optimiser encore le contexte Eager. Au lieu de charger 200 lignes de contexte métier sur le pool de clés API, je charge 50 lignes de l&amp;#39;essentiel, et les 150 autres lignes restent en LAZY dans `*_REFERENCE.adoc`.

Résultat mesuré : passage de **~25k tokens EAGER à ~10k tokens** (gain de 60%). L&amp;#39;agent n&amp;#39;a plus besoin de rappels énergivores.

==== Le Chaînon Manquant : `opencode.json`

Je dois vous avouer une chose que j&amp;#39;ai failli oublier de documenter. Au-dessus de tous ces fichiers .adoc, il y a un minuscule fichier JSON sans lequel rien ne fonctionne. Il s&amp;#39;appelle `opencode.json` et il fait six lignes. Littéralement six lignes.

[source,json]
----
{
  &amp;quot;$schema&amp;quot;: &amp;quot;https://opencode.ai/config.json&amp;quot;,
  &amp;quot;instructions&amp;quot;: [
    &amp;quot;AGENT.adoc&amp;quot;
  ]
}
----

C&amp;#39;est ce fichier qui dit à Opencode : « Au démarrage, charge `AGENT.adoc` automatiquement. » Sans lui, l&amp;#39;agent est une page blanche exactement comme je le décrivais au début de l&amp;#39;article. Avec lui, l&amp;#39;agent a déjà entre les mains les règles absolues, l&amp;#39;architecture du projet, et les commandes essentielles — avant même que je dise bonjour.

J&amp;#39;ai découvert l&amp;#39;importance de ce fichier par hasard. Sur `bakery-plugin`, il n&amp;#39;existait pas. Je me demandais pourquoi l&amp;#39;agent était systématiquement plus « perdu » sur ce projet que sur les autres. Les règles absolues étaient bien dans `AGENT.adoc` — mais `AGENT.adoc` n&amp;#39;était jamais chargé. L&amp;#39;agent ne lisait que ce que je lui disais de lire, manuellement, à chaque session. C&amp;#39;était la session 11 de `bakery-plugin` quand j&amp;#39;ai réalisé l&amp;#39;absence du `opencode.json`. Je l&amp;#39;ai créé — et la session 12 a démarré comme les autres.

Ce fichier est tellement évident pour moi maintenant que je ne pensais même plus à lui. Une erreur classique du développeur qui connaît trop son outil. Aujourd&amp;#39;hui, je le crée systématiquement *avant* `AGENT.adoc`. C&amp;#39;est la première pierre.

==== La Dualité de `INDEX.adoc`

Une autre subtilité qui mérite d&amp;#39;être explicitée : `INDEX.adoc` vit dans `.agents/` — un dossier que j&amp;#39;ai présenté comme LAZY. Pourtant, je le liste comme EAGER dans tous mes tableaux. Il y a une tension apparente ici.

La réalité terrain : les fichiers `.agents/INDEX.adoc` sont bien chargés automatiquement en début de session, au même titre que `AGENT.adoc` et `PROMPT_REPRISE.adoc`. Ils sont dans `.agents/` pour des raisons d&amp;#39;organisation — ne pas envahir la racine — mais leur comportement est EAGER.

Sur `plantuml-plugin`, `INDEX.adoc` fait 200 lignes et contient les règles absolues *complètes* avec leur historique (les leçons des sessions passées), les EPICs avec scores, et le portefeuille de projets. C&amp;#39;est le document que l&amp;#39;agent consulte pour savoir « où on en est ». Sur `bakery-plugin`, il fait 150 lignes avec la roadmap et les sessions récentes.

La redondance volontaire entre `AGENT.adoc` et `INDEX.adoc` peut surprendre. Les règles absolues sont présentes dans les deux. Pourquoi ? Parce qu&amp;#39;elles remplissent deux rôles différents : dans `AGENT.adoc`, elles sont *explicatives* (le storytelling de la règle, la leçon apprise) ; dans `INDEX.adoc`, elles sont *exécutives* (la règle nue, sans justification, pour une consultation rapide). L&amp;#39;agent lit `AGENT.adoc` une fois pour *comprendre* ; il relit `INDEX.adoc` à chaque session pour *appliquer*. Deux usages, deux formats.

[plantuml, format=svg, id=diag-dualite-agent-index, alt=&amp;quot;Comparaison entre AGENT.adoc (narratif) et INDEX.adoc (exécutif)&amp;quot;]
----
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center

title Dualité AGENT.adoc ←→ INDEX.adoc

left to right direction

rectangle &amp;quot;AGENT.adoc\n(Racine — EAGER)&amp;quot; as AGENT #E3F2FD {
  rectangle &amp;quot;📖 **Format Narratif**\nLe storytelling de la règle\nla leçon apprise, le contexte&amp;quot; as NARR
  rectangle &amp;quot;🏗️ **Architecture Complète**\nStructure projet, composants\nBacklog détaillé US&amp;quot; as ARCHI
  rectangle &amp;quot;📋 **Règles Explicatives**\nPourquoi la règle existe\nHistorique de l&amp;#39;incident&amp;quot; as EXPL
}

rectangle &amp;quot;INDEX.adoc\n(.agents/ — EAGER)&amp;quot; as INDEX #E8F5E9 {
  rectangle &amp;quot;⚡ **Format Exécutif**\nLa règle nue, sans justification\nConsultation rapide&amp;quot; as EXEC
  rectangle &amp;quot;📊 **Roadmap &amp;amp;amp; EPICs**\nTableau récapitulatif\nProgression, Score, Priorité&amp;quot; as ROAD
  rectangle &amp;quot;🌐 **Portefeuille Projets**\nVue transverse\n5 projets synchronisés&amp;quot; as PORT
}

AGENT --&amp;amp;gt; INDEX : &amp;quot;Agent lit AGENT.adoc\n1 fois pour **comprendre**&amp;quot;
INDEX --&amp;amp;gt; AGENT : &amp;quot;Agent relit INDEX.adoc\nchaque session pour **appliquer**&amp;quot;

note bottom of AGENT
  Taille max : 200 lignes
end note

note bottom of INDEX
  Taille max : 200 lignes
  Source de vérité en cas de divergence
end note

@enduml
----

Cette redondance assumée est un choix de conception. Elle consomme ~50 lignes supplémentaires de tokens EAGER — mais elle garantit que l&amp;#39;agent a toujours les règles sous les yeux, y compris dans le format concis qui facilite l&amp;#39;obéissance immédiate.

=== Les Fichiers LAZY : Le Manuel du Propriétaire

Ces fichiers vivent dans `.agents/` et ne sont lus que quand l&amp;#39;agent en a besoin. Ils constituent la véritable richesse de la méthode, car ils accumulent la connaissance du projet sans polluer le contexte courant.

[plantuml, format=svg, id=diag-agents-tree, alt=&amp;quot;Arborescence complète du dossier .agents/&amp;quot;]
----
@startuml
skinparam folderBackgroundColor #E3F2FD
skinparam folderBorderColor #1565C0
skinparam fileBackgroundColor #FFF3E0
skinparam fileBorderColor #EF6C00

folder &amp;quot;.agents/&amp;quot; as ROOT {
  file &amp;quot;INDEX.adoc\n(EAGER -- 200 lignes)&amp;quot; as IDX #E8F5E9
  file &amp;quot;AGENT_SESSION_MANAGER.adoc\n(Template session)&amp;quot; as ASM
  file &amp;quot;SESSION_CHECKLIST.adoc\n(Quand changer)&amp;quot; as CHK
  file &amp;quot;PROCEDURES.adoc\n(6 étapes + LAZY/EAGER)&amp;quot; as PRO
  file &amp;quot;SESSIONS_HISTORY.adoc\n(Toutes sessions)&amp;quot; as HIS

  folder &amp;quot;sessions/&amp;quot; as SESS {
    file &amp;quot;1-chore-migration.adoc&amp;quot; as S1
    file &amp;quot;109-formalisation-lazy.adoc&amp;quot; as S109 #FFECB3
    file &amp;quot;133-epic11-article.adoc&amp;quot; as S133
    file &amp;quot;... +130 autres&amp;quot; as SMORE
  }

  folder &amp;quot;archives/&amp;quot; as ARCH {
    file &amp;quot;COMPLETED_TASKS_2026-04.adoc&amp;quot; as CTA
    file &amp;quot;SESSIONS_HISTORY_83-95.adoc&amp;quot; as SHIST
    folder &amp;quot;sessions_summaries/&amp;quot; as SUM {
      file &amp;quot;SESSION_64_SUMMARY.adoc&amp;quot; as SU64
      file &amp;quot;SESSION_73_SUMMARY.adoc&amp;quot; as SU73
      file &amp;quot;...&amp;quot; as SUMORE
    }
    folder &amp;quot;prompts_archive/&amp;quot; as PARCH {
      file &amp;quot;PROMPT_REPRISE_S65.adoc&amp;quot; as PR65
      file &amp;quot;PROMPT_REPRISE_S75.adoc&amp;quot; as PR75
      file &amp;quot;...&amp;quot; as PMORE
    }
  }
}

IDX --&amp;amp;gt; SESS : &amp;quot;Indexe&amp;quot;
IDX --&amp;amp;gt; HIS : &amp;quot;Indexe&amp;quot;
IDX --&amp;amp;gt; ARCH : &amp;quot;Référence&amp;quot;

note right of S109
  Session 109 =
  Formalisation stratégie
  LAZY/EAGER
  Token : ~25k → ~10k
end note

@enduml
----

L&amp;#39;arborescence ci-dessus montre la structure réelle du dossier `.agents/` sur `plantuml-plugin`, le projet le plus mature. Notez la profondeur en trois couches : fichiers racine (métadonnées), dossier `sessions/` (archives chronologiques), et dossier `archives/` (agrégations et résumés). C&amp;#39;est cette profondeur qui transforme la gouvernance d&amp;#39;un simple fichier TODO en une **mémoire organisationnelle complète**.

**`.agents/sessions/{N}-{titre}.adoc`** -- Les archives détaillées de chaque session. Actuellement :

* `plantuml-plugin` : **133 sessions archivées** (de la session 1 à 133)
* `bakery-plugin` : **11 sessions**
* `magic-stick` : **23 sessions**
* `cheroliv.com` : **9 sessions formelles** + 7 sessions pré-système reconstituées rétroactivement

Chaque archive contient le contexte complet de la session, les décisions prises, les problèmes rencontrés et leur résolution, les commandes exécutées et leur output.

**`.agents/SESSIONS_HISTORY.adoc`** -- Un tableau récapitulatif de toutes les sessions avec un score. Exemple sur `cheroliv.com` :&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;| -6 | 2025-05 | chore | Initialisation projet Gradle/JBake | 7/10
|  1 | 2026-04-25 | chore | Migration gouvernance agent | 8/10
|  7 | 2026-04-27 | debug/fix | Correction publishSite | 9/10
|  8 | 2026-04-27 | analyse | Analyse article 0108 | 7/10&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;code&amp;gt;.agents/COMPLETED_TASKS_ARCHIVE_{mois}.adoc&amp;lt;/code&amp;gt;&amp;lt;/strong&amp;gt;&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;Les tâches terminées archivées par mois, pour ne pas surcharger le backlog actif. Quand une user story est terminée, elle migre ici. Le backlog reste lisible : maximum 10 items actifs.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;code&amp;gt;.agents/PROCEDURES.adoc&amp;lt;/code&amp;gt;&amp;lt;/strong&amp;gt;&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;Templates détaillés de la procédure de fin de session. Long, mais lu une seule fois par l&amp;amp;#8217;agent quand il apprend la méthode. Ensuite, la procédure devient mécanique.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;code&amp;gt;.agents/AGENT_MODUS_OPERANDI.adoc&amp;lt;/code&amp;gt;&amp;lt;/strong&amp;gt;&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;La documentation stratégique complète. Sur &amp;lt;code&amp;gt;plantuml-plugin&amp;lt;/code&amp;gt;, ce fichier fait &amp;lt;strong&amp;gt;900+ lignes&amp;lt;/strong&amp;gt; et se nomme en réalité &amp;lt;code&amp;gt;AGENT_METHODOLOGIES.adoc&amp;lt;/code&amp;gt; — j&amp;amp;#8217;ai changé le nom entre l&amp;amp;#8217;écriture de cet article et la mise en place effective. Ce genre de divergence de naming est inévitable dans un système artisanal qui évolue. L&amp;amp;#8217;important est la convention de nommage : si le fichier documente la &amp;lt;strong&amp;gt;méthode&amp;lt;/strong&amp;gt;, il commence par &amp;lt;code&amp;gt;AGENT_&amp;lt;/code&amp;gt; ou un préfixe explicite. Il documente la méthodologie Eager/Lazy, les patterns à suivre, les anti-patterns à éviter. Il est LAZY parce qu&amp;amp;#8217;un agent n&amp;amp;#8217;a pas besoin de relire toute la stratégie à chaque session, seulement quand il y a ambiguïté.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;code&amp;gt;*_REFERENCE.adoc&amp;lt;/code&amp;gt;&amp;lt;/strong&amp;gt;&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;Les références techniques spécifiques au projet. Sur &amp;lt;code&amp;gt;magic-stick&amp;lt;/code&amp;gt;, deux fichiers LAZY denses :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;AB_PARTITION_REFERENCE.adoc&amp;lt;/code&amp;gt; (147 lignes)&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;Architecture partition A/B GPT, scripts &amp;lt;code&amp;gt;update-system.sh&amp;lt;/code&amp;gt;, tailles estimées, mécanisme de rollback&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;BOOT_TEST_REFERENCE.adoc&amp;lt;/code&amp;gt; (144 lignes)&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;Procédure QEMU + VNC pour tester le boot de l&amp;amp;#8217;ISO sans matériel physique, checklist BIOS/UEFI, limitations CI/CD&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sur &amp;lt;code&amp;gt;plantuml-plugin&amp;lt;/code&amp;gt; :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;ARCHITECTURE.adoc&amp;lt;/code&amp;gt; (134 lignes)&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;Structure des 11 data classes, points d&amp;amp;#8217;attention (pièges à éviter), commandes de test optimisées&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;API_KEY_POOL_REFERENCE.adoc&amp;lt;/code&amp;gt;&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;Détails complets du pool de clés (LAZY pendant que &amp;lt;code&amp;gt;ESSENTIALS&amp;lt;/code&amp;gt; est EAGER)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_les_agents_spécialisés_une_équipe_virtuelle&amp;quot;&amp;gt;Les Agents Spécialisés : Une Équipe Virtuelle&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;La gouvernance ne se limite pas à des fichiers passifs. J&amp;amp;#8217;ai formalisé des &amp;lt;strong&amp;gt;rôles d&amp;amp;#8217;agents spécialisés&amp;lt;/strong&amp;gt; dans des fichiers LAZY dédiés, qui définissent le workflow attendu selon le type de tâche.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 6.25%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Agent&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Fichier&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Rôle&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Projet&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;CODER&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;CODER.adoc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Implémentation FTL/CSS/JS, balises sémantiques, critères d&amp;amp;#8217;accessibilité&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;cheroliv.com&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;SCRUM Master&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;SCRUM_MASTER.adoc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Planification US, découpage en sous-tâches, détection des dépendances&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;cheroliv.com&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;PlantUML Designer&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;PLANTUML_DESIGNER.adoc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Création de diagrammes, syntaxe PUML, intégration JBake&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;cheroliv.com&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le fichier &amp;lt;code&amp;gt;CODER.adoc&amp;lt;/code&amp;gt; sur &amp;lt;code&amp;gt;cheroliv.com&amp;lt;/code&amp;gt; contient des règles concrètes : &amp;lt;em&amp;gt;Un seul &amp;lt;code&amp;gt;&amp;amp;lt;h1&amp;amp;gt;&amp;lt;/code&amp;gt; par page&amp;lt;/em&amp;gt;, &amp;lt;em&amp;gt;Préfixer les chemins avec `${content.rootpath}`&amp;lt;/em&amp;gt;, &amp;lt;em&amp;gt;Déclarer la langue `&amp;amp;lt;html lang=&amp;quot;${content.lang!&amp;quot;fr&amp;quot;}&amp;quot;&amp;amp;gt;`&amp;lt;/em&amp;gt;. Ces conventions, écrites une fois, sont respectées automatiquement par l&amp;amp;#8217;agent depuis la session 1.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le fichier &amp;lt;code&amp;gt;SCRUM_MASTER.adoc&amp;lt;/code&amp;gt; impose une structure de livrable : &amp;lt;strong&amp;gt;Objectif&amp;lt;/strong&amp;gt;, &amp;lt;strong&amp;gt;Tâches&amp;lt;/strong&amp;gt; (ordonnées avec assignation), &amp;lt;strong&amp;gt;Critères d&amp;amp;#8217;acceptation&amp;lt;/strong&amp;gt;, &amp;lt;strong&amp;gt;Risques&amp;lt;/strong&amp;gt;. Quand je demande un plan d&amp;amp;#8217;action, l&amp;amp;#8217;agent produit cette structure sans que je l&amp;amp;#8217;aie demandée. La gouvernance &amp;lt;strong&amp;gt;programme&amp;lt;/strong&amp;gt; l&amp;amp;#8217;agent.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_quand_les_agents_spécialisés_deviennent_indispensables&amp;quot;&amp;gt;Quand les Agents Spécialisés Deviennent Indispensables&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;La création des agents spécialisés suit une courbe naturelle. Au début d&amp;amp;#8217;un projet, vous n&amp;amp;#8217;en avez pas besoin — &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt; suffit largement. Mais quand le projet grandit (disons, au-delà de 20 sessions), deux signaux doivent vous alerter :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;L&amp;amp;#8217;agent mélange les conventions de deux domaines distincts (ex: syntaxe PlantUML et règles CSS)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Vous passez plus de temps à corriger l&amp;amp;#8217;agent sur des conventions que vous lui avez déjà expliquées 5 fois&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sur &amp;lt;code&amp;gt;cheroliv.com&amp;lt;/code&amp;gt;, c&amp;amp;#8217;est arrivé à la session&amp;amp;#8230;&amp;amp;#8203; 1. Oui, dès le départ. Parce que ce projet est un site web avec trois langages (FTL, CSS, JS), du contenu AsciiDoc, et des diagrammes PlantUML — trois domaines qui n&amp;amp;#8217;ont rien à voir. L&amp;amp;#8217;agent CODER a besoin de connaître les tailles de police et les media queries ; l&amp;amp;#8217;agent PLANTUML_DESIGNER a besoin de connaître la syntaxe &amp;lt;code&amp;gt;@startuml&amp;lt;/code&amp;gt;. Sans séparation, l&amp;amp;#8217;agent CODER me proposait des diagrammes, et vice versa. Le chaos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sur &amp;lt;code&amp;gt;jhipster-gradle-plugins&amp;lt;/code&amp;gt;, j&amp;amp;#8217;ai créé deux agents spécialisés adaptés au développement de plugins Gradle : &amp;lt;code&amp;gt;PLUGIN_DEVELOPER.adoc&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;BACKLOG_MANAGER.adoc&amp;lt;/code&amp;gt;. Le premier encode toutes les conventions Kotlin/Gradle (pas de &amp;lt;code&amp;gt;!!&amp;lt;/code&amp;gt;, data classes pour les modèles, &amp;lt;code&amp;gt;@TaskAction&amp;lt;/code&amp;gt; pour les tâches). Le second sait que &amp;lt;code&amp;gt;persistence&amp;lt;/code&amp;gt; doit être stable avant que &amp;lt;code&amp;gt;assistant&amp;lt;/code&amp;gt; ne démarre son développement — une dépendance critique dans un mono-repo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;L&amp;amp;#8217;erreur à ne pas commettre : créer trop d&amp;amp;#8217;agents trop tôt. &amp;lt;code&amp;gt;plantuml-plugin&amp;lt;/code&amp;gt; a attendu la session 108 avant de formaliser un agent spécialisé pour le pool de clés API. Avant ça, le contexte métier tenait dans &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt;. La règle empirique : un agent spécialisé se justifie quand son domaine métier dépasse 100 lignes de documentation.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_la_convention_de_nommage_des_sessions&amp;quot;&amp;gt;La Convention de Nommage des Sessions&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Un détail qui paraît futile mais qui devient critique quand on atteint 100 sessions. Comment nommer les fichiers d&amp;amp;#8217;archive ?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;J&amp;amp;#8217;ai appris à mes dépens qu&amp;amp;#8217;une convention est nécessaire — quatre projets, quatre formats différents au début, et je ne m&amp;amp;#8217;y retrouvais plus. Aujourd&amp;amp;#8217;hui, la convention que j&amp;amp;#8217;ai stabilisée est :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code&amp;gt;----
{N}-{type}-{sujet-kebab-case}.adoc
----

Exemples concrets :

* `1-chore-migration-gouvernance-agent.adoc` — session 1, type chore
* `10-solidification-tests.adoc` — session 10, sans type explicite (le sujet suffit)
* `036-debug-graphify-symlink-epic9.adoc` — session 36 avec numéro sur 3 chiffres pour le tri

Le numéro de session est le critère de tri principal. Les projets utilisant des numéros sur 3 chiffres (001, 036, 133) évitent les problèmes de tri lexicographique quand on dépasse 99. C&amp;#39;est ce que j&amp;#39;utilise désormais sur `magic-stick` : `001-init-projet.adoc`, `036-debug-graphify-symlink-epic9.adoc`.

Le type est facultatif et dérivé des mots-clés de la session (debug, feature, refactor, docs, chore, test). Le sujet en kebab-case est la partie la plus importante : il doit permettre de retrouver une session sans ouvrir le fichier. Si vous vous demandez « c&amp;#39;était quelle session où on a corrigé le timeout des tests d&amp;#39;intégration ? », la réponse est `124-fix-timeout-integration-test.adoc`.

Pour les sessions historiques reconstituées (projets nés avant la gouvernance), j&amp;#39;utilise des numéros négatifs. Sur `cheroliv.com`, les sessions -6 à 0 couvrent toute l&amp;#39;histoire pré-gouvernance du projet. Et pour les sessions « non documentées » ou perdues, je crée une entrée dans `SESSIONS_HISTORY.adoc` sans archive correspondante, avec un score `?`. C&amp;#39;est plus honnête que faire semblant.

[plantuml, format=svg, id=diag-naming-convention, alt=&amp;quot;Arbre de décision pour le nommage des fichiers de session&amp;quot;]
----
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam wrapWidth 200

title Convention de Nommage des Sessions

start

:Une session se termine;
note right: Trigger &amp;quot;fin de session&amp;quot;

if (Session antérieure\nà la gouvernance ?) then (oui)
  :Numéro **NÉGATIF**\n-6, -5 ... 0;
  note right: Historique\nreconstitué
  :Suffixe : reconstitution;
else (non)
  :Numéro **POSITIF**\nsur 3 chiffres si &amp;amp;gt; 99;
  note right: 001, 036, 133\npour le tri lexicographique

  :Détecter le **TYPE**;
  if (Mots-clés trouvés ?) then (oui)
    :debug / feature / refactor\ndocs / chore / test;
  else (non)
    :Omettre le type\n(le sujet suffit);
  endif

  :Formuler le **SUJET** en kebab-case;
  note right
    Ex: fix-timeout-integration-test
    Doit permettre de retrouver
    sans ouvrir le fichier
  end note
endif

:Nom final : {N}-{type}-{sujet}.adoc;
note right
  • 1-chore-migration-gouvernance.adoc
  • 036-debug-graphify-symlink.adoc
  • 124-fix-timeout-integration-test.adoc
  • 133-epic11-article-blog-kg.adoc
end note

if (Session documentée ?) then (oui)
  :Créer archive dans sessions/;
  :Ajouter ligne SESSIONS_HISTORY\navec score X/10;
else (non)
  :Ajouter ligne SESSIONS_HISTORY\navec score **?**\n**sans archive**;
  note right: L&amp;#39;honnêteté\nplutôt que le vide
endif

stop
@enduml
----

==== `TEST_COVERAGE_ANALYSIS.adoc` — L&amp;#39;Étape 5 Décortiquée

L&amp;#39;étape 5 de la procédure de fin de session est la plus mystérieuse. Elle dit « Mettre à jour `TEST_COVERAGE_ANALYSIS.adoc` si des tests ont été ajoutés ou modifiés. » Mais à quoi ressemble ce fichier ?

Sur `plantuml-plugin`, il a évolué de quelques lignes à une structure complète. Voici sa forme stabilisée :

[source]
----
= Analyse de Couverture de Tests

== Suivi des Tests
|===
| Classe de test | Type | Tests | Statut | Dernière MAJ
| PlantumlServiceTest | unit | 45/45 | ✅ PASS | 2026-04-23
| ApiKeyPoolTest | integration | 15/15 | ✅ PASS | 2026-04-20
|===

== Historique par Session
| Session | Tests ajoutés | Tests modifiés | Couverture
| 133     | 0             | 2              | 100%
| 132     | 5             | 0              | 100%
|===
----

L&amp;#39;intérêt n&amp;#39;est pas le fichier lui-même — c&amp;#39;est l&amp;#39;obligation de *noter* ce qui a changé. Sans cette étape, après 50 sessions, vous ne savez plus quels tests couvrent quoi. L&amp;#39;agent non plus. Le fichier devient le référentiel unique de vérité sur la couverture de tests du projet.

Pour les projets sans tests traditionnels (comme `magic-stick` qui teste des scripts bash), l&amp;#39;étape 5 est remplacée par `SCRIPT_VERIFICATION.adoc`. Le mécanisme est le même : un fichier qui suit l&amp;#39;état de validation des scripts. Adaptez l&amp;#39;étape 5 à votre projet, mais ne la sautez jamais. Elle est le filet de sécurité qui empêche la régression silencieuse.

Si votre projet n&amp;#39;a *aucun* test — ni unitaire, ni fonctionnel, ni script — créez quand même le fichier vide avec une section « À faire : définir une stratégie de test. » C&amp;#39;est un marque-page qui rappellera à votre futur vous que ce sujet n&amp;#39;est pas traité.

[plantuml, format=svg, id=diag-session-flow, alt=&amp;quot;Flux d&amp;#39;une session type avec Eager/Lazy et agents&amp;quot;]
----
@startuml
skinparam backgroundColor #FEFEFE

start

:Début session;
note right: L&amp;#39;agent est une page blanche

:Chargement EAGER auto;
note right
  * AGENT.adoc (règles absolues)
  * PROMPT_REPRISE.adoc (mission N)
  * INDEX.adoc (état projet)
end note

if (Mission claire ?) then (oui)
  :Exécution directe;
else (non)
  :Charge LAZY sur demande;
  note right
    * SESSIONS_HISTORY.adoc (contexte passé)
    * sessions/{N-1}-*.adoc (décisions)
    * *_REFERENCE.adoc (architechture)
  end note
endif

:Délégation agent spécialisé ?;

if (CODER ?) then (oui)
  :Lit CODER.adoc;
  :Suit conventions FTL/CSS;
elseif (SCRUM Master ?) then (oui)
  :Lit SCRUM_MASTER.adoc;
  :Structure livrable imposée;
elseif (PlantUML ?) then (oui)
  :Lit PLANTUML_DESIGNER.adoc;
  :Syntaxe PUML + intégration;
else (non)
endif

:Travail de la session;

:Fin de session (trigger utilisateur);

:Procédure 6 étapes;
note right
  1. Archive sessions/N-*.adoc
  2. Maj PROMPT_REPRISE.adoc (N+1)
  3. Maj SESSIONS_HISTORY.adoc
  4. Maj INDEX.adoc
  5. Maj TEST_COVERAGE (si applicable)
  6. Maj COMPLETED_TASKS_ARCHIVE.adoc
end note

:Checklist [✅] x 6;

stop
@enduml
----

Le diagramme ci-dessus montre le cycle de vie complet d&amp;#39;une session. Le point clé est la bifurcation après le chargement EAGER : soit la mission est assez claire pour exécuter directement (80% des cas), soit l&amp;#39;agent charge du LAZY pour résoudre une ambiguïté (20% des cas). C&amp;#39;est cette discrimination qui économise les tokens.

== La Procédure de Fin de Session : La Règle d&amp;#39;Or

=== Pourquoi elle est Incontournable

Sans cette procédure, la stratégie Eager/Lazy ne sert à rien. C&amp;#39;est elle qui transforme le travail de la session en information persistante. Elle s&amp;#39;exécute **à la demande explicite de l&amp;#39;utilisateur** (mots-clés : &amp;quot;fin de session&amp;quot;, &amp;quot;je quitte&amp;quot;, etc.), et elle est **obligatoire** -- aucune exception, aucune omission.

Le fichier `SESSION_CHECKLIST.adoc` définit des métriques de session idéale :

* Durée : 15-30 minutes
* Fichiers modifiés : 1-3 maximum
* Échanges LLM : 5-10 messages
* Contexte tokens : &amp;amp;lt; 50k

Et des signes qu&amp;#39;il faut changer de session : _Le LLM répète des erreurs déjà corrigées_, _Plus de 3 fichiers modifiés en parallèle_, _Conversation &amp;amp;gt; 50 messages_. La règle d&amp;#39;or : **Mieux vaut 5 sessions de 20 minutes qu&amp;#39;une session de 2 heures avec debugging chaos.**

=== Le Flux des 6 Étapes (Silencieusement)

[plantuml, format=svg, id=diag-end-session-flow, alt=&amp;quot;Flux de la procédure de fin de session&amp;quot;]
----
@startuml
skinparam defaultTextAlignment center
skinparam wrapWidth 200
skinparam activityBackgroundColor #E3F2FD

start
:L&amp;#39;utilisateur dit &amp;quot;fin de session&amp;quot;;
note right: Mots-clés déclencheurs

:Agent détecte le trigger;

:Étape 1\nCréer archive\n`.agents/sessions/N-*.adoc`;
note right: Tout le contexte de la session

:Étape 2\nMettre à jour\n`PROMPT_REPRISE.adoc`;
note right: Mission N + critères d&amp;#39;acceptation N+1

:Étape 3\nMettre à jour\n`SESSIONS_HISTORY.adoc`;
note right: Ligne récap : # / Date / Type / Sujet / Score

:Étape 4\nMettre à jour\n`INDEX.adoc`;
note right: État courant, roadmap, fichiers modifiés

:Étape 5\nMettre à jour\n`TEST_COVERAGE_ANALYSIS.adoc`;
note right: Si tests ajoutés ou modifiés

:Étape 6\nMettre à jour\n`COMPLETED_TASKS_ARCHIVE.adoc`;
note right: Archiver tâches terminées

:Afficher la checklist de confirmation;
note right: Vérifier que chaque [✅] est mérité

stop
@enduml
----

=== Les Résultats de 150+ Sessions

Voici ce que donne cette procédure appliquée systématiquement sur mes quatre projets :

**plantuml-plugin** :

* **133 sessions** depuis le début du projet
* **240/240 tests PASS** (100% couverture) -- EPICs 1-7 terminées
* **57 scénarios Cucumber BDD** validés
* Règle de sûreté sur les fichiers de config née d&amp;#39;une erreur réelle (Session 2 bakery-plugin)

**bakery-plugin** :

* **11 sessions** en deux semaines
* Migration Supabase → Firebase terminée (9 tests corrigés)
* EPIC 6 (publishProfile) fonctionnel en production
* Règle 0 créée : `publishToMavenLocal` obligatoire après chaque modif

**magic-stick** :

* **23 sessions** pour construire un système live Xubuntu avec partition A/B
* Première ISO générée à la session 10
* Tests de boot QEMU + VNC formalisés (documentation LAZY de 144 lignes)
* CI/CD SourceForge fonctionnel

**cheroliv.com** :

* **9 sessions formelles** + reconstitution de 7 sessions pré-système
* Article 0101 (OpenCode PATH) publié
* Article 0108 (celui-ci) réécrit après analyse de ses lacunes
* Gouvernance complète migrée de Markdown vers AsciiDoc

=== La Checklist Finale

Après exécution silencieuse des 6 étapes, l&amp;#39;agent doit afficher une checklist de confirmation :&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;✅ Procédure de fin de session exécutée
📋 Checklist :
[✅] 1. Archive session N créée
[✅] 2. PROMPT_REPRISE.adoc mis à jour pour session N+1
[✅] 3. SESSIONS_HISTORY.adoc mis à jour
[✅] 4. INDEX.adoc mis à jour
[✅] 5. TEST_COVERAGE_ANALYSIS.adoc mis à jour (si applicable)
[✅] 6. COMPLETED_TASKS_ARCHIVE.adoc mis à jour&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Règle absolue&amp;lt;/strong&amp;gt; : aucune étape ne peut être marquée &amp;lt;code&amp;gt;[✅]&amp;lt;/code&amp;gt; si le fichier n&amp;amp;#8217;a pas été effectivement modifié et vérifié.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_le_guide_de_bootstrap_jour_1_session_0&amp;quot;&amp;gt;Le Guide de Bootstrap : Jour 1, Session 0&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Vous êtes convaincu par la méthode. Vous voulez l&amp;amp;#8217;appliquer à un nouveau projet. Par où commencer ?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;J&amp;amp;#8217;ai vécu ce moment le 28 avril 2026. J&amp;amp;#8217;ouvre Opencode sur &amp;lt;code&amp;gt;jhipster-gradle-plugins&amp;lt;/code&amp;gt;, mon mono-repo de deux plugins Gradle JHipster. C&amp;amp;#8217;est un projet qui existe déjà — le code est là, les tâches Gradle fonctionnent. Mais la gouvernance agent ? Zéro. Page blanche. Comme &amp;lt;code&amp;gt;plantuml-plugin&amp;lt;/code&amp;gt; à sa session 1, il y a des mois.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Voici la procédure exacte que j&amp;amp;#8217;ai suivie, et que je suivrai pour tout nouveau projet. Notez bien l&amp;amp;#8217;ordre — il est important.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-bootstrap&amp;quot; class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-e11aa343b861c2c558099445242ab4be.svg&amp;quot; alt=&amp;quot;Flux de bootstrap en 6 étapes pour initialiser la gouvernance agent sur un nouveau projet&amp;quot; width=&amp;quot;1749&amp;quot; height=&amp;quot;3371&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_étape_0_créer_opencode_json&amp;quot;&amp;gt;Étape 0 : Créer &amp;lt;code&amp;gt;opencode.json&amp;lt;/code&amp;gt;&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;C&amp;amp;#8217;est le premier fichier. Pas &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt;, pas &amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt;. &amp;lt;code&amp;gt;opencode.json&amp;lt;/code&amp;gt; d&amp;amp;#8217;abord, pour une raison simple : si vous créez &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt; en premier, vous allez oublier de créer le pont qui le charge automatiquement. Je l&amp;amp;#8217;ai fait sur &amp;lt;code&amp;gt;bakery-plugin&amp;lt;/code&amp;gt;, je sais de quoi je parle.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_étape_1_créer_les_dossiers&amp;quot;&amp;gt;Étape 1 : Créer les Dossiers&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p .agents/sessions .agents/archives&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Deux dossiers vides. &amp;lt;code&amp;gt;sessions/&amp;lt;/code&amp;gt; recevra les archives de chaque session. &amp;lt;code&amp;gt;archives/&amp;lt;/code&amp;gt; recevra les COMPLETED_TASKS_ARCHIVE mensuels. Ces dossiers doivent exister &amp;lt;strong&amp;gt;avant&amp;lt;/strong&amp;gt; que l&amp;amp;#8217;agent n&amp;amp;#8217;ait besoin d&amp;amp;#8217;écrire dedans. Un agent qui doit créer le dossier ET le fichier en même temps, c&amp;amp;#8217;est un agent qui peut échouer silencieusement.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_étape_2_créer_agent_adoc_le_fichier_maître&amp;quot;&amp;gt;Étape 2 : Créer &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt; — Le Fichier Maître&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Structure minimale pour la première version (elle grandira) :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code&amp;gt;= {NOM_PROJET} — Directives Agent

[CAUTION]&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;ARRÊT OBLIGATOIRE&amp;lt;/strong&amp;gt; avant rm, Write, suppression :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;LIRE le fichier en entier&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;VÉRIFIER git ls-files&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;DEMANDER confirmation&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;ATTENDRE &amp;quot;oui&amp;quot;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_projet&amp;quot;&amp;gt;Projet&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Nom&amp;lt;/strong&amp;gt; : &amp;amp;#8230;&amp;amp;#8203; &amp;lt;strong&amp;gt;Stack&amp;lt;/strong&amp;gt; : &amp;amp;#8230;&amp;amp;#8203; &amp;lt;strong&amp;gt;Documentation&amp;lt;/strong&amp;gt; : AsciiDoc&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_règles_absolues&amp;quot;&amp;gt;Règles Absolues&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_0_environnement_de_dev&amp;quot;&amp;gt;0. ENVIRONNEMENT DE DEV&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Commandes essentielles&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_1_commitsgit&amp;quot;&amp;gt;1. COMMITS/GIT&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Interdiction formelle&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_1b_fichiers_de_configuration_règle_absolue_de_sûreté&amp;quot;&amp;gt;1b. FICHIERS DE CONFIGURATION — RÈGLE ABSOLUE DE SÛRETÉ&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ne jamais écraser&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_2_tests_en_fin_de_session&amp;quot;&amp;gt;2. TESTS EN FIN DE SESSION&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Interdiction formelle&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_3_procédure_de_fin_de_session&amp;quot;&amp;gt;3. PROCÉDURE DE FIN DE SESSION&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Les 6 étapes obligatoires&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_gestion_du_contexte_lazyeager&amp;quot;&amp;gt;Gestion du Contexte — LAZY/EAGER&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Fichiers EAGER / Fichiers LAZY&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ce template minimal permet à l&amp;amp;#8217;agent de démarrer. La version riche — avec la structure du projet, les composants clés, les EPICs, le backlog — viendra à la session 1, quand l&amp;amp;#8217;agent aura déjà les règles de base en main et pourra vous aider à enrichir le document.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_étape_3_créer_prompt_reprise_adoc_la_mission_session_1&amp;quot;&amp;gt;Étape 3 : Créer &amp;lt;code&amp;gt;PROMPT_REPRISE.adoc&amp;lt;/code&amp;gt; — La Mission Session 1&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Un fichier qui dit explicitement : « Ceci est la session 1, mission à définir. » Maximum 70 lignes, avec une section Session 0 (résumé du bootstrap) et une section Session 1 (priorités à définir avec l&amp;amp;#8217;utilisateur).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_étape_4_créer_agentsindex_adoc_le_point_dentrée&amp;quot;&amp;gt;Étape 4 : Créer &amp;lt;code&amp;gt;.agents/INDEX.adoc&amp;lt;/code&amp;gt; — Le Point d&amp;amp;#8217;Entrée&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le fichier qui contiendra les règles absolues (version exécutive) et le tableau des sessions. Pour le bootstrap, il liste les règles 0 à 3 dans leur format concis, le portefeuille de projets (en incluant le nouveau projet avec un emoji 🆕), et la roadmap vide prête à être remplie.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_étape_5_créer_les_fichiers_lazy_structurants&amp;quot;&amp;gt;Étape 5 : Créer les Fichiers LAZY Structurants&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Dans l&amp;amp;#8217;ordre :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;.agents/SESSIONS_HISTORY.adoc&amp;lt;/code&amp;gt; — un tableau avec uniquement la session 0&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;.agents/SESSION_CHECKLIST.adoc&amp;lt;/code&amp;gt; — le template « Quand changer de session »&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;.agents/PROCEDURES.adoc&amp;lt;/code&amp;gt; — la procédure complète des 6 étapes + EAGER/LAZY&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;.agents/AGENT_SESSION_MANAGER.adoc&amp;lt;/code&amp;gt; — le template d&amp;amp;#8217;archive&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;.agents/TEST_COVERAGE_ANALYSIS.adoc&amp;lt;/code&amp;gt; — le tableau de suivi des tests, vide au départ&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Si votre projet a un domaine métier complexe (comme &amp;lt;code&amp;gt;jhipster-gradle-plugins&amp;lt;/code&amp;gt; avec son mono-repo persistence/assistant), créez les agents spécialisés dès maintenant :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;.agents/PLUGIN_DEVELOPER.adoc&amp;lt;/code&amp;gt; — les conventions de code et le boundary de dépendances (si plugin)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;.agents/BACKLOG_MANAGER.adoc&amp;lt;/code&amp;gt; — la structure de planification et les risques identifiés&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ne créez pas d&amp;amp;#8217;agents que vous n&amp;amp;#8217;utiliserez pas. Un agent sans conventions concrètes à encoder est un fichier mort qui pollue &amp;lt;code&amp;gt;.agents/&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_étape_6_ajouter_le_projet_au_portefeuille_de_tous_les_projets&amp;quot;&amp;gt;Étape 6 : Ajouter le Projet au Portefeuille de TOUS les Projets&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;C&amp;amp;#8217;est l&amp;amp;#8217;étape qu&amp;amp;#8217;on oublie systématiquement. Chaque &amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt; de chaque projet contient un tableau « Portefeuille de Projets » qui liste TOUS les projets avec la même méthodologie. Quand vous créez un nouveau projet, vous devez :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ajouter une ligne dans le portefeuille du nouveau projet (logique)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ajouter une ligne dans le portefeuille de TOUS les projets existants — oui, tous&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sur mes quatre (maintenant cinq) projets, cela veut dire ouvrir les &amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt; de &amp;lt;code&amp;gt;magic-stick&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bakery-gradle&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cheroliv.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;plantuml-gradle&amp;lt;/code&amp;gt; et y ajouter la ligne &amp;lt;code&amp;gt;jhipster-gradle-plugins | Session 1 | &amp;amp;#8230;&amp;amp;#8203; | 2026-04-28 🆕&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;C&amp;amp;#8217;est fastidieux. C&amp;amp;#8217;est manuel. C&amp;amp;#8217;est aussi la seule façon de garantir que, quel que soit le projet sur lequel vous travaillez, l&amp;amp;#8217;agent sait quels autres projets existent et quel est leur état. À la session 012 de &amp;lt;code&amp;gt;magic-stick&amp;lt;/code&amp;gt;, l&amp;amp;#8217;agent a détecté deux incohérences dans le portefeuille — &amp;lt;code&amp;gt;bakery-gradle&amp;lt;/code&amp;gt; avait son COMPLETED_TASKS_ARCHIVE en retard, et &amp;lt;code&amp;gt;plantuml-gradle&amp;lt;/code&amp;gt; avait un décalage entre sa documentation de procédure (5 étapes) et son INDEX (6 étapes). Sans ce tableau transverse, ces incohérences seraient restées invisibles.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-portfolio-graph&amp;quot; class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-c2b21ca8d7481b154894427d05899032.svg&amp;quot; alt=&amp;quot;Graphe du portefeuille de projets — références croisées entre INDEX.adoc&amp;quot; width=&amp;quot;1501&amp;quot; height=&amp;quot;802&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Je vous entends d&amp;amp;#8217;ici : « Mais c&amp;amp;#8217;est de la duplication ! » Oui. C&amp;amp;#8217;est de la duplication volontaire. Chaque &amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt; est autonome. Si vous ouvrez une session sur &amp;lt;code&amp;gt;magic-stick&amp;lt;/code&amp;gt;, l&amp;amp;#8217;agent doit savoir que &amp;lt;code&amp;gt;plantuml-gradle&amp;lt;/code&amp;gt; en est à la session 133 sans avoir à ouvrir un fichier dans un autre répertoire. C&amp;amp;#8217;est le prix de l&amp;amp;#8217;indépendance entre projets — et il est de 5 lignes par portefeuille.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_ce_que_le_bootstrap_coûte&amp;quot;&amp;gt;Ce que le Bootstrap Coûte&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Tout ça prend 20 minutes. Vingt minutes une fois, pour ne plus jamais en perdre vingt au début de chaque session. Le ROI est immédiat : dès la session 2, l&amp;amp;#8217;agent reprend là où vous avez laissé la session 1.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sur &amp;lt;code&amp;gt;jhipster-gradle-plugins&amp;lt;/code&amp;gt;, ce bootstrap m&amp;amp;#8217;a pris exactement 18 minutes — de &amp;lt;code&amp;gt;mkdir .agents/&amp;lt;/code&amp;gt; au dernier &amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt; mis à jour dans le portefeuille de &amp;lt;code&amp;gt;magic-stick&amp;lt;/code&amp;gt;. La session 1, qui suivra, démarrera avec un agent qui connaît déjà la structure du mono-repo, les commandes Gradle, et le boundary critique entre &amp;lt;code&amp;gt;persistence/&amp;lt;/code&amp;gt; (lightweight) et &amp;lt;code&amp;gt;assistant/&amp;lt;/code&amp;gt; (lourd). Vingt minutes bien investies.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_les_4_maintenant_5_projets_5_gouvernances_1_méthode&amp;quot;&amp;gt;Les 4 (maintenant 5) Projets, 5 Gouvernances, 1 Méthode&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le système Eager/Lazy n&amp;amp;#8217;est pas un boilerplate copié-collé. Chaque projet a adapté la méthode à ses spécificités.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_cheroliv_com_le_site_personnel&amp;quot;&amp;gt;cheroliv.com : Le Site Personnel&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Spécificité&amp;lt;/strong&amp;gt; : Contenu éditorial, accessibilité WCAG 2.1 AA, JBake statique. &amp;lt;strong&amp;gt;Adaptation&amp;lt;/strong&amp;gt; : Six épopées priorisées (P0-P4), agents spécialisés (CODER, SCRUM_MASTER, PLANTUML_DESIGNER), critères d&amp;amp;#8217;acceptation transverses sur l&amp;amp;#8217;accessibilité. &amp;lt;strong&amp;gt;Règle emblématique&amp;lt;/strong&amp;gt; : &amp;lt;code&amp;gt;./gradlew serve&amp;lt;/code&amp;gt; obligatoire, interdiction formelle du script &amp;lt;code&amp;gt;jbake.sh&amp;lt;/code&amp;gt; externe.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_bakery_gradle_le_plugin_multi_purpose&amp;quot;&amp;gt;bakery-gradle : Le Plugin Multi-Purpose&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Spécificité&amp;lt;/strong&amp;gt; : Plugin Gradle avec tâches de publication, génération de code Firebase, parsing YAML. &amp;lt;strong&amp;gt;Adaptation&amp;lt;/strong&amp;gt; : Règle 0 (publishToMavenLocal), règle de sûreté sur les fichiers de config renforcée par l&amp;amp;#8217;incident du &amp;lt;code&amp;gt;site.yml&amp;lt;/code&amp;gt;, EPIC 9 et 10 sur la configuration multi-fichiers. &amp;lt;strong&amp;gt;Règle emblématique&amp;lt;/strong&amp;gt; : &amp;lt;code&amp;gt;publishToMavenLocal&amp;lt;/code&amp;gt; après chaque modification du code source du plugin.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_plantuml_gradle_le_plugin_ia&amp;quot;&amp;gt;plantuml-gradle : Le Plugin IA&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Spécificité&amp;lt;/strong&amp;gt; : Intégration LangChain4j, génération de diagrammes PlantUML via LLM, tests avec clés API cloud. &amp;lt;strong&amp;gt;Adaptation&amp;lt;/strong&amp;gt; : Documentation LAZY dense (ARCHITECTURE, API_KEY_POOL_REFERENCE, DATASET_FINETUNING), EPICs numérotés (1-11 terminés, 107+ en cours), séparation EAGER/LAZY optimisée (gain de 60% de tokens). &amp;lt;strong&amp;gt;Règle emblématique&amp;lt;/strong&amp;gt; : Fichiers &amp;lt;code&amp;gt;*_ESSENTIALS.adoc&amp;lt;/code&amp;gt; (50 lignes) vs &amp;lt;code&amp;gt;*_REFERENCE.adoc&amp;lt;/code&amp;gt; (illimité) pour le pool de clés API.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_magic_stick_le_système_live&amp;quot;&amp;gt;magic-stick : Le Système Live&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Spécificité&amp;lt;/strong&amp;gt; : Build d&amp;amp;#8217;ISO Linux live (Xubuntu), partitionnement GPT A/B, CI/CD avec SourceForge. &amp;lt;strong&amp;gt;Adaptation&amp;lt;/strong&amp;gt; : Références techniques LAZY complexes (AB_PARTITION, BOOT_TEST), SCRIPT_VERIFICATION.adoc comme étape 5 spécifique (pas de tests traditionnels, mais vérification de scripts bash). &amp;lt;strong&amp;gt;Règle emblématique&amp;lt;/strong&amp;gt; : &amp;lt;code&amp;gt;isoTestAB&amp;lt;/code&amp;gt; skipé en CI (runners non privilégiés), tests réservés aux environnements locaux avec Docker &amp;lt;code&amp;gt;--privileged&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_règles_absolues_transverses&amp;quot;&amp;gt;Règles Absolues Transverses&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quelques règles critiques qui apparaissent dans le fichier &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt; Eager et s&amp;amp;#8217;appliquent à toutes les sessions sur tous les projets :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Environnement de dev&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;✅ Utiliser la commande propre au projet (&amp;lt;code&amp;gt;./gradlew serve&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;./gradlew functionalTest&amp;lt;/code&amp;gt;, etc.)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;❌ Interdit d&amp;amp;#8217;utiliser des scripts externes non versionnés&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Git&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;✅ Lecture seule autorisée (&amp;lt;code&amp;gt;git status&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;git diff&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;git log&amp;lt;/code&amp;gt;)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;❌ &amp;lt;code&amp;gt;git add&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;git commit&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;git push&amp;lt;/code&amp;gt; interdits sans permission explicite&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;❌ &amp;lt;code&amp;gt;git commit --amend&amp;lt;/code&amp;gt; interdit sauf conditions méticuleusement respectées&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fichiers de Configuration&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;❌ Ne jamais écraser un fichier config avec un &amp;lt;code&amp;gt;Write&amp;lt;/code&amp;gt; complet quand un &amp;lt;code&amp;gt;Edit&amp;lt;/code&amp;gt; partiel suffit&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;❌ Ne jamais remplacer des valeurs sensibles par des valeurs factices&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;❌ Vérifier &amp;lt;code&amp;gt;git check-ignore&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;git ls-files&amp;lt;/code&amp;gt; avant toute modification&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Tests&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;❌ Ne jamais lancer de tests en procédure de fin de session sans permission explicite&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Outils Interdits&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;❌ &amp;lt;code&amp;gt;TodoWrite&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;Task&amp;lt;/code&amp;gt;&amp;amp;#8201;&amp;amp;#8212;&amp;amp;#8201;la gouvernance se fait via les fichiers AsciiDoc, pas via des outils externes&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_les_anti_patterns_ce_que_150_sessions_mont_appris&amp;quot;&amp;gt;Les Anti-Patterns : Ce Que 150+ Sessions M&amp;amp;#8217;ont Appris&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Avec le recul, certaines erreurs sont devenues des patterns tellement prévisibles que je peux les lister comme des anti-patterns — des pièges dans lesquels tout nouveau projet tombera probablement. Les voici, classés par ordre de fréquence.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-antipatterns&amp;quot; class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-c4b4c7d8bfdda0f1287bc4f14752a381.svg&amp;quot; alt=&amp;quot;Les 7 anti-patterns de la gouvernance agent — arbre des pièges à éviter&amp;quot; width=&amp;quot;1783&amp;quot; height=&amp;quot;5600&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_anti_pattern_1_lagent_sans_opencode_json&amp;quot;&amp;gt;Anti-Pattern 1 : L&amp;amp;#8217;Agent sans &amp;lt;code&amp;gt;opencode.json&amp;lt;/code&amp;gt;&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;C&amp;amp;#8217;est l&amp;amp;#8217;erreur numéro un. Vous avez écrit un magnifique &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt; de 200 lignes, mais il n&amp;amp;#8217;est jamais chargé automatiquement. L&amp;amp;#8217;agent démarre nu. Vous passez 20 minutes à le recontextualiser — exactement ce que la méthode est censée éviter.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Symptôme&amp;lt;/strong&amp;gt; : L&amp;amp;#8217;agent pose des questions basiques sur la structure du projet alors que tout est dans &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt;. &amp;lt;strong&amp;gt;Correction&amp;lt;/strong&amp;gt; : Créer &amp;lt;code&amp;gt;opencode.json&amp;lt;/code&amp;gt; avec le bloc &amp;lt;code&amp;gt;instructions: [&amp;quot;AGENT.adoc&amp;quot;]&amp;lt;/code&amp;gt;. Vérifier que le fichier existe dans le working directory d&amp;amp;#8217;Opencode.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_anti_pattern_2_trop_dagents_trop_tôt&amp;quot;&amp;gt;Anti-Pattern 2 : Trop d&amp;amp;#8217;Agents Trop Tôt&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;À la session 2 de votre projet, vous créez 5 agents spécialisés. Aucun n&amp;amp;#8217;a de contenu réel, ce sont tous des stubs. Résultat : &amp;lt;code&amp;gt;.agents/&amp;lt;/code&amp;gt; est pollué de fichiers vides, et l&amp;amp;#8217;agent hésite sur lequel charger.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Symptôme&amp;lt;/strong&amp;gt; : Des fichiers d&amp;amp;#8217;agent contenant uniquement « À définir » ou une structure vide de 10 lignes. &amp;lt;strong&amp;gt;Correction&amp;lt;/strong&amp;gt; : Un agent spécialisé se justifie quand son domaine métier dépasse 100 lignes ou qu&amp;amp;#8217;il encode au moins 5 règles concrètes. En dessous, le contenu tient dans &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_anti_pattern_3_le_prompt_reprise_fantôme&amp;quot;&amp;gt;Anti-Pattern 3 : Le PROMPT_REPRISE Fantôme&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;PROMPT_REPRISE.adoc&amp;lt;/code&amp;gt; est mis à jour&amp;amp;#8230;&amp;amp;#8203; mais uniquement à la fin de la session, quand l&amp;amp;#8217;agent exécute la procédure. Entre-temps, la mission réelle de la session a dérivé — on a corrigé un bug imprévu au lieu d&amp;amp;#8217;implémenter la feature planifiée. Le &amp;lt;code&amp;gt;PROMPT_REPRISE.adoc&amp;lt;/code&amp;gt; de fin de session ne reflète pas ce qui s&amp;amp;#8217;est &amp;lt;strong&amp;gt;vraiment&amp;lt;/strong&amp;gt; passé.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Symptôme&amp;lt;/strong&amp;gt; : La section « Priorités » de &amp;lt;code&amp;gt;PROMPT_REPRISE.adoc&amp;lt;/code&amp;gt; mentionne une feature qui n&amp;amp;#8217;a pas été touchée, alors que vous avez passé la session sur un bug critique. &amp;lt;strong&amp;gt;Correction&amp;lt;/strong&amp;gt; : Mettre à jour &amp;lt;code&amp;gt;PROMPT_REPRISE.adoc&amp;lt;/code&amp;gt; en temps réel quand la session dérive de sa mission initiale. Ne pas attendre la procédure de fin de session pour documenter le changement de cap. 30 secondes d&amp;amp;#8217;édition pendant la session valent mieux qu&amp;amp;#8217;un &amp;lt;code&amp;gt;PROMPT_REPRISE&amp;lt;/code&amp;gt; mensonger.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_anti_pattern_4_larchive_vide&amp;quot;&amp;gt;Anti-Pattern 4 : L&amp;amp;#8217;Archive Vide&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;La procédure de fin de session crée l&amp;amp;#8217;archive&amp;amp;#8230;&amp;amp;#8203; mais l&amp;amp;#8217;agent, pressé d&amp;amp;#8217;afficher sa checklist, remplit le template avec des placeholders. &amp;lt;code&amp;gt;{detail}&amp;lt;/code&amp;gt; devient « Détail à compléter plus tard ». Ce « plus tard » n&amp;amp;#8217;arrive jamais.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Symptôme&amp;lt;/strong&amp;gt; : Des archives de session contenant les mots « TODO », « à compléter », ou des sections vides. &amp;lt;strong&amp;gt;Correction&amp;lt;/strong&amp;gt; : La règle absolue dit qu&amp;amp;#8217;aucune étape ne peut être marquée [✅] si le fichier n&amp;amp;#8217;a pas été effectivement modifié. Appliquez-la à vous-même : relisez l&amp;amp;#8217;archive avant d&amp;amp;#8217;afficher la checklist. Si une section est vide, remplissez-la maintenant.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_anti_pattern_5_le_portefeuille_fantôme&amp;quot;&amp;gt;Anti-Pattern 5 : Le Portefeuille Fantôme&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Vous créez un nouveau projet avec une gouvernance parfaite. Mais vous oubliez de l&amp;amp;#8217;ajouter au portefeuille des autres projets. Trois semaines plus tard, vous ouvrez une session sur &amp;lt;code&amp;gt;magic-stick&amp;lt;/code&amp;gt; et l&amp;amp;#8217;agent vous dit que vous avez 4 projets. Vous en avez 5. L&amp;amp;#8217;agent ne le sait pas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Symptôme&amp;lt;/strong&amp;gt; : Le comptage des projets dans les portefeuilles ne correspond pas à la réalité. &amp;lt;strong&amp;gt;Correction&amp;lt;/strong&amp;gt; : Scriptez la mise à jour du portefeuille, ou au minimum maintenez une checklist post-bootstrap qui inclut « Mettre à jour le portefeuille de tous les projets existants ». C&amp;amp;#8217;est l&amp;amp;#8217;étape la plus fastidieuse de la méthode, et donc la plus souvent omise.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_anti_pattern_6_la_redondance_cachée&amp;quot;&amp;gt;Anti-Pattern 6 : La Redondance Cachée&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Vous copiez-collez le même backlog de 50 lignes dans &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt;. À la session suivante, vous mettez à jour l&amp;amp;#8217;un mais pas l&amp;amp;#8217;autre. Vos deux fichiers EAGER se contredisent.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Symptôme&amp;lt;/strong&amp;gt; : Des divergences entre les EPICs listées dans &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt; et celles dans &amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt;. &amp;lt;strong&amp;gt;Correction&amp;lt;/strong&amp;gt; : La redondance doit être &amp;lt;strong&amp;gt;assumée et bornée&amp;lt;/strong&amp;gt;. &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt; contient le backlog détaillé avec user stories. &amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt; contient UNIQUEMENT le tableau récapitulatif des EPICs (nom, progression, priorité). Si les deux divergent, c&amp;amp;#8217;est &amp;lt;code&amp;gt;INDEX.adoc&amp;lt;/code&amp;gt; qui fait foi — parce que c&amp;amp;#8217;est lui qui est mis à jour à chaque fin de session. Documentez cette règle dans votre &amp;lt;code&amp;gt;AGENT.adoc&amp;lt;/code&amp;gt; : « En cas de divergence entre AGENT.adoc et INDEX.adoc, INDEX.adoc est la source de vérité. »&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_anti_pattern_7_la_session_trop_longue&amp;quot;&amp;gt;Anti-Pattern 7 : La Session Trop Longue&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Vous ignorez les signaux du &amp;lt;code&amp;gt;SESSION_CHECKLIST.adoc&amp;lt;/code&amp;gt;. 2 heures, 85 messages, 12 fichiers modifiés. L&amp;amp;#8217;agent tourne en boucle, vous aussi. Vous auriez dû changer de session il y a 45 minutes.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Symptôme&amp;lt;/strong&amp;gt; : Le LLM répète des erreurs déjà corrigées, le contexte tokens dépasse 80k, vous avez modifié à la fois &amp;lt;code&amp;gt;persistence/&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;assistant/&amp;lt;/code&amp;gt; dans la même session. &amp;lt;strong&amp;gt;Correction&amp;lt;/strong&amp;gt; : Le &amp;lt;code&amp;gt;SESSION_CHECKLIST.adoc&amp;lt;/code&amp;gt; existe pour une raison. Respectez-le. La règle d&amp;amp;#8217;or n&amp;amp;#8217;est pas une suggestion. 5 sessions de 20 minutes &amp;amp;gt; 1 session de 2 heures.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_pourquoi_asciidoc&amp;quot;&amp;gt;Pourquoi AsciiDoc ?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;J&amp;amp;#8217;ai choisi AsciiDoc pour plusieurs raisons pragmatiques qui ont un impact direct sur l&amp;amp;#8217;efficacité de la gouvernance :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Structure sémantique riche&amp;lt;/strong&amp;gt; : sections, tableaux, admonitions, attributs de document. Un agent lit mieux un document structuré qu&amp;amp;#8217;un fond de texte.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Attributs de document&amp;lt;/strong&amp;gt; (&amp;lt;code&amp;gt;:jbake-type:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;:jbake-status:&amp;lt;/code&amp;gt;) qui permettent de définir des métadonnées lisibles par la machine et l&amp;amp;#8217;humain.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Compatibilité avec JBake&amp;lt;/strong&amp;gt; : mon site statique est généré par JBake, qui traite nativement l&amp;amp;#8217;AsciiDoc. Les fichiers de gouvernance et les articles de blog partagent le même format. Mon article de blog &amp;lt;strong&amp;gt;sur&amp;lt;/strong&amp;gt; la gouvernance est écrit dans le même format que la gouvernance elle-même.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Longévité&amp;lt;/strong&amp;gt; : AsciiDoc est un format de documentation technique éprouvé, avec une spécification stable.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_résultat_une_continuité_sans_couture&amp;quot;&amp;gt;Résultat : Une Continuité sans Couture&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Depuis que j&amp;amp;#8217;ai mis ce système en place, passer d&amp;amp;#8217;une session Opencode à l&amp;amp;#8217;autre est devenu fluide. L&amp;amp;#8217;agent reprend exactement où je l&amp;amp;#8217;ai laissé, sans que j&amp;amp;#8217;aie à recontextualiser quoi que ce soit.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Les décisions prises à la session 3 sont accessibles à la session 133. Le backlog reste cohérent sur cinq projets simultanés. Les règles de sécurité (notamment sur git et les fichiers de config) sont respectées parce qu&amp;amp;#8217;elles sont omniprésentes dans le fichier Eager.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Les chiffres parlent :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;150+ sessions archivées&amp;lt;/strong&amp;gt; au total&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;60% de tokens EAGER économisés&amp;lt;/strong&amp;gt; après optimisation (Session 109)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Zéro rappel nécessaire&amp;lt;/strong&amp;gt; sur le contexte métier du pool de clés API&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;100% des tests PASS&amp;lt;/strong&amp;gt; sur plantuml-plugin (240/240)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Une seule catastrophe&amp;lt;/strong&amp;gt; (Session 2 bakery-plugin) qui a donné naissance à la règle de sûreté absolue&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;C&amp;amp;#8217;est un &amp;lt;strong&amp;gt;système artisanal&amp;lt;/strong&amp;gt;, construit avec les outils du métier (AsciiDoc, git, Gradle), sans dépendance à une plateforme propriétaire. Il reflète ma conviction fondamentale : &amp;lt;strong&amp;gt;un développeur crédible construit et utilise ses propres outils&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_liens&amp;quot;&amp;gt;Liens&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Mon site : [cheroliv.com](&amp;lt;a href=&amp;quot;https://cheroliv.com&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://cheroliv.com&amp;lt;/a&amp;gt;)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Le projet &amp;lt;code&amp;gt;bakery-gradle&amp;lt;/code&amp;gt; : [github.com/cheroliv/bakery-gradle-plugin](&amp;lt;a href=&amp;quot;https://github.com/cheroliv/bakery-gradle-plugin&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://github.com/cheroliv/bakery-gradle-plugin&amp;lt;/a&amp;gt;)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Le projet &amp;lt;code&amp;gt;plantuml-gradle&amp;lt;/code&amp;gt; : [github.com/cheroliv/plantuml-gradle-plugin](&amp;lt;a href=&amp;quot;https://github.com/cheroliv/plantuml-gradle-plugin&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://github.com/cheroliv/plantuml-gradle-plugin&amp;lt;/a&amp;gt;)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Le projet &amp;lt;code&amp;gt;magic-stick&amp;lt;/code&amp;gt; : [github.com/cheroliv/magic-stick](&amp;lt;a href=&amp;quot;https://github.com/cheroliv/magic-stick&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://github.com/cheroliv/magic-stick&amp;lt;/a&amp;gt;)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Le projet &amp;lt;code&amp;gt;cheroliv.com&amp;lt;/code&amp;gt; : [github.com/cheroliv/cheroliv.github.io](&amp;lt;a href=&amp;quot;https://github.com/cheroliv/cheroliv.github.io&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://github.com/cheroliv/cheroliv.github.io&amp;lt;/a&amp;gt;)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Le code qu&amp;amp;#8217;on écrit pour soi est le laboratoire du code qu&amp;amp;#8217;on enseigne aux autres.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Por que `serve` exibía um site feio enquanto `publishSite` era perfeito</title>
            <link >https://cheroliv.com//blog/2026/0107_jbake_serve_rendering_bug_fix_post.html</link>
            <pubDate>Wed, 22 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0107_jbake_serve_rendering_bug_fix_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_cena_quatro_screenshots_dois_renders&amp;quot;&amp;gt;A cena: quatro screenshots, dois renders&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_renderização_local&amp;quot;&amp;gt;Renderização local&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_renderizado_online&amp;quot;&amp;gt;Renderizado online&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_diagnóstico_serve_não_apontava_para_a_pasta_correta&amp;quot;&amp;gt;O diagnóstico: &amp;lt;code&amp;gt;serve&amp;lt;/code&amp;gt; não apontava para a pasta correta&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_causa_raiz_o_cli_do_jbake_não_funciona_assim&amp;quot;&amp;gt;A causa raiz: O CLI do JBake não funciona assim&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_argumentos_posicionais_não_flags&amp;quot;&amp;gt;A solução: argumentos posicionais, não flags&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_verificação_rápida_com_curl&amp;quot;&amp;gt;Verificação rápida com curl&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_esse_bug_era_sutil&amp;quot;&amp;gt;Por que esse bug era sutil&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 10 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Você conhece aquele momento em que o seu site local parece nada, mas uma vez colocado online tudo fica impecável? Isso aconteceu comigo com meu plugin Gradle JBake. A tarefa`serve`exibia botões quadrados, texto escuro sobre fundo preto, e um CSS estranhamente ausente. Porém`publishSite`gerava o mesmo site, ele, impecável. O culpado não era nem o CSS, nem o navegador, nem o cache. Era uma linha de código Kotlin — e um mau hábito com os argumentos na linha de comando.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;tic&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_cena_quatro_screenshots_dois_renders&amp;quot;&amp;gt;A cena: quatro screenshots, dois renders&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Era depois de uma falha do sistema. Eu tinha tirado quatro capturas de tela para comparar o renderizado local (&amp;lt;code&amp;gt;./gradlew serve`sobre`localhost:8820&amp;lt;/code&amp;gt;) com a renderização online (`publishSite`no GitHub Pages). Dois screenshots por página: topo e base.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_renderização_local&amp;quot;&amp;gt;Renderização local&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Topo da página&amp;lt;/strong&amp;gt;— Os botões &amp;lt;strong&amp;gt;Project&amp;lt;/strong&amp;gt; e &amp;lt;strong&amp;gt;Template&amp;lt;/strong&amp;gt; são&amp;lt;strong&amp;gt;pequenos, retangulares&amp;lt;/strong&amp;gt;, nas cores padrão (azul e verde). O cartão &amp;lt;strong&amp;gt;Start Writing&amp;lt;/strong&amp;gt; tem um&amp;lt;strong&amp;gt;fundo preto uniforme&amp;lt;/strong&amp;gt;, sem moldura.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/screen_local1.png&amp;quot; alt=&amp;quot;Rendu local - haut de page&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Rodapé&amp;lt;/strong&amp;gt;— O subtítulo &amp;lt;strong&amp;gt;&amp;quot;Últimos artigos e recursos&amp;lt;/strong&amp;gt;&amp;quot; é&amp;lt;strong&amp;gt;quase ilegível&amp;lt;/strong&amp;gt;, muito escuro sobre fundo preto. As datas sob as imagens do blog (&amp;quot;17 October 2013&amp;quot;, etc.) são&amp;lt;strong&amp;gt;ausentes&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/screen_local2.png&amp;quot; alt=&amp;quot;Rendu local - bas de page&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_renderizado_online&amp;quot;&amp;gt;Renderizado online&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Topo da página&amp;lt;/strong&amp;gt;— Os mesmos botões são&amp;lt;strong&amp;gt;grandes, ovinos, verde neon flashy&amp;lt;/strong&amp;gt;, texto em maiúsculas. O cartão tem um&amp;lt;strong&amp;gt;quadro branco arredondado com sombra projetada&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/screen_online1.png&amp;quot; alt=&amp;quot;Rendu en ligne - haut de page&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;rodapé&amp;lt;/strong&amp;gt;— O subtítulo é&amp;lt;strong&amp;gt;bem legível&amp;lt;/strong&amp;gt;. As datas são&amp;lt;strong&amp;gt;visíveis&amp;lt;/strong&amp;gt;. Os cartões dos artigos têm&amp;lt;strong&amp;gt;cantos arredondados&amp;lt;/strong&amp;gt;e os títulos são&amp;lt;strong&amp;gt;em negrito&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/screen_online2.png&amp;quot; alt=&amp;quot;Rendu en ligne - bas de page&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Primeiro reflexo: o tema. Utilizo um sistema de tema dinâmico (claro, escuro, alto contraste) baseado em`localStorage`. Talvez o local store desencadeava um tema high-contrast? Não. Já havia escritohttps://cheroliv.com/blog/2025/0098_preloading_css_variable_post.html[um artigo inteiro sobre o pré-carregamento dos temas], eu sei como isso funciona. E então, mesmo em modo high-contrast, a estrutura CSS (botões ovais, sombras) devia estar lá. Ela não estava lá.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Segundo reflexo: o cache do navegador. Não, eu tinha feito o teste com perfis limpos. O diff persistia.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Terceiro reflexo:`serve`não servia os mesmos arquivos que`publishSite`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_diagnóstico_serve_não_apontava_para_a_pasta_correta&amp;quot;&amp;gt;O diagnóstico: &amp;lt;code&amp;gt;serve&amp;lt;/code&amp;gt; não apontava para a pasta correta&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Meu plugin Gradle`bakery`tem duas tarefas principais:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;bake&amp;lt;/code&amp;gt;: gera o site estático em`build/bake/`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;serve&amp;lt;/code&amp;gt;: inicia um servidor web local&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;publishSite&amp;lt;/code&amp;gt;: empurra`build/bake/`para GitHub Pages&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A divergência só poderia vir de`serve`. Si &amp;lt;code&amp;gt;publishSite`publicava o conteúdo correto, é que`build/bake/`estava bom. Se`serve`exibia outra coisa, o que significa que não servia`build/bake/&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Vamos olhar o código. Em`SiteManager.kt`, a tarefa`serve`é definida assim :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;SiteManager.kt — La tâche serve (version buggy)&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;tasks.register(&amp;quot;serve&amp;quot;, JavaExec::class.java) { task -&amp;amp;gt;
    task.apply {
        mainClass.set(&amp;quot;org.jbake.launcher.Main&amp;quot;)
        classpath = jbakeRuntime
        environment(&amp;quot;GEM_PATH&amp;quot;, jbakeRuntime.asPath)
        jvmArgs(/* ... */)
        args = listOf(
            &amp;quot;-b&amp;quot;, file(site.bake.srcPath).absolutePath,
            &amp;quot;-s&amp;quot;, layout.buildDirectory.get()
                .asFile.resolve(site.bake.destDirPath)
                .absolutePath
        )
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;org.jbake.launcher.Main`é o ponto de entrada da CLI JBake 2.7.0. A ideia: passar&amp;lt;/code&amp;gt;-b`para a pasta de origem e`-s`para a pasta de destino, então iniciar o modo servidor. Mas&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_causa_raiz_o_cli_do_jbake_não_funciona_assim&amp;quot;&amp;gt;A causa raiz: O CLI do JBake não funciona assim&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;JBake 2.7.0 aguarda alguns&amp;lt;strong&amp;gt;argumentos posicionais&amp;lt;/strong&amp;gt;para a fonte e o destino, seguido de flags opcionais. A assinatura é :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;jbake &amp;amp;lt;source&amp;amp;gt; &amp;amp;lt;destination&amp;amp;gt; [options]&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas no meu código, eu escrevia :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;args = listOf(&amp;quot;-b&amp;quot;, &amp;quot;/path/to/site&amp;quot;, &amp;quot;-s&amp;quot;, &amp;quot;/path/to/build/bake&amp;quot;)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;JBake interpretava isso :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;-b&amp;lt;/code&amp;gt;→ analisar o sinalizador \&amp;quot;bake\&amp;quot; (booleano), consumido&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;/path/to/site&amp;lt;/code&amp;gt;→ torna-se argumento posicional 1 (fonte)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;-s&amp;lt;/code&amp;gt;→ analisar o opção &amp;quot;serve&amp;quot; (booleano), servidor iniciado&amp;lt;strong&amp;gt;imediatamente&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;/path/to/build/bake&amp;lt;/code&amp;gt;→ torna-se um argumento órfão, ignorado ou mal interpretado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Resultado: JBake estava tomando`/path/to/site`como fonte,&amp;lt;strong&amp;gt;não levava em conta o destino&amp;lt;/strong&amp;gt;fornecida (&amp;lt;code&amp;gt;build/bake&amp;lt;/code&amp;gt;), e utilizava seu diretório padrão (frequentemente`./output`ou uma cópia temporária). O arquivo`css/styles.css`o build era portanto sobrescrito ou ignorado, e é um velho CSS padrão (sem as variáveis personalizadas, sem os arredondamentos, sem as sombras) que estava sendo servido.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em comparação,&amp;lt;code&amp;gt;bake&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;publishSite`utilizam o&amp;lt;strong&amp;gt;plugin Gradle JBake oficial&amp;lt;/strong&amp;gt;(`jbake-gradle-plugin&amp;lt;/code&amp;gt;) que escreve bem em`build/bake/`via a API Gradle. Eles não passam pela CLI.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_argumentos_posicionais_não_flags&amp;quot;&amp;gt;A solução: argumentos posicionais, não flags&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A correção é trivial — uma vez que se sabe. Basta passar a fonte e o destino como argumentos posicionais, depois`-s`por último:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;SiteManager.kt — La tâche serve (version corrigée)&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;args = listOf(
    file(site.bake.srcPath).absolutePath,
    layout.buildDirectory.get()
        .asFile.resolve(site.bake.destDirPath)
        .absolutePath,
    &amp;quot;-s&amp;quot;
)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É isso. Três linhas alteradas, bug resolvido.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Após publicação local do plugin (&amp;lt;code&amp;gt;publishToMavenLocal&amp;lt;/code&amp;gt;), un `./gradlew serve`lança JBake com a sintaxe correta :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;jbake /home/user/project/site /home/user/project/build/bake -s&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E desta vez, o servidor Jetty integrado ao JBake serve bem o conteúdo de`build/bake/`— idêntico ao que é publicado online.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/serve-vs-publish-diagram.svg&amp;quot; alt=&amp;quot;serve vs publish diagram&amp;quot; width=&amp;quot;1763&amp;quot; height=&amp;quot;5484&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_verificação_rápida_com_curl&amp;quot;&amp;gt;Verificação rápida com curl&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para garantir que o servidor esteja servindo o conteúdo correto, um simples`curl`confirma que`index.html`contém`data-bs-theme=&amp;quot;light&amp;quot;`e que`build/bake/css/styles.css`pesa bem 31 402 bytes — idêntico ao arquivo gerado por`bake`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ ./gradlew serve
# Dans un autre terminal :
$ curl -s http://localhost:8820/ | grep data-bs-theme
&amp;amp;lt;html ... data-bs-theme=&amp;quot;light&amp;quot; ...&amp;amp;gt;

$ curl -I http://localhost:8820/css/styles.css
HTTP/1.1 200 OK
Content-Length: 31402
Content-Type: text/css&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O render local agora&amp;lt;strong&amp;gt;pixel-idêntico&amp;lt;/strong&amp;gt;ao renderizado implantado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_esse_bug_era_sutil&amp;quot;&amp;gt;Por que esse bug era sutil&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você vê por que era difícil de pegar?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Nenhum erro explícito&amp;lt;/strong&amp;gt;JBake não travava. Ele &amp;quot;funcionava&amp;quot;, simplesmente com uma pasta errada.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O build funcionava&amp;lt;/strong&amp;gt;:`./gradlew bake`gerava corretamente os arquivos em`build/bake/`.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O despliegue estava funcionando&amp;lt;/strong&amp;gt;:`publishSite`empurrava o bom conteúdo online&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Só &amp;lt;code&amp;gt;serve&amp;lt;/code&amp;gt; estava quebrado&amp;lt;/strong&amp;gt;: a tarefa de desenvolvimento, aquela que usamos constantemente para iterar.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O hábito de &amp;lt;code&amp;gt;-key value&amp;lt;/strong&amp;gt;: como desenvolvedor, somos condicionados por anos de CLI Unix (&amp;lt;/code&amp;gt;-o output`, &amp;lt;code&amp;gt;-i input&amp;lt;/code&amp;gt;). O CLI do JBake é uma exceção — a fonte e o destino são posicionais.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se você mantém um plugin Gradle que envolve JBake (ou qualquer ferramenta CLI),&amp;lt;strong&amp;gt;leia a documentação dos argumentos&amp;lt;/strong&amp;gt;mesmo se você pensa que os conhece. Uma hipótese implícita (&amp;lt;code&amp;gt;-s&amp;lt;/code&amp;gt; = &amp;quot;set destination&amp;quot;) pode custar horas de depuração visual.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui, o fix era literalmente para mudar:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;args = listOf(&amp;quot;-b&amp;quot;, src, &amp;quot;-s&amp;quot;, dest)   // ❌ BUG&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;en :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;args = listOf(src, dest, &amp;quot;-s&amp;quot;)         // ✅ FIX&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Três tokens deslocados, e meu site local volta a ficar tão bonito quanto o site em produção.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Lição&amp;lt;/strong&amp;gt;: quando a renderização difere entre local e produção sem razão óbvia, suspeite primeiro do &amp;lt;strong&amp;gt;pipeline&amp;lt;/strong&amp;gt; — não o CSS, não o navegador, e muito menos o framework. Frequentemente é a etapa logo antes da renderização que está trapaceando.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>OpenCode e o PATH incompleto : por que suas ferramentas não estavam no shell do agente</title>
            <link >https://cheroliv.com//blog/2026/0106_opencode_path_sdkman_nvm_post.html</link>
            <pubDate>Tue, 21 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0106_opencode_path_sdkman_nvm_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_cena_um_agente_cego_num_mundo_de_ferramentas&amp;quot;&amp;gt;A cena: um agente cego num mundo de ferramentas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_anatomia_do_bug_dois_shells_dois_mundos&amp;quot;&amp;gt;Anatomia do bug: dois shells, dois mundos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_os_culpados_zshrc_vs_zshenv&amp;quot;&amp;gt;Os culpados: .zshrc vs .zshenv&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagnóstico_passo_a_passo&amp;quot;&amp;gt;Diagnóstico passo a passo&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_verificar_o_path_do_agente&amp;quot;&amp;gt;Verificar o PATH do agente&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_2_identifique_o_que_está_em_zshrc_mas_não_está_em_zshenv&amp;quot;&amp;gt;2. Identifique o que está em &amp;lt;code&amp;gt;.zshrc&amp;lt;/code&amp;gt; mas não está em `.zshenv&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_3_verificar_se_o_zshenv_existe&amp;quot;&amp;gt;3. Verificar se o .zshenv existe&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_zshenv_os_caminhos_corretos&amp;quot;&amp;gt;A solução: .zshenv + os caminhos corretos&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_criar_zshenv_com_todos_os_caminhos_essenciais&amp;quot;&amp;gt;Criar .zshenv com todos os caminhos essenciais&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_não_source_sdkman_init_sh_no_zshenv&amp;quot;&amp;gt;Por que não &amp;lt;code&amp;gt;source sdkman-init.sh&amp;lt;/code&amp;gt; no .zshenv?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_caso_nvm_quando_o_gerenciador_de_versões_esquece_de_deixar_uma_marca&amp;quot;&amp;gt;O caso NVM: quando o gerenciador de versões esquece de deixar uma marca&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_nvm&amp;quot;&amp;gt;O problema NVM&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_criar_o_symlink_current_para_nvm&amp;quot;&amp;gt;A solução: criar o symlink &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; para NVM&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_automatizar_a_atualização_do_symlink&amp;quot;&amp;gt;Automatizar a atualização do symlink&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_tabela_resumo_dos_caminhos&amp;quot;&amp;gt;Tabela resumo dos caminhos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_armadilhas_e_mitigações&amp;quot;&amp;gt;Armadilhas e mitigações&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_lições_aprendidas&amp;quot;&amp;gt;Lições aprendidas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_verificação_final&amp;quot;&amp;gt;Verificação final&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_links&amp;quot;&amp;gt;Links&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_documentação_oficial&amp;quot;&amp;gt;Documentação oficial&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_ferramentas_mencionadas&amp;quot;&amp;gt;Ferramentas mencionadas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_para_ir_além&amp;quot;&amp;gt;Para ir além&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 13 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Você inicia o OpenCode no seu terminal, tudo funciona. O agente tenta um`gh`— não encontrado. Um`java -version`— ausente. Um`node`— em nenhum lugar. Porém, essas ferramentas estão realmente aqui no &amp;lt;em&amp;gt;votre&amp;lt;/em&amp;gt; shell. O problema? OpenCode lança shells.&amp;lt;strong&amp;gt;não interativos&amp;lt;/strong&amp;gt;que nunca leem seu`.zshrc`. Como diagnosticar e corrigir isso corretamente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_cena_um_agente_cego_num_mundo_de_ferramentas&amp;quot;&amp;gt;A cena: um agente cego num mundo de ferramentas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Era uma terça-feira à noite. Eu acabara de instalarhttps://opencode.ai[OpenCode], o agente IA que prometia transformar a minha forma de codar. Primeiro teste: pedir a ele para listar os meus repositórios do GitHub.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ opencode
&amp;amp;gt; Utilise gh pour lister mes repos
❌ bash: gh: command not found&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Estranho.`gh`funcionava perfeitamente no meu terminal. Estou tentando outra coisa:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;&amp;amp;gt; Vérifie la version de Java
❌ bash: java: command not found&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Então:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;&amp;amp;gt; Lance le build Gradle
❌ bash: gradle: command not found&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Java, Gradle, Node,&amp;lt;code&amp;gt;gh&amp;lt;/code&amp;gt;— tudo era invisível para o agente. Meu terminal, porém, via tudo. Como se o agente e eu vivêssemos em dois mundos paralelos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Levei horas para entender. Horas de`echo $PATH`, de &amp;lt;code&amp;gt;which java&amp;lt;/code&amp;gt;, de crescente frustração. Finalmente percebi que o problema não eram as ferramentas — era o&amp;lt;strong&amp;gt;shell&amp;lt;/strong&amp;gt;. OpenCode, assim como todo agente que inicia subprocessos, trabalha em shells&amp;lt;strong&amp;gt;não interativos&amp;lt;/strong&amp;gt;. E o Zsh, nesses shells, ignora simplesmente seu`.zshrc`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O que se segue é o relato completo deste diagnóstico, da resolução e da lição que tirei. Se você utiliza um agente de IA — OpenCode, Aider, Cursor ou mesmo scripts cron — esse problema o afetará um dia.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_anatomia_do_bug_dois_shells_dois_mundos&amp;quot;&amp;gt;Anatomia do bug: dois shells, dois mundos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando você abre um terminal, o Zsh o trata como um shell&amp;lt;strong&amp;gt;interativo&amp;lt;/strong&amp;gt;. Ele carrega`.zshrc`, que inicializa tudo: SDKMAN, NVM, pnpm, os aliases, o seu prompt bonito. O seu ambiente está completo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas quando o OpenCode executa um comando, ele não inicia um terminal interativo. Ele inicia um shell&amp;lt;strong&amp;gt;não interativo&amp;lt;/strong&amp;gt;— um shell de tarefa, sem humano atrás da tela. E Zsh, neste contexto, salta`.zshrc`. Ele lê apenas`.zshenv`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Por que essa distinção existe? Porque um shell não-interativo foi projetado para executar scripts, não para atender a um usuário humano. Carregar os aliases, o prompt e as completions em um script que roda em segundo plano seria um desperdício. O problema é que o seu PATH — a informação mais crítica para localizar os executáveis — costuma ser inicializado em`.zshrc`, não em`.zshenv`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 5) ]

@startuml
skinparam backgroundColor #FEFEFE

actor &amp;quot;Você&amp;quot; as user
actor &amp;quot;OpenCode
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE

actor &amp;quot;Você&amp;quot; as user
actor &amp;quot;OpenCode
(agente)&amp;quot; as agent
participant &amp;quot;Shell
interativo
(.zshrc carregado)&amp;quot; as ishell
participant &amp;quot;Shell
não interativo
(.zshrc ignorado)&amp;quot; as nshell

user -&amp;amp;gt; ishell : gh, java, node ✅
agent -&amp;amp;gt; nshell : gh, java, node ❌

note right of ishell
  Source .zshrc :
  - SDKMAN init
  - NVM init
  - ~/apps dans PATH
  - pnpm dans PATH
end note

note right of nshell
  .zshrc ignoré !
  PATH = /usr/bin:/bin
  + quelques répertoires défaut
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A raiz do problema:&amp;lt;strong&amp;gt;Zsh apenas carrega &amp;lt;code&amp;gt;.zshrc&amp;lt;/code&amp;gt; para shells interativos&amp;lt;/strong&amp;gt;. OpenCode, como todo agente de IA que inicia subprocessos, utiliza shells não-interativos. Esses shells leem apenas`.zshenv`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_os_culpados_zshrc_vs_zshenv&amp;quot;&amp;gt;Os culpados: .zshrc vs .zshenv&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Zsh dispõe de&amp;lt;strong&amp;gt;quatro arquivos de inicialização&amp;lt;/strong&amp;gt;, cada um com um papel preciso. É um design elegante — mas isso também é o nó do problema:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 5%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;arquivo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Obtido quando&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;papel&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Altera o PATH ?&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;.zshenv&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;sempre&amp;lt;/strong&amp;gt;(interativo + não interativo + login)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Variáveis de ambiente essenciais, PATH&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅ Sim — é o lugar dele/ela&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;.zprofile&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Apenas shells de login&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Comandos lentos (uma vez por sessão)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Possível&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;.zshrc&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Shells interativos apenas&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Alias, prompt, completão, ferramentas interativas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;❌ Não para as variáveis críticas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;.zlogin&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Shells de login (depois .zshrc)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Mensagens de boas-vindas, finalização&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;raramente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A tabela dá uma indicação, mas é preciso compreendê-la em profundidade. Pense nisso como os cómodos de uma casa:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;`.zshenv`é o&amp;lt;strong&amp;gt;hall de entrada&amp;lt;/strong&amp;gt;— todos passam por lá, visitante ou residente. Se você colocar algo aqui, qualquer shell poderá vê-lo.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;`.zshrc`é o&amp;lt;strong&amp;gt;sala&amp;lt;/strong&amp;gt;— apenas os residentes (shells interativos) entram ali. Os visitantes (shells não interativos) permanecem no hall.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;.zprofile&amp;lt;/code&amp;gt; et `.zlogin`são peças especializadas para shells de login (como quando você se conecta via SSH).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O drama é que a maioria de nós colocamos o PATH na sala. E o agente de IA, ele, nunca tem o direito de entrar lá.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O problema em um diagrama:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/zsh-init-flow.svg&amp;quot; alt=&amp;quot;zsh init flow&amp;quot; width=&amp;quot;543&amp;quot; height=&amp;quot;404&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando você abre um terminal, o Zsh é interativo: ele lê`.zshrc`, tudo funciona. Quando o OpenCode lança um shell para executar um comando, o Zsh é não-interativo: ele pula`.zshrc`, só lê`.zshenv`. Et si `.zshenv`não existe ou não contém o PATH — é o deserto.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Por que o Zsh faz isso?&amp;lt;/strong&amp;gt;É uma escolha de design herdada do Unix. Um shell não interativo deve ser&amp;lt;strong&amp;gt;rápido&amp;lt;/strong&amp;gt; et &amp;lt;strong&amp;gt;reprodutível&amp;lt;/strong&amp;gt;. Carregar os aliases, os prompts coloridos e as inicializações pesadas do SDKMAN em um script de cron ou um agente de IA seria lento e frágil. A separação, portanto, é lógica :`.zshenv`para o essencial (variáveis, PATH)`.zshrc`para o conforto (aliases, prompt, completamos). O problema ocorre quando colocamos o essencial no conforto.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_diagnóstico_passo_a_passo&amp;quot;&amp;gt;Diagnóstico passo a passo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes de corrigir, é preciso entender exatamente o que falta. Este é um método de diagnóstico reprodutível — guarde-a nos favoritos se você trabalha com agentes de IA.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_verificar_o_path_do_agente&amp;quot;&amp;gt;Verificar o PATH do agente&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Comparamos o que o seu terminal interativo vê com o que um shell não interativo vê — ou seja, o que o OpenCode vê.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Dans votre terminal interactif (tout fonctionne)
echo $PATH | tr &amp;#39;:&amp;#39; &amp;#39;\n&amp;#39; | grep -v &amp;#39;^/usr&amp;#39; | sort&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Resultado típico :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;/home/cheroliv/apps
/home/cheroliv/.nvm/versions/node/v22.19.0/bin
/home/cheroliv/.sdkman/candidates/java/current/bin
/home/cheroliv/.sdkman/candidates/gradle/current/bin
/home/cheroliv/.local/share/pnpm
/home/cheroliv/.local/bin&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Agora, vamos simular o que o agente vê — um shell não-interativo :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Shell non-interactif : pas de .zshrc
zsh -c &amp;#39;echo $PATH&amp;#39; | tr &amp;#39;:&amp;#39; &amp;#39;\n&amp;#39; | grep -v &amp;#39;^/usr&amp;#39; | sort&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Resultado :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;/home/cheroliv/.local/bin&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Cinco caminhos de seis desapareceram.&amp;lt;/strong&amp;gt;O agente está amputado de 83% do seu ambiente. É como se você fosse solicitado a cozinhar sem metade dos seus utensílios — você poderia ferver água, mas pouco mais.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock warning&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-warning&amp;quot; title=&amp;quot;Warning&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Encomenda`zsh -c &amp;#39;echo $PATH&amp;#39;`é seu&amp;lt;strong&amp;gt;ferramenta de diagnóstico número 1&amp;lt;/strong&amp;gt;. Se uma ferramenta que você utiliza diariamente estiver ausente do resultado, seu agente de IA também não poderá vê-la. Teste-a antes e depois de cada modificação de`.zshenv`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_2_identifique_o_que_está_em_zshrc_mas_não_está_em_zshenv&amp;quot;&amp;gt;2. Identifique o que está em &amp;lt;code&amp;gt;.zshrc&amp;lt;/code&amp;gt; mas não está em `.zshenv&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Agora, estamos procurando o culpado em`.zshrc`. Filtrar as linhas que mencionam nossas ferramentas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;grep -n &amp;#39;apps\|SDKMAN\|NVM\|PNPM\|PATH&amp;#39; ~/.zshrc&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Costuma-se encontrar :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;119: PATH=&amp;quot;/usr/bin/python3:$HOME/apps:$PATH&amp;quot;           # ← pas exporté !
171: export NVM_DIR=&amp;quot;$HOME/.nvm&amp;quot;
172: [ -s &amp;quot;$NVM_DIR/nvm.sh&amp;quot; ] &amp;amp;amp;&amp;amp;amp; \. &amp;quot;$NVM_DIR/nvm.sh&amp;quot;   # ← NVM init
176: nvm use --lts --silent                              # ← active une version
179: export PNPM_HOME=&amp;quot;/home/cheroliv/.local/share/pnpm&amp;quot;
187: export SDKMAN_DIR=&amp;quot;$HOME/.sdkman&amp;quot;
188: [[ -s &amp;quot;$HOME/.sdkman/bin/sdkman-init.sh&amp;quot; ]] &amp;amp;amp;&amp;amp;amp; source &amp;quot;$HOME/.sdkman/bin/sdkman-init.sh&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Tudo isso é&amp;lt;strong&amp;gt;invisível&amp;lt;/strong&amp;gt;para OpenCode. E o`PATH`da linha 119 nem mesmo é`export`é — ele nunca sai do shell atual. É um detalhe técnico crucial: uma variável sem`export`Permanece local ao shell que a define. Os sub-shells — como aqueles lançados pelo OpenCode — nunca a herdam.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_3_verificar_se_o_zshenv_existe&amp;quot;&amp;gt;3. Verificar se o .zshenv existe&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;cat ~/.zshenv 2&amp;amp;gt;/dev/null || echo &amp;quot;FICHIER ABSENT&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se a resposta é « ARQUIVO AUSENTE », é aí que tudo se decide.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_zshenv_os_caminhos_corretos&amp;quot;&amp;gt;A solução: .zshenv + os caminhos corretos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A estratégia é simples, mas exige precisão: colocar dentro`.zshenv` &amp;lt;strong&amp;gt;apenas&amp;lt;/strong&amp;gt;caminhos essenciais, sem executar os scripts de inicialização pesados. não movemos`.zshrc`em`.zshenv`— extraímos o essencial.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_criar_zshenv_com_todos_os_caminhos_essenciais&amp;quot;&amp;gt;Criar .zshenv com todos os caminhos essenciais&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`.zshenv`é o&amp;lt;strong&amp;gt;arquivo único&amp;lt;/strong&amp;gt;que Zsh garante de sourcear em&amp;lt;strong&amp;gt;todos&amp;lt;/strong&amp;gt;os contextos. É aqui que o PATH deve ir.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;export SDKMAN_DIR=&amp;quot;$HOME/.sdkman&amp;quot;
export NVM_DIR=&amp;quot;$HOME/.nvm&amp;quot;
export PNPM_HOME=&amp;quot;$HOME/.local/share/pnpm&amp;quot;
export PATH=&amp;quot;$HOME/apps:$HOME/.sdkman/candidates/java/current/bin:$HOME/.sdkman/candidates/gradle/current/bin:$HOME/.nvm/current/bin:$PNPM_HOME:$HOME/.local/bin:$HOME/.local/share/JetBrains/Toolbox/scripts:/usr/bin/python3:$PATH&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Utiliza-se`$HOME/.nvm/current/bin`e não`$HOME/.nvm/versions/node/v22.19.0/bin`. A razão : as versões do Node mudam. Um caminho fixo se torna falso no próximo`nvm install`. Mais detalhes na seção seguinte.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_por_que_não_source_sdkman_init_sh_no_zshenv&amp;quot;&amp;gt;Por que não &amp;lt;code&amp;gt;source sdkman-init.sh&amp;lt;/code&amp;gt; no .zshenv?&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A abordagem mais tentadora seria simplesmente reproduzir em`.zshenv`o que fazemos em`.zshrc`— carregar os scripts de inicialização. Nós&amp;lt;strong&amp;gt;poderia&amp;lt;/strong&amp;gt;estar tentado de fazer:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# ❌ MAUVAISE IDÉE
[[ -s &amp;quot;$HOME/.sdkman/bin/sdkman-init.sh&amp;quot; ]] &amp;amp;amp;&amp;amp;amp; source &amp;quot;$HOME/.sdkman/bin/sdkman-init.sh&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Problemas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Lento&amp;lt;/strong&amp;gt; : `sdkman-init.sh`faz resoluções de rede e verificações a cada shell. Em um shell não interativo, é um custo desnecessário.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;frágil&amp;lt;/strong&amp;gt;: SDKMAN espera um contexto interativo. Sua inicialização pode falhar silenciosamente em um pipe ou em um subshell.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;inútil&amp;lt;/strong&amp;gt;: SDKMAN coloca seus candidatos em`~/.sdkman/candidates/&amp;amp;lt;tool&amp;amp;gt;/current/bin`— links simbólicos estáveis apontando para a versão ativa. Pode‑se utilizá‑los&amp;lt;strong&amp;gt;diretamente&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A abordagem correta: contornar a inicialização do SDKMAN e apontar diretamente para os symlinks`current`. É a chave de toda a solução :&amp;lt;strong&amp;gt;usar a estrutura de arquivos como contrato, em vez do código de inicialização&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;Abordagem Lenta\n(source sdkman-init.sh)&amp;quot; as slow {
  card &amp;quot;.zshenv source sdkman-init.sh
2-3 segundos por shell
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;Abordagem Lenta\n(source sdkman-init.sh)&amp;quot; as slow {
  card &amp;quot;.zshenv source sdkman-init.sh
2-3 segundos por shell
Verificações de rede
Risco de falha&amp;quot; as s1 #FDEDEC
}

rectangle &amp;quot;Abordagem rápida
(symlinks diretos)&amp;quot; as fast {
  card &amp;quot;.zshenv export PATH=...current/bin
0 ms
Sem rede
Sem inicialização&amp;quot; as s2 #E8F8E8
}

slow --&amp;amp;gt; fast : Même résultat final\nLe PATH pointe sur current/bin\ndans les deux cas

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_caso_nvm_quando_o_gerenciador_de_versões_esquece_de_deixar_uma_marca&amp;quot;&amp;gt;O caso NVM: quando o gerenciador de versões esquece de deixar uma marca&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_problema_nvm&amp;quot;&amp;gt;O problema NVM&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É aqui que a investigação me levou mais longe. SDKMAN tem um design elegante: quando se faz`sdk install java 25.0.2-tem`, ele cria um symlink :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;~/.sdkman/candidates/java/current -&amp;amp;gt; ~/.sdkman/candidates/java/25.0.2-tem&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este link simbólico é&amp;lt;strong&amp;gt;sempre atualizado&amp;lt;/strong&amp;gt;Apontem-no em`.zshenv`e você está coberto, não importa qual seja o shell. Bonito.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;NVM, ele, não cria&amp;lt;strong&amp;gt;nada&amp;lt;/strong&amp;gt;de tal. Sem symlink`current`. Não há ponto de ancoragem estável. É necessário apontar para um caminho versionado embutido, que se parece com isso :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;~/.nvm/versions/node/v22.19.0/bin/node&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No próximo`nvm install 24`, este caminho está morto. Seu`.zshenv`apontará para uma versão que não é mais a versão ativa. É uma bomba-relógio.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Por que o NVM faz isso?&amp;lt;/strong&amp;gt;NVM funciona modificando dinamicamente o PATH a cada`nvm use`. Foi projetado para desenvolvedores que mudam de versão frequentemente, em um shell interativo. O symlink`current`Não estava nas especificações iniciais — é um esquecimento de design que vamos corrigir nós mesmos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/nvm-vs-sdkman.svg&amp;quot; alt=&amp;quot;nvm vs sdkman&amp;quot; width=&amp;quot;1746&amp;quot; height=&amp;quot;5676&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_solução_criar_o_symlink_current_para_nvm&amp;quot;&amp;gt;A solução: criar o symlink &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; para NVM&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Como o NVM não faz isso, nós o fazemos nós mesmos. O princípio é o mesmo que o SDKMAN — um symlink`current`que sempre aponta para a versão ativa. Cria-se uma vez, então automatiza-se para que se atualize sozinho.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Créer le symlink initial
ln -sfn &amp;quot;$HOME/.nvm/versions/node/v22.19.0&amp;quot; &amp;quot;$HOME/.nvm/current&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Agora, em`.zshenv`, utiliza :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$HOME/.nvm/current/bin&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em vez de :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# ❌ Chemin en dur — cassé au prochain changement de version
$HOME/.nvm/versions/node/v22.19.0/bin&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_automatizar_a_atualização_do_symlink&amp;quot;&amp;gt;Automatizar a atualização do symlink&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um symlink não vale nada se não se atualiza. O symlink`current`deve ser atualizado quando fazemos`nvm use` ou &amp;lt;code&amp;gt;nvm install&amp;lt;/code&amp;gt;. A solução : um&amp;lt;strong&amp;gt;embrulho&amp;lt;/strong&amp;gt;— uma função que envolve o comando verdadeiro`nvm`e atualiza o symlink após cada chamada&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# À la fin de .zshrc, APRÈS le chargement de NVM
nvm use --lts --silent
ln -sfn &amp;quot;$(nvm_version_path &amp;quot;$(nvm current)&amp;quot;)&amp;quot; &amp;quot;$NVM_DIR/current&amp;quot;

_nvm() {
  command nvm &amp;quot;$@&amp;quot;
  local rc=$?
  ln -sfn &amp;quot;$(nvm_version_path &amp;quot;$(nvm current)&amp;quot;)&amp;quot; &amp;quot;$NVM_DIR/current&amp;quot;
  return $rc
}
alias nvm=&amp;#39;_nvm&amp;#39;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Como funciona, em detalhe:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 5) ]

@startuml
skinparam backgroundColor #FEFEFE

actor Développeur
participant &amp;quot;nvm envolvedor
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE

actor Développeur
participant &amp;quot;nvm envolvedor
(_nvm)&amp;quot; as wrapper
participant &amp;quot;nvm real\n(command nvm)&amp;quot; as realnvm
participant &amp;quot;~/.nvm/current\n(symlink)&amp;quot; as symlink

Développeur -&amp;amp;gt; wrapper : nvm use 20
wrapper -&amp;amp;gt; realnvm : command nvm use 20
realnvm --&amp;amp;gt; wrapper : version activée
wrapper -&amp;amp;gt; symlink : ln -sfn .../v20.16.0 ~/.nvm/current
wrapper --&amp;amp;gt; Développeur : retour

note right of symlink
  Toujours à jour !
  Utilisé par .zshenv
  → accessible par OpenCode
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O wrapper`_nvm`chama o comando verdadeiro`nvm`, então atualiza o symlink. O alias`nvm=&amp;#39;_nvm&amp;#39;`certifique-se de que ao digitar`nvm`, passamos pelo wrapper. E na inicialização do shell, fazemos o mesmo depois`nvm use --lts`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock tip&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-tip&amp;quot; title=&amp;quot;Tip&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`nvm_version_path`é uma função interna do NVM que resolve o caminho completo de uma versão. Isso evita de reconstruir o caminho manualmente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_tabela_resumo_dos_caminhos&amp;quot;&amp;gt;Tabela resumo dos caminhos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes de passar para as armadilhas, um resumo visual da transformação. À esquerda, o que você tinha (tudo em`.zshrc`, invisível para o agente). à direita, o que você tem agora (os caminhos essenciais em`.zshenv`, visíveis em todo lugar).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;ferramenta&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Antes (.zshrc apenas)&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Depois (.zshenv + symlink)&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Visível pelo OpenCode?&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;~/apps&amp;lt;/code&amp;gt;(gh, vscode)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;PATH não exportado no .zshrc&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`$HOME/apps`em .zshenv&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;SDKMAN Java&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`sdkman-init.sh`carregado no .zshrc&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`$HOME/.sdkman/candidates/java/current/bin`no .zshenv&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;(No output, as there is no French text provided to translate)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;SDKMAN Gradle&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`sdkman-init.sh`carregado no .zshrc&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`$HOME/.sdkman/candidates/gradle/current/bin`em .zshenv&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;NVM Node&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`nvm use --lts`no .zshrc&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`$HOME/.nvm/current/bin`em .zshenv (link simbólico)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;pnpm&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`$PNPM_HOME`no .zshrc&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`$PNPM_HOME`no .zshenv&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;JetBrains Toolbox&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Auto-adicionado pelo Toolbox em .zshrc&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`$HOME/.local/share/JetBrains/Toolbox/scripts`no .zshenv&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Python 3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`/usr/bin/python3`no PATH .zshrc&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Explícitamente em .zshenv&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�✅&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_armadilhas_e_mitigações&amp;quot;&amp;gt;Armadilhas e mitigações&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nem tudo é perfeito com essa abordagem. Aqui estão os problemas que encontrei, e como contorná-los.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3334%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;armadilha&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;descrição&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;mitigação&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Caminho duplicado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Se .zshenv e .zshrc adicionam o mesmo caminho, aparece duas vezes&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`.zshenv`é fonte&amp;lt;strong&amp;gt;antes&amp;lt;/strong&amp;gt;.zshrc. Os dois são lidos em um shell interativo. O PATH pode duplicar. É estético, não funcional. Para evitá‑lo: coloque apenas no .zshenv os caminhos &amp;lt;em&amp;gt;ausentes&amp;lt;/em&amp;gt; do PATH padrão.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;NVM : versão fixa no .zshenv&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;colocar`~/.nvm/versions/node/v22.19.0/bin`hard-coded torna-se falso no próximo`nvm install`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Usar o symlink`$HOME/.nvm/current/bin`+ o wrapper`_nvm`no .zshrc&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;SDKMAN: source sdkman-init.sh no .zshenv&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Lento, frágil, inútil em um shell não interativo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Usar os symlinks`candidates/&amp;amp;lt;tool&amp;amp;gt;/current/bin`diretamente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Alias ausente após modificação&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O wrapper`nvm=&amp;#39;_nvm&amp;#39;`no .zshrc só tem efeito após recarregar&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Reiniciar o shell ou`source ~/.zshrc`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;OpenCode não vê as mudanças&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O agente já lançou seus subshells com o .zshenv antigo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Reinicie o OpenCode após a modificação de .zshenv&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;.zshenv demasiado carregado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Colocar funções pesadas ou inicializações interativas no .zshenv&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;.zshenv = variáveis de ambiente + PATH apenas. Não`source`, sem funções pesadas, sem prompts.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_lições_aprendidas&amp;quot;&amp;gt;Lições aprendidas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;.zshrc` é interativo, &amp;lt;code&amp;gt;.zshenv&amp;lt;/code&amp;gt; é universal&amp;lt;/strong&amp;gt;— Se uma variável deve existir em &amp;lt;em&amp;gt;todos&amp;lt;/em&amp;gt; os shells (agentes IA, cron, scripts, IDE), ela vai para`.zshenv`. A sala é confortável, mas o hall de entrada é o único lugar onde todos passam.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Os gerentes de versões não são iguais&amp;lt;/strong&amp;gt;— SDKMAN cria symlinks`current`por design. NVM não. É preciso preencher essa lacuna manualmente. É uma lição importante: antes de configurar seu PATH, verifique se o gerenciador oferece um ponto de ancoragem estável.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Não source os init scripts em .zshenv&amp;lt;/strong&amp;gt;—&amp;lt;code&amp;gt;sdkman-init.sh&amp;lt;/code&amp;gt; et `nvm.sh`são projetados para um shell interativo. Eles são lentos e frágeis em um contexto não-interativo. Os symlinks`current`são suficientes e são instantâneos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Sempre testar em shell não-interativo&amp;lt;/strong&amp;gt;—`zsh -c &amp;#39;echo $PATH&amp;#39;`simula exatamente o que um agente vê. Este é o teste de validação. Sem este teste, você não sabe se sua configuração funciona para os agentes.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O padrão wrapper é reutilizável&amp;lt;/strong&amp;gt;— O mesmo padrão`_nvm`+`alias nvm=&amp;#39;_nvm&amp;#39;`Aplica-se a qualquer ferramenta que modifique o PATH dinamicamente sem deixar um rastro estável. É uma ferramenta a mais na sua caixa de ideias.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;ANTES&amp;quot; as avant {
  card &amp;quot;Shell interativo : ✅
Shell não interativo : ❌
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;ANTES&amp;quot; as avant {
  card &amp;quot;Shell interativo : ✅
Shell não interativo : ❌
OpenCode : ❌
Cron : ❌
Scripts : ❌&amp;quot; as av1 #FDEDEC
}

rectangle &amp;quot;DEPOIS&amp;quot; as apres {
  card &amp;quot;Shell interativo : ✅\nShell não interativo : ✅\nOpenCode : ✅\nCron : ✅\nScripts : ✅&amp;quot; as ap1 #E8F8E8
}

avant --&amp;amp;gt; apres : .zshenv +\nsymlink ~/.nvm/current

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_verificação_final&amp;quot;&amp;gt;Verificação final&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Depois de ter criado`.zshenv`e o symlink NVM, verifique que tudo funciona — nos dois contextos :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Shell interactif (votre terminal)
gh --version &amp;amp;amp;&amp;amp;amp; java -version &amp;amp;amp;&amp;amp;amp; node --version&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Shell non-interactif (simulation OpenCode)
zsh -c &amp;#39;gh --version &amp;amp;amp;&amp;amp;amp; java -version &amp;amp;amp;&amp;amp;amp; node --version&amp;#39;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ambos devem ter sucesso. Se sim, seu agente de IA verá as mesmas ferramentas que você. Se não, volte ao diagnóstico passo a passo — você provavelmente esqueceu um caminho ou o symlink NVM não está atualizado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock tip&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-tip&amp;quot; title=&amp;quot;Tip&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Automatize este teste.&amp;lt;/strong&amp;gt;Adicione esta verificação em um script de healthcheck que você executa após cada atualização das suas ferramentas. Un`zsh -c &amp;#39;which java &amp;amp;amp;&amp;amp;amp; which node &amp;amp;amp;&amp;amp;amp; which gh&amp;#39;`Em CI, é uma garantia contra as surpresas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;amp;lt;/think&amp;amp;gt; (No text provided to translate; output remains empty.) Um shell não-interativo é como um convidado silencioso: ele lê apenas o que está exibido na porta de entrada. Se o PATH estiver na sala de estar, ele nunca o verá. &amp;lt;em&amp;gt;__&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_links&amp;quot;&amp;gt;Links&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_documentação_oficial&amp;quot;&amp;gt;Documentação oficial&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://zsh.sourceforge.io/Doc/Release/Files.html&amp;quot;&amp;gt;Documentação do Zsh: Arquivos de inicialização&amp;lt;/a&amp;gt;— A referência oficial sobre os arquivos de inicialização do Zsh. É lá que tudo é explicado, mesmo que se esqueça frequentemente.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://opencode.ai/docs/config/&amp;quot;&amp;gt;OpenCode : configuração&amp;lt;/a&amp;gt;— Como configurar OpenCode e seu ambiente de execução&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_ferramentas_mencionadas&amp;quot;&amp;gt;Ferramentas mencionadas&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/nvm-sh/nvm&amp;quot;&amp;gt;NVM no GitHub&amp;lt;/a&amp;gt;— Node Version Manager. Gerenciador de versões Node.js.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://sdkman.io&amp;quot;&amp;gt;SDKMAN — site oficial&amp;lt;/a&amp;gt;— SDKMAN! O gerenciador de versões para a JVM (Java, Kotlin, Gradle&amp;amp;#8230;&amp;amp;#8203;)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://sdkman.io/install&amp;quot;&amp;gt;SDKMAN : instalação&amp;lt;/a&amp;gt;— Guia de instalação do SDKMAN.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://cli.github.com&amp;quot;&amp;gt;gh` — GitHub CLI&amp;lt;/a&amp;gt;— A ferramenta de linha de comando GitHub, essencial para todo desenvolvedor.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://gradle.org&amp;quot;&amp;gt;Gradle — site oficial&amp;lt;/a&amp;gt;— O sistema de build que instalamos via SDKMAN.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://pnpm.io&amp;quot;&amp;gt;pnpm — site oficial&amp;lt;/a&amp;gt;— O gerenciador de pacotes Node.js rápido e econômico em espaço.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://www.jetbrains.com/toolbox-app/&amp;quot;&amp;gt;JetBrains Toolbox&amp;lt;/a&amp;gt;— O gerenciador de IDE JetBrains, que adiciona seus scripts ao PATH.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_para_ir_além&amp;quot;&amp;gt;Para ir além&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://blog.andy2003.org/posts/2022/zsh-dotfiles/&amp;quot;&amp;gt;Dotfiles do Zsh : um guia completo&amp;lt;/a&amp;gt;— Artigo aprofundado sobre a gestão de dotfiles Zsh.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://wiki.archlinux.org/title/Zsh&amp;quot;&amp;gt;Zsh no ArchWiki&amp;lt;/a&amp;gt;— Uma das melhores documentações comunitárias sobre Zsh.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/nvm-sh/nvm/issues&amp;quot;&amp;gt;NVM problemas GitHub&amp;lt;/a&amp;gt;— Para ver as discussões ao redor do symlink`current`e problemas de PATH.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Integrar o Graphify em um workflow Gradle: do Knowledge Graph ao diagrama PlantUML em um comando</title>
            <link >https://cheroliv.com//blog/2026/0105_integrer_graphify_workflow_gradle_post.html</link>
            <pubDate>Sun, 19 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0105_integrer_graphify_workflow_gradle_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_diagramas_sempre_atrasados_em_relação_ao_código&amp;quot;&amp;gt;O problema: diagramas sempre atrasados em relação ao código&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_um_pipeline_determinístico_knowledge_graph_plantuml&amp;quot;&amp;gt;A solução: um pipeline determinístico Knowledge Graph → PlantUML&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_1_instalar_graphify_e_extrair_o_knowledge_graph&amp;quot;&amp;gt;Passo 1: Instalar Graphify e extrair o Knowledge Graph&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalação&amp;quot;&amp;gt;Instalação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configurar_as_exclusões&amp;quot;&amp;gt;Configurar as exclusões&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_extrair_o_knowledge_graph&amp;quot;&amp;gt;Extrair o Knowledge Graph&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_2_o_plugin_gradle_transforma_o_knowledge_graph_em_plantuml&amp;quot;&amp;gt;Passo 2 : O plugin Gradle transforma o Knowledge Graph em PlantUML&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_do_pipeline&amp;quot;&amp;gt;Arquitetura do pipeline&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_os_componentes_internos&amp;quot;&amp;gt;Os componentes internos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_regras_de_renderização&amp;quot;&amp;gt;Regras de renderização&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_3_uso_diário&amp;quot;&amp;gt;Passo 3 : Uso diário&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_completo&amp;quot;&amp;gt;Diagrama completo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_filtrar_por_comunidade&amp;quot;&amp;gt;Filtrar por comunidade&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_filtrar_por_tipo_de_aresta&amp;quot;&amp;gt;Filtrar por tipo de aresta&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_filtrar_por_tipo_de_nó_e_confiança&amp;quot;&amp;gt;Filtrar por tipo de nó e confiança&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diretório_de_saída_personalizado&amp;quot;&amp;gt;Diretório de saída personalizado&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referência_completa_das_propriedades&amp;quot;&amp;gt;Referência completa das propriedades&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_pipeline_completo_em_um_workflow_gradle&amp;quot;&amp;gt;O pipeline completo em um workflow Gradle&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_tipo_de_workflow&amp;quot;&amp;gt;Tipo de workflow&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_integração_no_ciclo_de_desenvolvimento&amp;quot;&amp;gt;Integração no ciclo de desenvolvimento&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_atualização_incremental&amp;quot;&amp;gt;Atualização incremental&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_dogfooding_o_plugin_se_documenta_por_si_mesmo&amp;quot;&amp;gt;Dogfooding: o plugin se documenta por si mesmo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_é_determinístico_e_por_que_isso_importa&amp;quot;&amp;gt;Por que é determinístico (e por que isso importa)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagramas_do_próprio_pipeline&amp;quot;&amp;gt;Diagramas do próprio pipeline&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_integração_na_governança_do_projeto&amp;quot;&amp;gt;Integração na governança do projeto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_preparação_lista_de_verificação_em_5_minutos&amp;quot;&amp;gt;Preparação: lista de verificação em 5 minutos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_armadilhas_e_mitigações&amp;quot;&amp;gt;Armadilhas e mitigações&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_se_obtém_no_final&amp;quot;&amp;gt;O que se obtém no final&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_links&amp;quot;&amp;gt;Links&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 12 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Sua base de código cresce. As dependências entre módulos se multiplicam. A documentação da arquitetura torna-se obsoleta antes mesmo de ser escrita. E se seu build Gradle pudesse &amp;lt;em&amp;gt;automaticamente&amp;lt;/em&amp;gt; gerar diagramas atualizados a partir da estrutura real do código ? É exatamente isso que o pipeline Graphify + PlantUML Gradle Plugin :`graphify . --no-viz`extrai o Knowledge Graph,`./gradlew generateKnowledgeGraphDiagram`o transforma em diagramas PlantUML. Zero LLM, zero manual, 100% determinista.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_problema_diagramas_sempre_atrasados_em_relação_ao_código&amp;quot;&amp;gt;O problema: diagramas sempre atrasados em relação ao código&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Todo projeto que ultrapassa alguns milhares de linhas conhece esse sintoma:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Desenha-se um diagrama de arquitetura no início do projeto.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O código evolui, as dependências mudam&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O diagrama se torna uma mentira decorativa&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ninguém o atualiza porque é chato&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Os novos chegados baseiam-se nisso e cometem erros&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/probleme-diagrammes-obsoletes.svg&amp;quot; alt=&amp;quot;probleme diagrammes obsoletes&amp;quot; width=&amp;quot;824&amp;quot; height=&amp;quot;418&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A questão não é &amp;lt;em&amp;gt;é necessário ter diagramas?&amp;lt;/em&amp;gt; — todo mundo sabe que sim. A questão é:&amp;lt;strong&amp;gt;Quem os mantém atualizados?&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A resposta: ninguém. A menos que seja automático.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_um_pipeline_determinístico_knowledge_graph_plantuml&amp;quot;&amp;gt;A solução: um pipeline determinístico Knowledge Graph → PlantUML&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O princípio é simples: em vez de desenhar os diagramas à mão, os&amp;lt;strong&amp;gt;gera a partir da estrutura real do código&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 8) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

actor Développeur
component &amp;quot;Graphify\n(pip install graphifyy)&amp;quot; as Graphify
collections &amp;quot;graphify-out/graph.json\n(Knowledge Graph)&amp;quot; as KGJSON
component &amp;quot;PlantUML Gradle Plugin
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

actor Développeur
component &amp;quot;Graphify\n(pip install graphifyy)&amp;quot; as Graphify
collections &amp;quot;graphify-out/graph.json\n(Knowledge Graph)&amp;quot; as KGJSON
component &amp;quot;PlantUML Gradle Plugin
(generateKnowledgeGraphDiagram)&amp;quot; as Plugin
component &amp;quot;Analisador de Grafo de Conhecimento&amp;quot; as Parser
component &amp;quot;KnowledgeGraphRenderer&amp;quot; as Renderer
component &amp;quot;PlantumlService
(validação + renderização PNG)&amp;quot; as PS
collections &amp;quot;diagrams/knowledge-graph/\n(.puml + .png)&amp;quot; as Output

Développeur --&amp;amp;gt; Graphify : graphify . --no-viz
Graphify --&amp;amp;gt; KGJSON : extrait la structure\ndu code source
Développeur --&amp;amp;gt; Plugin : ./gradlew generateKnowledgeGraphDiagram
Plugin --&amp;amp;gt; Parser : parse(graph.json)
Parser --&amp;amp;gt; Plugin : KnowledgeGraph\n(noeuds + arêtes + communautés)
Plugin --&amp;amp;gt; Renderer : render(graph, filters)
Renderer --&amp;amp;gt; Plugin : code PlantUML\ndéterministe
Plugin --&amp;amp;gt; PS : validateSyntax + generateImage
PS --&amp;amp;gt; Output : .puml + .png

note bottom of Plugin
  Pipeline DÉTERMINISTE
  Aucun appel LLM
  Résultat reproductible
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Dois comandos. É isso.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Étape 1 : extraire le Knowledge Graph
graphify . --no-viz

# Étape 2 : générer les diagrammes PlantUML
./gradlew generateKnowledgeGraphDiagram&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O resultado? Arquivos`.puml` et &amp;lt;code&amp;gt;.png`em`diagrams/knowledge-graph/&amp;lt;/code&amp;gt;, versionados no Git, sempre atualizados com o código.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_passo_1_instalar_graphify_e_extrair_o_knowledge_graph&amp;quot;&amp;gt;Passo 1: Instalar Graphify e extrair o Knowledge Graph&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_instalação&amp;quot;&amp;gt;Instalação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Graphify é uma ferramenta Python que analisa sua base de código e constrói um grafo de conhecimento estruturado:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Méthode recommandée
uv tool install graphifyy &amp;amp;amp;&amp;amp;amp; graphify install --platform opencode

# Alternative avec pip
pip install graphifyy &amp;amp;amp;&amp;amp;amp; graphify install --platform opencode&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_configurar_as_exclusões&amp;quot;&amp;gt;Configurar as exclusões&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Criar um arquivo`.graphifyignore`à raiz do projeto para excluir os arquivos que não fazem parte da lógica de negócios :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-gitignore hljs&amp;quot; data-lang=&amp;quot;gitignore&amp;quot;&amp;gt;# Secrets — JAMAIS dans le graphe
*-context.yml
*.env

# Fichiers générés
build/
.gradle/

# Tests fonctionnels
src/functionalTest/&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_extrair_o_knowledge_graph&amp;quot;&amp;gt;Extrair o Knowledge Graph&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;graphify . --no-viz&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O flag`--no-viz`Salta a geração de HTML (desnecessária em um pipeline Gradle). O resultado é um arquivo`graphify-out/graph.json`contendo :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Nós&amp;lt;/strong&amp;gt;: classes, funções, ficheiros — com o seu tipo e comunidade&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Arestas&amp;lt;/strong&amp;gt;: relações entre nós (EXTRACTED do código, INFERRED pelo LLM)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Comunidades&amp;lt;/strong&amp;gt;: agrupamentos automáticos de nós ligados&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Exemplo de estrutura`graph.json`&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-json hljs&amp;quot; data-lang=&amp;quot;json&amp;quot;&amp;gt;{
  &amp;quot;nodes&amp;quot;: [
    {&amp;quot;id&amp;quot;: &amp;quot;0&amp;quot;, &amp;quot;label&amp;quot;: &amp;quot;LlmService&amp;quot;, &amp;quot;file_type&amp;quot;: &amp;quot;code&amp;quot;, &amp;quot;community&amp;quot;: 0},
    {&amp;quot;id&amp;quot;: &amp;quot;1&amp;quot;, &amp;quot;label&amp;quot;: &amp;quot;ApiKeyPool&amp;quot;, &amp;quot;file_type&amp;quot;: &amp;quot;code&amp;quot;, &amp;quot;community&amp;quot;: 0},
    {&amp;quot;id&amp;quot;: &amp;quot;2&amp;quot;, &amp;quot;label&amp;quot;: &amp;quot;PlantumlService&amp;quot;, &amp;quot;file_type&amp;quot;: &amp;quot;code&amp;quot;, &amp;quot;community&amp;quot;: 1}
  ],
  &amp;quot;links&amp;quot;: [
    {&amp;quot;source&amp;quot;: &amp;quot;1&amp;quot;, &amp;quot;target&amp;quot;: &amp;quot;0&amp;quot;, &amp;quot;relation&amp;quot;: &amp;quot;uses&amp;quot;, &amp;quot;confidence&amp;quot;: &amp;quot;EXTRACTED&amp;quot;, &amp;quot;weight&amp;quot;: 0.9},
    {&amp;quot;source&amp;quot;: &amp;quot;0&amp;quot;, &amp;quot;target&amp;quot;: &amp;quot;2&amp;quot;, &amp;quot;relation&amp;quot;: &amp;quot;calls&amp;quot;, &amp;quot;confidence&amp;quot;: &amp;quot;INFERRED&amp;quot;, &amp;quot;weight&amp;quot;: 0.7}
  ]
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O código-fonte (&amp;lt;code&amp;gt;.kt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.java&amp;lt;/code&amp;gt;) é analisado localmente pelo tree-sitter,&amp;lt;strong&amp;gt;sem apelo LLM&amp;lt;/strong&amp;gt;. Apenas os arquivos de documentação (&amp;lt;code&amp;gt;.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.md&amp;lt;/code&amp;gt;) precisam de uma chamada LLM para a extração semântica. Portanto`--update`sobre do código Kotlin é quase instantâneo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_passo_2_o_plugin_gradle_transforma_o_knowledge_graph_em_plantuml&amp;quot;&amp;gt;Passo 2 : O plugin Gradle transforma o Knowledge Graph em PlantUML&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_arquitetura_do_pipeline&amp;quot;&amp;gt;Arquitetura do pipeline&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plugin`com.cheroliv.plantuml`incorpora uma tarefa`generateKnowledgeGraphDiagram`que transforma`graph.json`em diagramas PlantUML de maneira&amp;lt;strong&amp;gt;totalmente determinista&amp;lt;/strong&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/architecture-pipeline-kg.svg&amp;quot; alt=&amp;quot;architecture pipeline kg&amp;quot; width=&amp;quot;1195&amp;quot; height=&amp;quot;450&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_os_componentes_internos&amp;quot;&amp;gt;Os componentes internos&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Componente&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Papel&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;KnowledgeGraphParser&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Analisar`graph.json`— suporta 3 formatos : graphify nativo (&amp;lt;code&amp;gt;nodes&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;), legacy (&amp;lt;code&amp;gt;communities&amp;lt;/code&amp;gt;), plano. Resolve os IDs numéricos para rótulos.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;KnowledgeGraphRenderer&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Transforma deterministicamente um`KnowledgeGraph`em código PlantUML. Grupos por tipo, comunidades em pacotes, legenda automática.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;GenerateKnowledgeGraphDiagramTask&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tarefa Gradle que orquestra: parse → render → validate → PNG. Configurável via propriedades do Gradle.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;kgmodels.kt&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Modelos de dados :`KnowledgeGraph`, &amp;lt;code&amp;gt;KnowledgeGraphNode&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;KnowledgeGraphEdge&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;KnowledgeGraphCommunity&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;EdgeType&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;PlantumlService&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Validação sintática + renderização PNG (reutilizado por todas as tarefas do plugin).&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_regras_de_renderização&amp;quot;&amp;gt;Regras de renderização&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O renderer aplica convenções visuais determinísticas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3334%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Tipo de aresta&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Notação PlantUML&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Significação&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;extraído&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;-&amp;amp;#8594;&amp;lt;/code&amp;gt;(traço cheio, preto)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Relação extraída do código fonte (certeza)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;deduzido&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;..&amp;amp;gt;&amp;lt;/code&amp;gt;(traço pontilhado)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Relação inferida pelo LLM (pontuação de confiança)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;AMBÍGUO&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;--x&amp;lt;/code&amp;gt;(traço vermelho pontilhado)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Relação ambígua (a verificar)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;As comunidades são renderizadas como pacotes PlantUML com uma paleta de cores automática.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_passo_3_uso_diário&amp;quot;&amp;gt;Passo 3 : Uso diário&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_completo&amp;quot;&amp;gt;Diagrama completo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Générer le diagramme du Knowledge Graph complet
./gradlew generateKnowledgeGraphDiagram&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Saída:`diagrams/knowledge-graph/knowledge-graph-full.puml`+&amp;lt;code&amp;gt;.png&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_filtrar_por_comunidade&amp;quot;&amp;gt;Filtrar por comunidade&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Une seule communauté
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.community=community_0

# Limiter le nombre de noeuds (lisibilité)
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.community=community_0 \
  -Pplantuml.kg.maxNodes=15&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_filtrar_por_tipo_de_aresta&amp;quot;&amp;gt;Filtrar por tipo de aresta&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Uniquement les relations certaines (EXTRACTED)
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.edgeTypes=EXTRACTED

# Relations certaines + inférées
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.edgeTypes=EXTRACTED,INFERRED&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_filtrar_por_tipo_de_nó_e_confiança&amp;quot;&amp;gt;Filtrar por tipo de nó e confiança&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Uniquement les classes de code
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.nodeTypes=code

# Seuil de confiance minimum (pour les INFERRED)
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.minConfidence=0.7&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diretório_de_saída_personalizado&amp;quot;&amp;gt;Diretório de saída personalizado&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.outputDir=docs/architecture&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_referência_completa_das_propriedades&amp;quot;&amp;gt;Referência completa das propriedades&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3334%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Propriedade&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;defeito&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Descrição&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;plantuml.kg.community&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;(todas)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Filtrar as comunidades por nome (correspondência de subcadeia)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;plantuml.kg.edgeTypes&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;em&amp;gt;(todos)&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tipos de arestas separados por vírgulas:`EXTRACTED`, &amp;lt;code&amp;gt;INFERRED&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;AMBIGUOUS&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;plantuml.kg.minConfidence&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;0.0&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Limite mínima de confiança para as arestas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;plantuml.kg.maxNodes&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;em&amp;gt;(ilimitado)&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Número máximo de nós a exibir&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;plantuml.kg.nodeTypes&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;em&amp;gt;(todos)&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tipos de nós separados por vírgulas (ex.&amp;lt;code&amp;gt;class&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;code&amp;lt;/code&amp;gt;)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;plantuml.kg.outputDir&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;diagrams/knowledge-graph&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Diretório de saída para os arquivos`.puml` et &amp;lt;code&amp;gt;.png&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_pipeline_completo_em_um_workflow_gradle&amp;quot;&amp;gt;O pipeline completo em um workflow Gradle&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_tipo_de_workflow&amp;quot;&amp;gt;Tipo de workflow&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/workflow-complet.svg&amp;quot; alt=&amp;quot;workflow complet&amp;quot; width=&amp;quot;618&amp;quot; height=&amp;quot;714&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_integração_no_ciclo_de_desenvolvimento&amp;quot;&amp;gt;Integração no ciclo de desenvolvimento&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O pipeline integra-se naturalmente nas etapas-chave do desenvolvimento:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 5) ]

@startuml
skinparam backgroundColor #FEFEFE

state &amp;quot;Desenvolvimento&amp;quot; as dev
state &amp;quot;Extraction
^^^^^
 Syntax Error? (Assumed diagram type: state)

@startuml
skinparam backgroundColor #FEFEFE

state &amp;quot;Desenvolvimento&amp;quot; as dev
state &amp;quot;Extraction
graphify . --no-viz&amp;quot; as extract
state &amp;quot;Geração
./gradlew generateKnowledgeGraphDiagram&amp;quot; as generate
state &amp;quot;Commit
diagramas versionados&amp;quot; as commit

[*] --&amp;amp;gt; dev
dev --&amp;amp;gt; extract : Code modifié
extract --&amp;amp;gt; generate : graph.json à jour
generate --&amp;amp;gt; commit : .puml + .png générés
commit --&amp;amp;gt; dev : Diagrammes dans le repo

note right of extract
  Quasi instantané sur du code Kotlin
  (tree-sitter, pas de LLM)
end note

note right of generate
  Déterministe
  Pas de LLM
  Résultat reproductible
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_atualização_incremental&amp;quot;&amp;gt;Atualização incremental&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando o código muda, não reconstruímos todo o grafo do zero :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Mise à jour incrémentale (fichiers modifiés uniquement)
graphify . --update

# Puis régénérer les diagrammes
./gradlew generateKnowledgeGraphDiagram&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;--update`reextraído apenas os arquivos modificados (detectados por SHA256 em`graphify-out/cache/&amp;lt;/code&amp;gt;). Em código Kotlin, é quase instantâneo porque o tree-sitter trabalha localmente sem chamada ao LLM.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_dogfooding_o_plugin_se_documenta_por_si_mesmo&amp;quot;&amp;gt;Dogfooding: o plugin se documenta por si mesmo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plugin PlantUML existe para transformar prompts em diagramas. Ele também pode transformar o Knowledge Graph da sua &amp;lt;em&amp;gt;próprio&amp;lt;/em&amp;gt; codebase em diagramas de documentação. É &amp;lt;em&amp;gt;dogfooding&amp;lt;/em&amp;gt;: o plugin consome o seu próprio serviço.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package &amp;quot;Pipeline normal
(utilizador → diagramas)&amp;quot; as normal {
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package &amp;quot;Pipeline normal
(utilizador → diagramas)&amp;quot; as normal {
    [Fichier .prompt] as prompt
    [LlmService\n+ ApiKeyPool] as llm1
    [ProcessPlantumlPromptsTask] as task1
    [Diagramme PNG] as out1

    prompt --&amp;amp;gt; task1
    task1 --&amp;amp;gt; llm1
    llm1 --&amp;amp;gt; out1
}

package &amp;quot;Grafo de Conhecimento do Pipeline\n(determinista, sem LLM)&amp;quot; as kg {
    [graphify-out/graph.json] as kgjson
    [KnowledgeGraphParser] as parser
    [KnowledgeGraphRenderer] as renderer
    [PlantumlService] as ps
    [Diagramme PNG\n(documentation du plugin)] as out2

    kgjson --&amp;amp;gt; parser
    parser --&amp;amp;gt; renderer
    renderer --&amp;amp;gt; ps
    ps --&amp;amp;gt; out2
}

package &amp;quot;Pipeline Dogfooding\n(LLM → documentação do plugin)&amp;quot; as dogfood {
    [GraphifyPromptAdapter] as gpa
    [Fichiers .prompt\nauto-générés] as auto_prompt
    [LlmService\n+ ApiKeyPool] as llm2
    [ProcessPlantumlPromptsTask] as task2
    [Diagramme PNG\n(documentation LLM)] as out3

    kgjson --&amp;amp;gt; gpa
    gpa --&amp;amp;gt; auto_prompt
    auto_prompt --&amp;amp;gt; task2
    task2 --&amp;amp;gt; llm2
    llm2 --&amp;amp;gt; out3
}

note &amp;quot;Mesmo LlmService, mesmo ApiKeyPool,
mesmo PlantumlService
— ZERO duplicação&amp;quot; as N

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Tarefas Gradle associadas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3334%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;tarefa&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;LLM ?&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Descrição&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;generateKnowledgeGraphDiagram&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Não&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Transforma`graph.json`em PlantUML (determinístico)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;generateDiagramDocs&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sim&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;gere alguns`.prompt`a partir do grafo, os trata via LLM (dogfooding)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Documentation déterministe (rapide, pas de LLM)
./gradlew generateKnowledgeGraphDiagram

# Documentation LLM (plus riche, consomme des tokens)
./gradlew generateDiagramDocs&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_é_determinístico_e_por_que_isso_importa&amp;quot;&amp;gt;Por que é determinístico (e por que isso importa)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O ponto-chave do pipeline`generateKnowledgeGraphDiagram`:&amp;lt;strong&amp;gt;ele não chama nenhum LLM&amp;lt;/strong&amp;gt;. A transformação`graph.json`→ PlantUML é uma função pura.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

rectangle &amp;quot;Pipeline determinista
(generateKnowledgeGraphDiagram)&amp;quot; as det {
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

rectangle &amp;quot;Pipeline determinista
(generateKnowledgeGraphDiagram)&amp;quot; as det {
    [graph.json] as json
    [KnowledgeGraphParser] as parser
    [KnowledgeGraphRenderer] as renderer
    [PlantUML code] as puml

    json --&amp;amp;gt; parser : parse
    parser --&amp;amp;gt; renderer : KnowledgeGraph
    renderer --&amp;amp;gt; puml : render (fonction pure)
}

rectangle &amp;quot;Pipeline LLM
(processPlantumlPrompts)&amp;quot; as llm {
    [.prompt] as prompt
    [LlmService] as llmSvc
    [ChatModel] as model
    [PlantUML code] as puml2

    prompt --&amp;amp;gt; llmSvc
    llmSvc --&amp;amp;gt; model : API call
    model --&amp;amp;gt; puml2 : réponse non-déterministe
}

note bottom of det
  Même entrée → même sortie
  Pas de latence réseau
  Pas de coût en tokens
  Reproductible en CI
end note

note bottom of llm
  Même entrée → sortie variable
  Latence réseau (1-5s)
  Coût en tokens
  Nécessite une clave API
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os benefícios concretos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;vantagem&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Impacto&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Reproducibilidade&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;mesmo`graph.json`→ mesmo diagrama. Exatamente. Sempre.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Custo zero&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sem chamada LLM = sem tokens = sem fatura da API.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Zero latência&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Parse + render demora ~100ms, não 1-5 segundos.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Compatível CI&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nenhuma chave API necessária. Nenhum teste flaky devido a uma resposta LLM variável.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Versionável&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Le &amp;lt;code&amp;gt;.puml`gerado é texto. Pode`diff&amp;lt;/code&amp;gt;, o revisor no PR, versioná-lo no Git.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_diagramas_do_próprio_pipeline&amp;quot;&amp;gt;Diagramas do próprio pipeline&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para fechar o ciclo, aqui está o diagrama do pipeline como seria gerado pelo plugin :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/pipeline-sequence.svg&amp;quot; alt=&amp;quot;pipeline sequence&amp;quot; width=&amp;quot;1804&amp;quot; height=&amp;quot;554&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_integração_na_governança_do_projeto&amp;quot;&amp;gt;Integração na governança do projeto&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No nosso projeto, este pipeline integra-se numa estratégia de gestão de contexto EAGER/LAZY para o agente IA. O Knowledge Graph substitui a documentação de arquitetura manual por um grafo estruturado, consultável e auto-atualizado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 16) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package &amp;quot;EAGER\n(sempre carregado)&amp;quot; as eager {
    [PROMPT_REPRISE.adoc\n(contexte session)] as pr
    [INDEX.adoc\n(vue d&amp;#39;ensemble)] as idx
    [GRAPH_REPORT.adoc\n(~50 lignes)] as gr
}

package &amp;quot;LAZY — Estratégia\n(sob demanda)&amp;quot; as lazy_strat {
    [Méthodologies] as meth
    [Archives sessions] as sessions
}

package &amp;quot;LAZY — Graphify
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package &amp;quot;EAGER\n(sempre carregado)&amp;quot; as eager {
    [PROMPT_REPRISE.adoc\n(contexte session)] as pr
    [INDEX.adoc\n(vue d&amp;#39;ensemble)] as idx
    [GRAPH_REPORT.adoc\n(~50 lignes)] as gr
}

package &amp;quot;LAZY — Estratégia\n(sob demanda)&amp;quot; as lazy_strat {
    [Méthodologies] as meth
    [Archives sessions] as sessions
}

package &amp;quot;LAZY — Graphify
(consultas direcionadas)&amp;quot; as lazy_graph {
    [graph.json] as gj
    [Queries\n(query/path/explain)] as queries
}

package &amp;quot;Pipeline PlantUML
gerado automaticamente&amp;quot; as puml {
    [generateKnowledgeGraphDiagram] as kgTask
    [generateDiagramDocs] as ddTask
    [Diagrammes PNG] as diagrams
}

Agent --&amp;amp;gt; eager : Lit en début de session
Agent ..&amp;amp;gt; lazy_strat : Charge si type détecté
Agent ..&amp;amp;gt; lazy_graph : Query si besoin structurel
kgTask --&amp;amp;gt; diagrams : Déterministe
ddTask --&amp;amp;gt; diagrams : Via LLM

note right of gr
  Condensé du Knowledge Graph
  God nodes + communautés
  Remplace ECOSYSTEM_OVERVIEW
  (225 lignes → 50 lignes)
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O casamento dos dois sistemas é complementar :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A estratégia trata do QUANDO e do COMO&amp;lt;/strong&amp;gt;— governança, workflow, arquivamento&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Graphify gerencia o quê e o onde&amp;lt;/strong&amp;gt;— estrutura do código, relações, consultas direcionadas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ A estratégia de sessão gerencia o&amp;lt;strong&amp;gt;QUANDO&amp;lt;/strong&amp;gt; et le &amp;lt;strong&amp;gt;como&amp;lt;/strong&amp;gt;(governança, workflow, limites), Graphify gerencia o&amp;lt;strong&amp;gt;O QUE&amp;lt;/strong&amp;gt; et le &amp;lt;strong&amp;gt;ONDE&amp;lt;/strong&amp;gt;(estrutura do código, relações, consultas direcionadas). O pipeline PlantUML gerencia o&amp;lt;strong&amp;gt;COM O QUE&amp;lt;/strong&amp;gt;(diagramas determinísticos, versionados, sempre atualizados). &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_preparação_lista_de_verificação_em_5_minutos&amp;quot;&amp;gt;Preparação: lista de verificação em 5 minutos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3334%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;#&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Etapa&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;encomenda&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Instalar Graphify&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;uv tool install graphifyy&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Configurar as exclusões&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Criar`.graphifyignore`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Extrair o Knowledge Graph&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;graphify . --no-viz&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;4&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Gerar os diagramas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;./gradlew generateKnowledgeGraphDiagram&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Fazer commit dos resultados&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;git add diagrams/knowledge-graph/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Script complet en 5 commandes
uv tool install graphifyy
cat &amp;amp;gt; .graphifyignore &amp;amp;lt;&amp;amp;lt; &amp;#39;EOF&amp;#39;
*-context.yml
*.env
build/
.gradle/
EOF
graphify . --no-viz
./gradlew generateKnowledgeGraphDiagram
git add graphify-out/GRAPH_REPORT.adoc graphify-out/graph.json diagrams/knowledge-graph/&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_armadilhas_e_mitigações&amp;quot;&amp;gt;Armadilhas e mitigações&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3334%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;armadilha&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Descrição&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;mitigação&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Grafo muito denso&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Um grande projeto gera centenas de nós ilegíveis&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Utilizar`-Pplantuml.kg.maxNodes=30`e filtrar por comunidade&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Exclusões demasiado largas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Muitos arquivos em`.graphifyignore`reduce o valor do grafo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Começar por excluir apenas credentials e build&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Direção das setas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;As arestas INFERRED podem ter uma direção ambígua&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Filtrar por`-Pplantuml.kg.edgeTypes=EXTRACTED`para as relações certas apenas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Grafo obsoleto&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O código muda, mas o grafo não é reconstruído&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Utilizar`graphify . --update`régulièrement ou o hook git`graphify hook install`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Custo atualizado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;As atualizações incrementais são quase gratuitas (tree-sitter local)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Apenas os docs (&amp;lt;code&amp;gt;.adoc&amp;lt;/code&amp;gt;) consumem tokens LLM&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_que_se_obtém_no_final&amp;quot;&amp;gt;O que se obtém no final&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;ANTES&amp;quot; as avant {
    card &amp;quot;Diagramas desenhados à mão
Sempre obsoletos
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;ANTES&amp;quot; as avant {
    card &amp;quot;Diagramas desenhados à mão
Sempre obsoletos
Ninguém os atualiza&amp;quot; as av1 #FDEDEC
}

rectangle &amp;quot;depois&amp;quot; as apres {
    card &amp;quot;Diagramas gerados automaticamente
Sempre atualizados com o código
Versionados no Git
Determinísticos e reproduzíveis&amp;quot; as ap1 #E8F8E8
}

avant --&amp;amp;gt; apres : graphify . --no-viz\n+ ./gradlew generateKnowledgeGraphDiagram

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os benefícios concretos :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Benefício&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Detalhe&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Documentação sempre atualizada&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Os diagramas refletem o código atual, não um instantâneo manual&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Zero esforço de manutenção&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Os diagramas são regenerados a cada build&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Redução da dívida técnica&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Não precisa mais manter os diagramas à mão&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Contexto visual para os novos&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Um novo desenvolvedor entende a arquitetura olhando os diagramas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Potencial de fine-tuning&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Os pares (subgrafo → diagrama) são exemplos de treinamento de IA&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Validação automática&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`PlantumlService.validateSyntax()`verifica cada diagrama gerado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Onboarding rápido&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;5 diagramas = visão completa da arquitetura&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;amp;lt;/think&amp;amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O &amp;lt;em&amp;gt;porquê&amp;lt;/em&amp;gt; : diagramas sempre atualizados. O &amp;lt;em&amp;gt;como&amp;lt;/em&amp;gt; : dois comandos em um pipeline Gradle.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_links&amp;quot;&amp;gt;Links&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/nicholasgasior/graphify&amp;quot;&amp;gt;Graphify no GitHub&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/cheroliv/plantuml-gradle&amp;quot;&amp;gt;Plugin Gradle PlantUML&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;/blog/2026/0104_fg_command_super_tips_post.html&amp;quot;&amp;gt;A super dica do comando fg&amp;lt;/a&amp;gt;— artigo anterior sobre o workflow terminal&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Integrar Graphify em um fluxo de trabalho Gradle: do Knowledge Graph ao diagrama PlantUML em um único comando</title>
            <link >https://cheroliv.com//blog/pilot-batch/0105_integrer_graphify_workflow_gradle_post.html</link>
            <pubDate>Sun, 19 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/pilot-batch/0105_integrer_graphify_workflow_gradle_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_diagramas_sempre_atrasados_em_relação_ao_código&amp;quot;&amp;gt;O problema: diagramas sempre atrasados em relação ao código&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_um_pipeline_determinístico_grafo_de_conhecimento_plantuml&amp;quot;&amp;gt;A solução : um pipeline determinístico Grafo de Conhecimento → PlantUML&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_1_instalar_graphify_e_extrair_o_knowledge_graph&amp;quot;&amp;gt;Passo 1: Instalar Graphify e extrair o Knowledge Graph&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalação&amp;quot;&amp;gt;Instalação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configurar_as_exclusões&amp;quot;&amp;gt;Configurar as exclusões&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_extrair_o_knowledge_graph&amp;quot;&amp;gt;Extrair o Knowledge Graph&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_2_o_plugin_gradle_transforma_o_knowledge_graph_em_plantuml&amp;quot;&amp;gt;Etapa 2 : O plugin Gradle transforma o Knowledge Graph em PlantUML&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_do_pipeline&amp;quot;&amp;gt;Arquitetura do pipeline&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_os_componentes_internos&amp;quot;&amp;gt;Os componentes internos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_regras_de_renderização&amp;quot;&amp;gt;Regras de renderização&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_3_uso_diário&amp;quot;&amp;gt;Passo 3: Uso diário&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_completo&amp;quot;&amp;gt;diagrama completo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_filtrar_por_comunidade&amp;quot;&amp;gt;Filtrar por comunidade&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_filtrar_por_tipo_de_aresta&amp;quot;&amp;gt;Filtrar por tipo de aresta&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_filtrar_por_tipo_de_nó_e_confiança&amp;quot;&amp;gt;Filtrar por tipo de nó e confiança&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diretório_de_saída_personalizado&amp;quot;&amp;gt;Diretório de saída personalizado&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referência_completa_das_propriedades&amp;quot;&amp;gt;Referência completa das propriedades&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_pipeline_completo_em_um_fluxo_de_trabalho_gradle&amp;quot;&amp;gt;O pipeline completo em um fluxo de trabalho Gradle&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_tipo_de_workflow&amp;quot;&amp;gt;tipo de workflow&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_integração_no_ciclo_de_desenvolvimento&amp;quot;&amp;gt;Integração no ciclo de desenvolvimento&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_atualização_incremental&amp;quot;&amp;gt;Atualização incremental&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_dogfooding_o_plugin_se_documenta_sozinho&amp;quot;&amp;gt;O dogfooding : o plugin se documenta sozinho&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_isso_é_determinístico_e_por_que_isso_importa&amp;quot;&amp;gt;Por que isso é determinístico (e por que isso importa)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagramas_do_próprio_pipeline&amp;quot;&amp;gt;Diagramas do próprio pipeline&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_integração_na_governança_do_projeto&amp;quot;&amp;gt;Integração na governança do projeto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configuração_lista_de_verificação_em_5_minutos&amp;quot;&amp;gt;Configuração: lista de verificação em 5 minutos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_armadilhas_e_mitigações&amp;quot;&amp;gt;Armadilhas e mitigações&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_se_obtém_no_final&amp;quot;&amp;gt;O que se obtém no final&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_links&amp;quot;&amp;gt;Links&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 12 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Seu codebase está crescendo. As dependências entre módulos estão se multiplicando. A documentação de arquitetura torna-se obsoleta antes mesmo de ser escrita. E se o seu build Gradle pudesse &amp;lt;em&amp;gt;automaticamente&amp;lt;/em&amp;gt; gerar diagramas atualizados a partir da estrutura real do código? É exatamente isso que o pipeline Graphify + PlantUML Gradle Plugin faz:`graphify . --no-viz`extrai o Knowledge Graph`./gradlew generateKnowledgeGraphDiagram`o transforma em diagramas PlantUML. Zero LLM, zero manual, 100% determinístico.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_problema_diagramas_sempre_atrasados_em_relação_ao_código&amp;quot;&amp;gt;O problema: diagramas sempre atrasados em relação ao código&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Todo projeto que ultrapassa algumas milhares de linhas conhece esse síndrome :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Desenha-se um diagrama de arquitetura no início do projeto&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O código evolui, as dependências mudam&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O diagrama torna-se uma mentira decorativa&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ninguém o atualiza porque é penoso&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Os recém-chegados baseiam-se nisso e cometem erros.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/probleme-diagrammes-obsoletes.svg&amp;quot; alt=&amp;quot;probleme diagrammes obsoletes&amp;quot; width=&amp;quot;824&amp;quot; height=&amp;quot;418&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A questão não é &amp;lt;em&amp;gt;precisa de diagramas ?&amp;lt;/em&amp;gt; — todo mundo sabe que sim. A questão é :&amp;lt;strong&amp;gt;quem os mantém atualizados?&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A resposta: ninguém. Exceto se for automático.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_um_pipeline_determinístico_grafo_de_conhecimento_plantuml&amp;quot;&amp;gt;A solução : um pipeline determinístico Grafo de Conhecimento → PlantUML&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O princípio é simples: em vez de desenhar os diagramas à mão, nós os&amp;lt;strong&amp;gt;gerado a partir da estrutura real do código&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

actor Développeur
component &amp;quot;Graphify
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

actor Développeur
component &amp;quot;Graphify
(pip install graphifyy)&amp;quot; as Graphify
collections &amp;quot;graphify-out/graph.json
(Grafo de Conhecimento)&amp;quot; as KGJSON
component &amp;quot;PlantUML Gradle Plugin\n(generateKnowledgeGraphDiagram)&amp;quot; as Plugin
component &amp;quot;KnowledgeGraphParser&amp;quot; as Parser
component &amp;quot;Renderizador de Grafo de Conhecimento&amp;quot; as Renderer
component &amp;quot;PlantumlService
(validação + renderização PNG)&amp;quot; as PS
collections &amp;quot;diagrams/knowledge-graph/\n(.puml + .png)&amp;quot; as Output

Développeur --&amp;amp;gt; Graphify : graphify . --no-viz
Graphify --&amp;amp;gt; KGJSON : extrait la structure\ndu code source
Développeur --&amp;amp;gt; Plugin : ./gradlew generateKnowledgeGraphDiagram
Plugin --&amp;amp;gt; Parser : parse(graph.json)
Parser --&amp;amp;gt; Plugin : KnowledgeGraph\n(noeuds + arêtes + communautés)
Plugin --&amp;amp;gt; Renderer : render(graph, filters)
Renderer --&amp;amp;gt; Plugin : code PlantUML\ndéterministe
Plugin --&amp;amp;gt; PS : validateSyntax + generateImage
PS --&amp;amp;gt; Output : .puml + .png

note bottom of Plugin
  Pipeline DÉTERMINISTE
  Aucun appel LLM
  Résultat reproductible
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Dois comandos. É só isso.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Étape 1 : extraire le Knowledge Graph
graphify . --no-viz

# Étape 2 : générer les diagrammes PlantUML
./gradlew generateKnowledgeGraphDiagram&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O resultado? Arquivos`.puml` et &amp;lt;code&amp;gt;.png`em`diagrams/knowledge-graph/&amp;lt;/code&amp;gt;, versionados no Git, sempre atualizados com o código.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_passo_1_instalar_graphify_e_extrair_o_knowledge_graph&amp;quot;&amp;gt;Passo 1: Instalar Graphify e extrair o Knowledge Graph&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_instalação&amp;quot;&amp;gt;Instalação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Graphify é uma ferramenta Python que analisa seu codebase e constrói um grafo de conhecimento estruturado :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Méthode recommandée
uv tool install graphifyy &amp;amp;amp;&amp;amp;amp; graphify install --platform opencode

# Alternative avec pip
pip install graphifyy &amp;amp;amp;&amp;amp;amp; graphify install --platform opencode&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_configurar_as_exclusões&amp;quot;&amp;gt;Configurar as exclusões&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Criar um arquivo`.graphifyignore`na raiz do projeto para excluir os arquivos que não fazem parte da lógica de negócio:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-gitignore hljs&amp;quot; data-lang=&amp;quot;gitignore&amp;quot;&amp;gt;# Secrets — JAMAIS dans le graphe
*-context.yml
*.env

# Fichiers générés
build/
.gradle/

# Tests fonctionnels
src/functionalTest/&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_extrair_o_knowledge_graph&amp;quot;&amp;gt;Extrair o Knowledge Graph&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;graphify . --no-viz&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A flag`--no-viz`Pula a geração HTML (inutil em um pipeline Gradle). O resultado é um arquivo`graphify-out/graph.json`contendo :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Nós&amp;lt;/strong&amp;gt;: classes, funções, ficheiros — com o seu tipo e comunidade&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;arestas&amp;lt;/strong&amp;gt;: relações entre nós (EXTRACTED a partir do código, INFERRED pelo LLM)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Comunidades&amp;lt;/strong&amp;gt;: agrupamentos automáticos de nós ligados&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Exemplo de estrutura`graph.json`&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-json hljs&amp;quot; data-lang=&amp;quot;json&amp;quot;&amp;gt;{
  &amp;quot;nodes&amp;quot;: [
    {&amp;quot;id&amp;quot;: &amp;quot;0&amp;quot;, &amp;quot;label&amp;quot;: &amp;quot;LlmService&amp;quot;, &amp;quot;file_type&amp;quot;: &amp;quot;code&amp;quot;, &amp;quot;community&amp;quot;: 0},
    {&amp;quot;id&amp;quot;: &amp;quot;1&amp;quot;, &amp;quot;label&amp;quot;: &amp;quot;ApiKeyPool&amp;quot;, &amp;quot;file_type&amp;quot;: &amp;quot;code&amp;quot;, &amp;quot;community&amp;quot;: 0},
    {&amp;quot;id&amp;quot;: &amp;quot;2&amp;quot;, &amp;quot;label&amp;quot;: &amp;quot;PlantumlService&amp;quot;, &amp;quot;file_type&amp;quot;: &amp;quot;code&amp;quot;, &amp;quot;community&amp;quot;: 1}
  ],
  &amp;quot;links&amp;quot;: [
    {&amp;quot;source&amp;quot;: &amp;quot;1&amp;quot;, &amp;quot;target&amp;quot;: &amp;quot;0&amp;quot;, &amp;quot;relation&amp;quot;: &amp;quot;uses&amp;quot;, &amp;quot;confidence&amp;quot;: &amp;quot;EXTRACTED&amp;quot;, &amp;quot;weight&amp;quot;: 0.9},
    {&amp;quot;source&amp;quot;: &amp;quot;0&amp;quot;, &amp;quot;target&amp;quot;: &amp;quot;2&amp;quot;, &amp;quot;relation&amp;quot;: &amp;quot;calls&amp;quot;, &amp;quot;confidence&amp;quot;: &amp;quot;INFERRED&amp;quot;, &amp;quot;weight&amp;quot;: 0.7}
  ]
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O código fonte (&amp;lt;code&amp;gt;.kt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.java&amp;lt;/code&amp;gt;) é analisado localmente pelo tree-sitter,&amp;lt;strong&amp;gt;irrecorrível LLM&amp;lt;/strong&amp;gt;. Apenas os arquivos de documentação (&amp;lt;code&amp;gt;.adoc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.md&amp;lt;/code&amp;gt;) exigem uma chamada LLM para extração semântica. Portanto`--update`em código Kotlin é quase instantâneo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_etapa_2_o_plugin_gradle_transforma_o_knowledge_graph_em_plantuml&amp;quot;&amp;gt;Etapa 2 : O plugin Gradle transforma o Knowledge Graph em PlantUML&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_arquitetura_do_pipeline&amp;quot;&amp;gt;Arquitetura do pipeline&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plugin`com.cheroliv.plantuml`inclui uma tarefa`generateKnowledgeGraphDiagram`que transforma`graph.json`em diagramas PlantUML de maneira&amp;lt;strong&amp;gt;totalmente determinista&amp;lt;/strong&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/architecture-pipeline-kg.svg&amp;quot; alt=&amp;quot;architecture pipeline kg&amp;quot; width=&amp;quot;1210&amp;quot; height=&amp;quot;450&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_os_componentes_internos&amp;quot;&amp;gt;Os componentes internos&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Componente&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Papel&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;KnowledgeGraphParser&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Analisar`graph.json`suporta 3 formatos: graphify nativo (&amp;lt;code&amp;gt;nodes&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;), legado (&amp;lt;code&amp;gt;communities&amp;lt;/code&amp;gt;), flat. Resolve os IDs numéricos como labels.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;KnowledgeGraphRenderer&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Transforma um de forma determinista`KnowledgeGraph`em código PlantUML. Grupos por tipo, comunidades em pacotes, legenda automática.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;GenerateKnowledgeGraphDiagramTask&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tarefa Gradle que orquestra: parse → render → validate → PNG. Configurável via propriedades do Gradle.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;kgmodels.kt&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Modelos de dados :`KnowledgeGraph`, &amp;lt;code&amp;gt;KnowledgeGraphNode&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;KnowledgeGraphEdge&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;KnowledgeGraphCommunity&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;EdgeType&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;PlantumlService&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Validação sintática + renderização PNG (reutilizado por todas as tarefas do plugin).&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_regras_de_renderização&amp;quot;&amp;gt;Regras de renderização&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O renderer aplica convenções visuais determinísticas:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3334%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Tipo de aresta&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Notação PlantUML&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Significado&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;extraído&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;-&amp;amp;#8594;&amp;lt;/code&amp;gt;(traço cheio, preto)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Relação extraída do código fonte (certeza)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;deduzido&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;..&amp;amp;gt;&amp;lt;/code&amp;gt;(traço pontilhado)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Relação inferida pelo LLM (pontuação de confiança)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;AMBÍGUO&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;--x&amp;lt;/code&amp;gt;(traço vermelho pontilhado)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;relação ambígua (a verificar)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;As comunidades são renderizadas como pacotes PlantUML com uma paleta de cores automática.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_passo_3_uso_diário&amp;quot;&amp;gt;Passo 3: Uso diário&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_completo&amp;quot;&amp;gt;diagrama completo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Générer le diagramme du Knowledge Graph complet
./gradlew generateKnowledgeGraphDiagram&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Saída :`diagrams/knowledge-graph/knowledge-graph-full.puml`+&amp;lt;code&amp;gt;.png&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_filtrar_por_comunidade&amp;quot;&amp;gt;Filtrar por comunidade&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Une seule communauté
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.community=community_0

# Limiter le nombre de noeuds (lisibilité)
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.community=community_0 \
  -Pplantuml.kg.maxNodes=15&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_filtrar_por_tipo_de_aresta&amp;quot;&amp;gt;Filtrar por tipo de aresta&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Uniquement les relations certaines (EXTRACTED)
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.edgeTypes=EXTRACTED

# Relations certaines + inférées
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.edgeTypes=EXTRACTED,INFERRED&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_filtrar_por_tipo_de_nó_e_confiança&amp;quot;&amp;gt;Filtrar por tipo de nó e confiança&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Uniquement les classes de code
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.nodeTypes=code

# Seuil de confiance minimum (pour les INFERRED)
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.minConfidence=0.7&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diretório_de_saída_personalizado&amp;quot;&amp;gt;Diretório de saída personalizado&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.outputDir=docs/architecture&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_referência_completa_das_propriedades&amp;quot;&amp;gt;Referência completa das propriedades&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3334%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Propriedade&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;defeito&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Descrição&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;plantuml.kg.community&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;em&amp;gt;(todas)&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Filtrar comunidades por nome (correspondência de substring)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;plantuml.kg.edgeTypes&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;em&amp;gt;(todos)&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tipos de arestas separados por vírgulas :`EXTRACTED`, &amp;lt;code&amp;gt;INFERRED&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;AMBIGUOUS&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;plantuml.kg.minConfidence&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;0.0&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Limite mínima de confiança para as arestas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;plantuml.kg.maxNodes&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;(ilimitado)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Número máximo de nós para exibir&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;plantuml.kg.nodeTypes&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;(todos)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tipos de nós separados por vírgulas (ex.&amp;lt;code&amp;gt;class&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;code&amp;lt;/code&amp;gt;)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;plantuml.kg.outputDir&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;diagrams/knowledge-graph&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Diretório de saída para os arquivos`.puml` et &amp;lt;code&amp;gt;.png&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_pipeline_completo_em_um_fluxo_de_trabalho_gradle&amp;quot;&amp;gt;O pipeline completo em um fluxo de trabalho Gradle&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_tipo_de_workflow&amp;quot;&amp;gt;tipo de workflow&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/workflow-complet.svg&amp;quot; alt=&amp;quot;workflow complet&amp;quot; width=&amp;quot;618&amp;quot; height=&amp;quot;714&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_integração_no_ciclo_de_desenvolvimento&amp;quot;&amp;gt;Integração no ciclo de desenvolvimento&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O pipeline se integra naturalmente nas etapas-chave do desenvolvimento:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 5) ]

@startuml
skinparam backgroundColor #FEFEFE

state &amp;quot;Desenvolvimento&amp;quot; as dev
state &amp;quot;Extração
^^^^^
 Syntax Error? (Assumed diagram type: state)

@startuml
skinparam backgroundColor #FEFEFE

state &amp;quot;Desenvolvimento&amp;quot; as dev
state &amp;quot;Extração
graphify . --no-viz&amp;quot; as extract
state &amp;quot;Geração
./gradlew generateKnowledgeGraphDiagram&amp;quot; as generate
state &amp;quot;Commit
diagramas versionados&amp;quot; as commit

[*] --&amp;amp;gt; dev
dev --&amp;amp;gt; extract : Code modifié
extract --&amp;amp;gt; generate : graph.json à jour
generate --&amp;amp;gt; commit : .puml + .png générés
commit --&amp;amp;gt; dev : Diagrammes dans le repo

note right of extract
  Quasi instantané sur du code Kotlin
  (tree-sitter, pas de LLM)
end note

note right of generate
  Déterministe
  Pas de LLM
  Résultat reproductible
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_atualização_incremental&amp;quot;&amp;gt;Atualização incremental&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando o código muda, não reconstruímos todo o grafo do zero :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Mise à jour incrémentale (fichiers modifiés uniquement)
graphify . --update

# Puis régénérer les diagrammes
./gradlew generateKnowledgeGraphDiagram&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;--update`reextrai apenas os arquivos modificados (detectados por SHA256 em`graphify-out/cache/&amp;lt;/code&amp;gt;). No código Kotlin, é quase instantâneo porque o tree-sitter trabalha localmente sem chamada LLM.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_dogfooding_o_plugin_se_documenta_sozinho&amp;quot;&amp;gt;O dogfooding : o plugin se documenta sozinho&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plugin PlantUML existe para transformar prompts em diagramas. Ele também pode transformar o Knowledge Graph da sua &amp;lt;em&amp;gt;própria&amp;lt;/em&amp;gt; codebase em diagramas de documentação. Isso é &amp;lt;em&amp;gt;dogfooding&amp;lt;/em&amp;gt; : o plugin consome seu próprio serviço.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 16) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package &amp;quot;Pipeline normal\n(usuário → diagramas)&amp;quot; as normal {
    [Fichier .prompt] as prompt
    [LlmService\n+ ApiKeyPool] as llm1
    [ProcessPlantumlPromptsTask] as task1
    [Diagramme PNG] as out1

    prompt --&amp;amp;gt; task1
    task1 --&amp;amp;gt; llm1
    llm1 --&amp;amp;gt; out1
}

package &amp;quot;Pipeline Knowledge Graph
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package &amp;quot;Pipeline normal\n(usuário → diagramas)&amp;quot; as normal {
    [Fichier .prompt] as prompt
    [LlmService\n+ ApiKeyPool] as llm1
    [ProcessPlantumlPromptsTask] as task1
    [Diagramme PNG] as out1

    prompt --&amp;amp;gt; task1
    task1 --&amp;amp;gt; llm1
    llm1 --&amp;amp;gt; out1
}

package &amp;quot;Pipeline Knowledge Graph
(determinístico, sem LLM)&amp;quot; as kg {
    [graphify-out/graph.json] as kgjson
    [KnowledgeGraphParser] as parser
    [KnowledgeGraphRenderer] as renderer
    [PlantumlService] as ps
    [Diagramme PNG\n(documentation du plugin)] as out2

    kgjson --&amp;amp;gt; parser
    parser --&amp;amp;gt; renderer
    renderer --&amp;amp;gt; ps
    ps --&amp;amp;gt; out2
}

package &amp;quot;Pipeline Dogfooding
(LLM → documentação do plugin)&amp;quot; as dogfood {
    [GraphifyPromptAdapter] as gpa
    [Fichiers .prompt\nauto-générés] as auto_prompt
    [LlmService\n+ ApiKeyPool] as llm2
    [ProcessPlantumlPromptsTask] as task2
    [Diagramme PNG\n(documentation LLM)] as out3

    kgjson --&amp;amp;gt; gpa
    gpa --&amp;amp;gt; auto_prompt
    auto_prompt --&amp;amp;gt; task2
    task2 --&amp;amp;gt; llm2
    llm2 --&amp;amp;gt; out3
}

note &amp;quot;Mesmo LlmService, mesmo ApiKeyPool,
mesmo PlantumlService
— ZERO duplicação&amp;quot; as N

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Tarefas do Gradle associadas:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3334%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Tarefa&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;LLM ?&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Descrição&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;generateKnowledgeGraphDiagram&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Não&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Transforme`graph.json`em PlantUML (determinístico)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;generateDiagramDocs&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sim&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Gere alguns`.prompt`desde o grafo, ele os trata via LLM (dogfooding)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Documentation déterministe (rapide, pas de LLM)
./gradlew generateKnowledgeGraphDiagram

# Documentation LLM (plus riche, consomme des tokens)
./gradlew generateDiagramDocs&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_isso_é_determinístico_e_por_que_isso_importa&amp;quot;&amp;gt;Por que isso é determinístico (e por que isso importa)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O ponto-chave do pipeline`generateKnowledgeGraphDiagram`:&amp;lt;strong&amp;gt;Ele não chama nenhum LLM&amp;lt;/strong&amp;gt;. A transformação`graph.json`PlantUML é uma função pura.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

rectangle &amp;quot;Pipeline determinístico
(generateKnowledgeGraphDiagram)&amp;quot; as det {
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

rectangle &amp;quot;Pipeline determinístico
(generateKnowledgeGraphDiagram)&amp;quot; as det {
    [graph.json] as json
    [KnowledgeGraphParser] as parser
    [KnowledgeGraphRenderer] as renderer
    [PlantUML code] as puml

    json --&amp;amp;gt; parser : parse
    parser --&amp;amp;gt; renderer : KnowledgeGraph
    renderer --&amp;amp;gt; puml : render (fonction pure)
}

rectangle &amp;quot;Pipeline LLM\n(processPlantumlPrompts)&amp;quot; as llm {
    [.prompt] as prompt
    [LlmService] as llmSvc
    [ChatModel] as model
    [PlantUML code] as puml2

    prompt --&amp;amp;gt; llmSvc
    llmSvc --&amp;amp;gt; model : API call
    model --&amp;amp;gt; puml2 : réponse non-déterministe
}

note bottom of det
  Même entrée → même sortie
  Pas de latence réseau
  Pas de coût en tokens
  Reproductible en CI
end note

note bottom of llm
  Même entrée → sortie variable
  Latence réseau (1-5s)
  Coût en tokens
  Nécessite une clave API
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os benefícios concretos :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Vantagem&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Impacto&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Reprodutibilidade&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Mesmo`graph.json`→ mesmo diagrama. Exatamente. Todas as vezes.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Custo zero&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sem chamada LLM = sem tokens = sem fatura da API.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Zero latência&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Parse + render demora ~100ms, não 1-5 segundos.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Compatível CI&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nenhuma chave de API necessária. Nenhum teste flaky devido a uma resposta LLM variável.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;versionável&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Le &amp;lt;code&amp;gt;.puml`gerado é texto. Nós podemos`diff&amp;lt;/code&amp;gt;, o reviewer no PR, versioná-lo no Git.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_diagramas_do_próprio_pipeline&amp;quot;&amp;gt;Diagramas do próprio pipeline&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para fechar o laço, eis o diagrama do pipeline tal como seria gerado pelo plugin :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/pipeline-sequence.svg&amp;quot; alt=&amp;quot;pipeline sequence&amp;quot; width=&amp;quot;1819&amp;quot; height=&amp;quot;554&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_integração_na_governança_do_projeto&amp;quot;&amp;gt;Integração na governança do projeto&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em nosso projeto, este pipeline se integra em uma estratégia de gestão de contexto EAGER/LAZY para o agente IA. O Knowledge Graph substitui a documentação de arquitetura manual por um grafo estruturado, consultável e autoatualizado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package &amp;quot;EAGER
(sempre carregado)&amp;quot; as eager {
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package &amp;quot;EAGER
(sempre carregado)&amp;quot; as eager {
    [PROMPT_REPRISE.adoc\n(contexte session)] as pr
    [INDEX.adoc\n(vue d&amp;#39;ensemble)] as idx
    [GRAPH_REPORT.adoc\n(~50 lignes)] as gr
}

package &amp;quot;LAZY — Estratégia\n(sob demanda)&amp;quot; as lazy_strat {
    [Méthodologies] as meth
    [Archives sessions] as sessions
}

package &amp;quot;LAZY — Graphify
(consultas direcionadas)&amp;quot; as lazy_graph {
    [graph.json] as gj
    [Queries\n(query/path/explain)] as queries
}

package &amp;quot;Pipeline PlantUML
(gerado automaticamente)&amp;quot; as puml {
    [generateKnowledgeGraphDiagram] as kgTask
    [generateDiagramDocs] as ddTask
    [Diagrammes PNG] as diagrams
}

Agent --&amp;amp;gt; eager : Lit en début de session
Agent ..&amp;amp;gt; lazy_strat : Charge si type détecté
Agent ..&amp;amp;gt; lazy_graph : Query si besoin structurel
kgTask --&amp;amp;gt; diagrams : Déterministe
ddTask --&amp;amp;gt; diagrams : Via LLM

note right of gr
  Condensé du Knowledge Graph
  God nodes + communautés
  Remplace ECOSYSTEM_OVERVIEW
  (225 lignes → 50 lignes)
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A união dos dois sistemas é complementar :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A estratégia gerencia o QUANDO e o COMO&amp;lt;/strong&amp;gt;— governança, fluxo de trabalho, arquivamento&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Graphify trata o QUÊ e o ONDE&amp;lt;/strong&amp;gt;— estrutura do código, relações, consultas direcionadas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ A estratégia de sessão trata o&amp;lt;strong&amp;gt;quando&amp;lt;/strong&amp;gt; et le &amp;lt;strong&amp;gt;como&amp;lt;/strong&amp;gt;(governança, workflow, limites), Graphify gerencia o&amp;lt;strong&amp;gt;O que&amp;lt;/strong&amp;gt; et le &amp;lt;strong&amp;gt;onde&amp;lt;/strong&amp;gt;(estrutura do código, relações, consultas direcionadas). O pipeline PlantUML gerencia o&amp;lt;strong&amp;gt;COM O QUE&amp;lt;/strong&amp;gt;(diagramas determinísticos, versionados, sempre atualizados). &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_configuração_lista_de_verificação_em_5_minutos&amp;quot;&amp;gt;Configuração: lista de verificação em 5 minutos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3334%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;(Note: Since no French text was provided in the user&amp;amp;#8217;s input for translation, the output is an empty string as per the instruction to translate only the given fragment.)&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;etapa&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;encomenda&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Instalar Graphify&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;uv tool install graphifyy&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Configurar as exclusões&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Criar`.graphifyignore`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Extrair o Knowledge Graph&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;graphify . --no-viz&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;4&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Gerar os diagramas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;./gradlew generateKnowledgeGraphDiagram&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Confirmar os resultados&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;git add diagrams/knowledge-graph/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Script complet en 5 commandes
uv tool install graphifyy
cat &amp;amp;gt; .graphifyignore &amp;amp;lt;&amp;amp;lt; &amp;#39;EOF&amp;#39;
*-context.yml
*.env
build/
.gradle/
EOF
graphify . --no-viz
./gradlew generateKnowledgeGraphDiagram
git add graphify-out/GRAPH_REPORT.adoc graphify-out/graph.json diagrams/knowledge-graph/&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_armadilhas_e_mitigações&amp;quot;&amp;gt;Armadilhas e mitigações&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3334%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Armadilha&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Descrição&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;mitigação&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Gráfico muito denso&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Um grande projeto gera centenas de nós ilegíveis&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Usar`-Pplantuml.kg.maxNodes=30`e filtrar por comunidade&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Exclusões demasiado amplas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Demasiados arquivos em`.graphifyignore`reduz o valor do grafo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Começar por excluir apenas credentials e build&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Direção das setas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;As arestas INFERRED podem ter uma direção ambígua&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Filtrar por`-Pplantuml.kg.edgeTypes=EXTRACTED`para as relações certas apenas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Grafo obsoleto&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O código muda mas o grafo não é reconstruído&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Utilizar`graphify . --update`regularmente ou o hook do Git`graphify hook install`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Custo atualizado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;As atualizações incrementais são quase gratuitas (tree-sitter local)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Apenas os docs (&amp;lt;code&amp;gt;.adoc&amp;lt;/code&amp;gt;) consumem tokens LLM&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_que_se_obtém_no_final&amp;quot;&amp;gt;O que se obtém no final&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;antes&amp;quot; as avant {
    card &amp;quot;Diagramas desenhados à mão
Sempre obsoletos
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;antes&amp;quot; as avant {
    card &amp;quot;Diagramas desenhados à mão
Sempre obsoletos
Ninguém os atualiza&amp;quot; as av1 #FDEDEC
}

rectangle &amp;quot;DEPOIS&amp;quot; as apres {
    card &amp;quot;Diagramas gerados automaticamente
Sempre atualizado com o código
Versionados no Git
Determinísticos e reprodutíveis&amp;quot; as ap1 #E8F8E8
}

avant --&amp;amp;gt; apres : graphify . --no-viz\n+ ./gradlew generateKnowledgeGraphDiagram

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os benefícios concretos :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Benefício&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Detalhe&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Documentação sempre atualizada&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Os diagramas refletem o código atual, não um snapshot manual&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Zero esforço de manutenção&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Os diagramas se regeneram a cada build&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Redução da dívida técnica&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Não é mais necessário manter os diagramas manualmente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Contexto visual para os novos&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Um novo desenvolvedor compreende a arquitetura observando os diagramas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Ajuste fino potencial&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Os pares (subgrafo → diagrama) são exemplos de treinamento de IA&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Validação automática&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`PlantumlService.validateSyntax()`verifica cada diagrama gerado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Onboarding rápido&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;5 diagramas = visão completa da arquitetura&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ Quem tem um _porquê&amp;lt;/em&amp;gt; pode suportar todos os &amp;lt;em&amp;gt;como&amp;lt;/em&amp;gt;. &amp;lt;/em&amp;gt;__&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O &amp;lt;em&amp;gt;porquê&amp;lt;/em&amp;gt; : diagramas sempre atualizados. O &amp;lt;em&amp;gt;como&amp;lt;/em&amp;gt; : dois comandos em um pipeline Gradle.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_links&amp;quot;&amp;gt;Links&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/nicholasgasior/graphify&amp;quot;&amp;gt;Graphify no GitHub&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/cheroliv/plantuml-gradle&amp;quot;&amp;gt;PlantUML Gradle Plugin&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;/blog/2026/0104_fg_command_super_tips_post.html&amp;quot;&amp;gt;As super dicas do comando fg&amp;lt;/a&amp;gt;— artigo anterior sobre o fluxo de trabalho do terminal&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>O super tip do comando fg: quando Ctrl+Z salva sua sessão de terminal</title>
            <link >https://cheroliv.com//blog/2026/0104_fg_command_super_tips_post.html</link>
            <pubDate>Sat, 18 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0104_fg_command_super_tips_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_um_terminal_ocupado&amp;quot;&amp;gt;O problema: um terminal ocupado&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_os_três_comandos_indispensáveis&amp;quot;&amp;gt;Os três comandos indispensáveis&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_ctrlz_suspender_sigtstp&amp;quot;&amp;gt;Ctrl+Z — Suspender (SIGTSTP)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fg_retomar_em_primeiro_plano&amp;quot;&amp;gt;fg — Retomar em primeiro plano&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_bg_retomar_em_segundo_plano&amp;quot;&amp;gt;bg — Retomar em segundo plano&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_jobs_listar_os_jobs_do_shell&amp;quot;&amp;gt;jobs — Listar os jobs do shell&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_cenário_1_o_build_longo&amp;quot;&amp;gt;Cenário 1: o build longo&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_a_passo&amp;quot;&amp;gt;passo a passo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_cenário_2_o_editor_esquecido&amp;quot;&amp;gt;Cenário 2: O editor esquecido&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_cenário_3_o_processo_opencode_suspenso&amp;quot;&amp;gt;Cenário 3 : O processo opencode suspenso&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_cenário_4_gerenciar_vários_trabalhos_simultaneamente&amp;quot;&amp;gt;Cenário 4: Gerenciar vários trabalhos simultaneamente&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_resumo_dos_comandos_dos_trabalhos&amp;quot;&amp;gt;Resumo dos comandos dos trabalhos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diferenças_entre_fg_bg_ctrlz_ctrlc_e&amp;quot;&amp;gt;Diferenças entre fg, bg, Ctrl+Z, Ctrl+C e &amp;amp;amp;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_ciclo_de_vida_completo_de_um_trabalho&amp;quot;&amp;gt;O ciclo de vida completo de um trabalho&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_dicas_avançadas&amp;quot;&amp;gt;Dicas avançadas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fg_com_o_trabalho_mais_recente_e_a_alternância&amp;quot;&amp;gt;fg com o trabalho mais recente e a alternância&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_matar_um_trabalho_corretamente&amp;quot;&amp;gt;matar um trabalho corretamente&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_désown_desanexar_do_shell&amp;quot;&amp;gt;désown : desanexar do shell&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_combinar_com_nohup_para_os_processos_longos&amp;quot;&amp;gt;Combinar com nohup para os processos longos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_armadilhas_e_erros_comuns&amp;quot;&amp;gt;Armadilhas e erros comuns&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_confundir_ctrlz_e_ctrlc&amp;quot;&amp;gt;Confundir Ctrl+Z e Ctrl+C&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_os_trabalhos_são_locais_ao_shell&amp;quot;&amp;gt;Os trabalhos são locais ao shell&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_os_jobs_não_sobrevivem_ao_fechamento_do_shell&amp;quot;&amp;gt;Os jobs não sobrevivem ao fechamento do shell&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_os_processos_interativos_e_fg&amp;quot;&amp;gt;Os processos interativos e fg&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_resumo&amp;quot;&amp;gt;Diagrama resumo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_fg_é_subestimado&amp;quot;&amp;gt;Por que fg é subestimado&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_memória_técnica_os_sinais&amp;quot;&amp;gt;Memória técnica: os sinais&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referências&amp;quot;&amp;gt;Referências&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 10 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Você lança`./gradlew build`, o terminal está bloqueado por 3 minutos, e você precisa fazer outra coisa &amp;lt;em&amp;gt;aguardando&amp;lt;/em&amp;gt;? Não é necessário abrir uma nova aba.`Ctrl+Z`suspende o processo,`fg`o traz exatamente para onde você o deixou. É a dica Unix mais subestimada do dia a dia de um desenvolvedor.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_problema_um_terminal_ocupado&amp;quot;&amp;gt;O problema: um terminal ocupado&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Todo desenvolvedor já passou por essa situação:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ ./gradlew build
&amp;amp;gt; Building...&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O terminal está bloqueado. Você não pode mais digitar nenhum comando. E você precisa consultar um arquivo, executar um`git status`, ou verificar uma porta ocupada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE

actor Développeur as dev
participant &amp;quot;Terminal&amp;quot; as term
participant &amp;quot;Gradle
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE

actor Développeur as dev
participant &amp;quot;Terminal&amp;quot; as term
participant &amp;quot;Gradle
( processo )&amp;quot; as gradle

dev -&amp;amp;gt; term : ./gradlew build
term -&amp;amp;gt; gradle : Lance le build
gradle -&amp;amp;gt; term : Occupe le terminal\n(pas de prompt)
dev -&amp;amp;gt; term : veut taper une commande
term --&amp;amp;gt; dev : ❌ Terminal bloqué

note right of dev
  Le développeur est
  paralysé pendant
  toute la durée du build
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A maioria dos desenvolvedores abre um novo terminal. Mas existe uma solução mais elegante, mais rápida e nativa: o gerenciamento de jobs.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_os_três_comandos_indispensáveis&amp;quot;&amp;gt;Os três comandos indispensáveis&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_ctrlz_suspender_sigtstp&amp;quot;&amp;gt;Ctrl+Z — Suspender (SIGTSTP)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`Ctrl+Z`envia o sinal`SIGTSTP`ao processo em primeiro plano. O processo é&amp;lt;strong&amp;gt;suspenso&amp;lt;/strong&amp;gt;— não morto — e o terminal recupera o seu prompt.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ ./gradlew build
&amp;amp;gt; Building...
^Z
[1]+  Stoppé    ./gradlew build
$&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O número entre colchetes`[1]`é o&amp;lt;strong&amp;gt;número do emprego&amp;lt;/strong&amp;gt;. Le `+`indica o trabalho padrão&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_fg_retomar_em_primeiro_plano&amp;quot;&amp;gt;fg — Retomar em primeiro plano&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`fg`Traz o trabalho suspenso para o primeiro plano. O processo retoma exatamente onde havia parado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ fg
./gradlew build
&amp;amp;gt; Building... (reprend où il en était)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com um número de trabalho específico :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ fg %2&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_bg_retomar_em_segundo_plano&amp;quot;&amp;gt;bg — Retomar em segundo plano&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`bg`Retoma o processo &amp;lt;em&amp;gt;sem&amp;lt;/em&amp;gt; bloquear o terminal. Ideal para builds longos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ bg
[1]+ ./gradlew build &amp;amp;amp;
$&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le `&amp;amp;amp;`no final significa que o processo está sendo executado em segundo plano.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_jobs_listar_os_jobs_do_shell&amp;quot;&amp;gt;jobs — Listar os jobs do shell&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ jobs
[1]-  Stoppé    vim README.md
[2]+  En cours   ./gradlew build&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 5) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

state &amp;quot;Primeiro plano
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

state &amp;quot;Primeiro plano
(terminal bloqueado)&amp;quot; as fg #LightCoral
state &amp;quot;Suspenso\n(Ctrl+Z)&amp;quot; as stopped #LightYellow
state &amp;quot;Fundo\n(bg)&amp;quot; as bg_ #LightGreen

[*] --&amp;amp;gt; fg : Lance une commande
fg --&amp;amp;gt; stopped : Ctrl+Z\n(SIGTSTP)
stopped --&amp;amp;gt; fg : fg\nreprend en avant-plan
stopped --&amp;amp;gt; bg_ : bg\nreprend en arrière-plan
bg_ --&amp;amp;gt; fg : fg\nramène en avant-plan
fg --&amp;amp;gt; [*] : Commande terminée

note right of stopped
  Le processus est figé
  mais _pas_ tué
end note

note left of bg_
  Le terminal est libre
  le build continue
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_cenário_1_o_build_longo&amp;quot;&amp;gt;Cenário 1: o build longo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É o caso de uso mais comum. Você inicia um build Gradle, o terminal está bloqueado, e você precisa fazer outra coisa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 8) ]

@startuml
skinparam backgroundColor #FEFEFE
autonumber

actor Développeur as dev
participant &amp;quot;Terminal&amp;quot; as term
participant &amp;quot;Gradle&amp;quot; as gradle
database &amp;quot;Estado
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE
autonumber

actor Développeur as dev
participant &amp;quot;Terminal&amp;quot; as term
participant &amp;quot;Gradle&amp;quot; as gradle
database &amp;quot;Estado
processo&amp;quot; as state

dev -&amp;amp;gt; term : ./gradlew build
term -&amp;amp;gt; gradle : Lance le build
gradle -&amp;amp;gt; state : En cours (FG)
dev -&amp;amp;gt; term : Ctrl+Z
term -&amp;amp;gt; state : Suspendu (job [1])
term --&amp;amp;gt; dev : Prompt libre $

dev -&amp;amp;gt; term : git status
term --&amp;amp;gt; dev : working tree clean

dev -&amp;amp;gt; term : fg
term -&amp;amp;gt; state : Reprend (FG)
gradle --&amp;amp;gt; dev : Build réussi ✓

note right of state
  Le processus Gradle
  n&amp;#39;a jamais été tué
  Il reprend exactement
  où il s&amp;#39;était arrêté
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_passo_a_passo&amp;quot;&amp;gt;passo a passo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ ./gradlew build
&amp;amp;gt; Building...
^Z
[1]+  Stoppé    ./gradlew build

$ git status
On branch main
nothing to commit, working tree clean

$ fg
./gradlew build
&amp;amp;gt; Building...
BUILD SUCCESSFUL in 2m 34s&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_cenário_2_o_editor_esquecido&amp;quot;&amp;gt;Cenário 2: O editor esquecido&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você está em`vim`, você precisa consultar outro arquivo &amp;lt;em&amp;gt;sans&amp;lt;/em&amp;gt; sair`vim`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Dans vim, en mode commande :
:shell
$ cat /path/to/other/file.txt
$ exit
# Retour dans vim&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas é pesado. Com`Ctrl+Z`, é imediato:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Dans vim, n&amp;#39;importe quel moment :
Ctrl+Z
[1]+  Stoppé    vim README.md

$ cat /path/to/other/file.txt
# ... lire le fichier ...

$ fg
vim README.md
# Retour exact là où on en était, curseur en place&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/vim-suspend.svg&amp;quot; alt=&amp;quot;vim suspend&amp;quot; width=&amp;quot;410&amp;quot; height=&amp;quot;510&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;NOTA:`vim`restaura perfeitamente o seu estado após um`fg`: posição do cursor, conteúdo do buffer, histórico de desfazer — tudo está preservado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_cenário_3_o_processo_opencode_suspenso&amp;quot;&amp;gt;Cenário 3 : O processo opencode suspenso&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este é o cenário que motivou este artigo. Você utiliza`opencode`(ferramenta CLI para pilotar um LLM no seu código), e o processo está suspenso.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 7) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
skinparam componentStyle rectangle

actor Développeur as dev
participant &amp;quot;opencode
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
skinparam componentStyle rectangle

actor Développeur as dev
participant &amp;quot;opencode
(agente LLM)&amp;quot; as agent
participant &amp;quot;carapaça&amp;quot; as shell
participant &amp;quot;git&amp;quot; as git

dev -&amp;amp;gt; agent : Session active
agent -&amp;amp;gt; agent : Tour de parole\ndu LLM en cours

note over agent
  Ctrl+Z arrive
  en plein milieu
  d&amp;#39;une analyse de code
end note

dev -&amp;amp;gt; agent : Ctrl+Z
agent -&amp;amp;gt; shell : Suspendu (job [1])
dev -&amp;amp;gt; git : git diff
git --&amp;amp;gt; dev : changements visibles
dev -&amp;amp;gt; shell : fg
shell -&amp;amp;gt; agent : Reprend (job [1])
agent --&amp;amp;gt; dev : Reprend exactement\nl&amp;#39;analyse en cours

note right of agent
  Aucune perte de contexte
  Le LLM reprend sa réponse
  exactement où il en était
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ opencode
# ... session en cours, LLM analyse le code ...
^Z
[1]+  Stoppé    opencode

$ git diff HEAD~1
# vérifier les changements récents

$ fg
opencode
# Le LLM reprend là où il s&amp;#39;était arrêté&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A vantagem é dupla :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Sem perda de contexto&amp;lt;/strong&amp;gt;: a sessão LLM, o histórico de conversa, os arquivos abertos — tudo está preservado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Sem reinicialização&amp;lt;/strong&amp;gt;: não é necessário reiniciar o opencode, recarregar o contexto, explicar novamente o problema ao LLM&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_cenário_4_gerenciar_vários_trabalhos_simultaneamente&amp;quot;&amp;gt;Cenário 4: Gerenciar vários trabalhos simultaneamente&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;fg&amp;lt;/code&amp;gt; et `bg`aceitam um número de job para direcionar um processo específico quando vários estão suspensos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ vim config.yml
^Z
[1]+  Stoppé    vim config.yml

$ ./gradlew test
^Z
[2]+  Stoppé    ./gradlew test

$ htop
^Z
[3]+  Stoppé    htop

$ jobs
[1]   Stoppé    vim config.yml
[2]-  Stoppé    ./gradlew test
[3]+  Stoppé    htop

$ fg %2
./gradlew test
# Le build reprend en avant-plan

$ bg %3
[3] htop &amp;amp;amp;
# htop reprend en arrière-plan&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 13) ]

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;Trabalhos suspensos&amp;quot; as jobs #LightYellow {
  card &amp;quot;[1] vim config.yml&amp;quot; as j1
  card &amp;quot;[2] ./gradlew test&amp;quot; as j2
  card &amp;quot;[3] htop&amp;quot; as j3
}

rectangle &amp;quot;Ações&amp;quot; as actions #LightGreen {
  card &amp;quot;fg %2\n→ primeiro plano&amp;quot; as a1
  card &amp;quot;bg %3
→ fundo&amp;quot; as a2
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;Trabalhos suspensos&amp;quot; as jobs #LightYellow {
  card &amp;quot;[1] vim config.yml&amp;quot; as j1
  card &amp;quot;[2] ./gradlew test&amp;quot; as j2
  card &amp;quot;[3] htop&amp;quot; as j3
}

rectangle &amp;quot;Ações&amp;quot; as actions #LightGreen {
  card &amp;quot;fg %2\n→ primeiro plano&amp;quot; as a1
  card &amp;quot;bg %3
→ fundo&amp;quot; as a2
  card &amp;quot;fg %1\n→ primeiro plano&amp;quot; as a3
}

jobs --&amp;amp;gt; actions : fg/bg + numéro de job
actions --&amp;amp;gt; j2 : Reprend
actions --&amp;amp;gt; j3 : En arrière-plan

note bottom of jobs
  Chaque job conserve
  son état exact
  entre les suspend/resume
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_resumo_dos_comandos_dos_trabalhos&amp;quot;&amp;gt;Resumo dos comandos dos trabalhos&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 20%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 40%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 40%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;pedido&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;efeito&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Exemplo&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;Ctrl+Z&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Suspende o processo em primeiro plano&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Envio de`SIGTSTP`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;fg&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Retoma o job padrão em primeiro plano&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;fg&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;fg %1&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;fg %n&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Retoma o job n em primeiro plano&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;fg %2&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;bg&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Retome o trabalho padrão em segundo plano&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;bg&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;bg %1&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;bg %n&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Retoma o job n em segundo plano&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;bg %2&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Lista os jobs do shell corrente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;jobs -l&amp;lt;/code&amp;gt;(com PID)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;kill %n&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Termine o job n&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;kill %2&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_diferenças_entre_fg_bg_ctrlz_ctrlc_e&amp;quot;&amp;gt;Diferenças entre fg, bg, Ctrl+Z, Ctrl+C e &amp;amp;amp;&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Muitos desenvolvedores confundem esses mecanismos. Eis um esclarecimento decisivo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 15) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

rectangle &amp;quot;**Ctrl+Z**\nSIGTSTP&amp;quot; as ctrlz #LightYellow {
  card &amp;quot;Suspende o processo\n(ele ainda está na memória)\nUtilizável com fg/bg&amp;quot; as c1
}

rectangle &amp;quot;**Ctrl+C**\nSIGINT&amp;quot; as ctrlc #LightCoral {
  card &amp;quot;Finaliza o processo\n(sem possibilidade de retorno)\nPerda de todo o estado&amp;quot; as c2
}

rectangle &amp;quot;**&amp;amp;amp; **\n(fundo)&amp;quot; as ampersand #LightGreen {
  card &amp;quot;Inicie diretamente
em segundo plano
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

rectangle &amp;quot;**Ctrl+Z**\nSIGTSTP&amp;quot; as ctrlz #LightYellow {
  card &amp;quot;Suspende o processo\n(ele ainda está na memória)\nUtilizável com fg/bg&amp;quot; as c1
}

rectangle &amp;quot;**Ctrl+C**\nSIGINT&amp;quot; as ctrlc #LightCoral {
  card &amp;quot;Finaliza o processo\n(sem possibilidade de retorno)\nPerda de todo o estado&amp;quot; as c2
}

rectangle &amp;quot;**&amp;amp;amp; **\n(fundo)&amp;quot; as ampersand #LightGreen {
  card &amp;quot;Inicie diretamente
em segundo plano
Terminal livre imediatamente&amp;quot; as c3
}

rectangle &amp;quot;**fg**\n(primeiro plano)&amp;quot; as fg_ #SkyBlue {
  card &amp;quot;Traga um trabalho
suspenso em primeiro plano
Terminal ocupado&amp;quot; as c4
}

rectangle &amp;quot;**bg**
(background)&amp;quot; as bg_ #Lavender {
  card &amp;quot;Retoma um trabalho suspenso em segundo plano\nTerminal livre&amp;quot; as c5
}

ctrlz -right-&amp;amp;gt; fg_ : fg pour reprendre
ctrlz -right-&amp;amp;gt; bg_ : bg pour continuer\nen arrière-plan
ctrlc -right-&amp;amp;gt; c2 : Pas de retour\nprocessus tué

note bottom of ctrlz
  Le processus est **figé**
  mais **pas tué**
end note

note bottom of ampersand
  Alternative : lancer
  ./gradlew build &amp;amp;amp;
  directement en arrière-plan
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 16.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 16.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3335%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Atalho/Comando&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Sinal&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Efeito&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Voltar atrás?&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;Ctrl+Z&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;SIGTSTP&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Suspender o processo (congelado, na memória)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sim:`fg` ou &amp;lt;code&amp;gt;bg&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;Ctrl+C&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;SIGINT&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Mate o processo (terminado)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Não : processo destruído&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;Ctrl+\&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;SIGQUIT&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Mate o processo + core dump&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Não : processo destruído&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;commande &amp;amp;amp;&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;—&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Execute em segundo plano diretamente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`fg`para trazê-lo de volta para o primeiro plano&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock tip&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-tip&amp;quot; title=&amp;quot;Tip&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;code&amp;gt;Ctrl+Z`é&amp;lt;strong&amp;gt;não destrutivo&amp;lt;/strong&amp;gt;. O processo está congelado tal como está, na memória. Você pode retomar imediatamente com`fg&amp;lt;/code&amp;gt;. É um &amp;lt;em&amp;gt;pausa&amp;lt;/em&amp;gt;, não um &amp;lt;em&amp;gt;parada&amp;lt;/em&amp;gt;.
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_ciclo_de_vida_completo_de_um_trabalho&amp;quot;&amp;gt;O ciclo de vida completo de um trabalho&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 8) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

title Ciclo de vida completo de um job shell
state &amp;quot;Inexistente&amp;quot; as none
state &amp;quot;Primeiro plano\n(ocupa o terminal)&amp;quot; as foreground #LightCoral
state &amp;quot;Suspenso
^^^^^
 Syntax Error? (Assumed diagram type: state)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

title Ciclo de vida completo de um job shell
state &amp;quot;Inexistente&amp;quot; as none
state &amp;quot;Primeiro plano\n(ocupa o terminal)&amp;quot; as foreground #LightCoral
state &amp;quot;Suspenso
(Ctrl+Z / SIGTSTP)&amp;quot; as suspended #LightYellow
state &amp;quot;plano de fundo
(bg ou &amp;amp;amp;)&amp;quot; as background #LightGreen
state &amp;quot;Concluído
(código de saída 0)&amp;quot; as done #LightGray

[*] --&amp;amp;gt; none
none --&amp;amp;gt; foreground : tape une commande
foreground --&amp;amp;gt; suspended : Ctrl+Z
foreground --&amp;amp;gt; done : fin normale

suspended --&amp;amp;gt; foreground : fg
suspended --&amp;amp;gt; background : bg
suspended --&amp;amp;gt; done : kill %n

background --&amp;amp;gt; foreground : fg
background --&amp;amp;gt; done : fin normale\n(en arrière-plan)
background --&amp;amp;gt; suspended : Ctrl+Z\n(si fg puis Ctrl+Z)

done --&amp;amp;gt; [*]

note right of suspended
  L&amp;#39;état suspendu est
  le pivot de tout le
  système de jobs
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_dicas_avançadas&amp;quot;&amp;gt;Dicas avançadas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_fg_com_o_trabalho_mais_recente_e_a_alternância&amp;quot;&amp;gt;fg com o trabalho mais recente e a alternância&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;fg`sem argumento traz o trabalho marcado&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;(o mais recente). Mas você também pode usar&amp;lt;/code&amp;gt;%-`pour direcionar o trabalho anterior :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ vim file1.txt
^Z
[1]+  Stoppé    vim file1.txt

$ vim file2.txt
^Z
[2]+  Stoppé    vim file2.txt

$ fg      # ramène job [2] (le plus récent)
$ fg %-   # ramène job [1] (le précédent)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_matar_um_trabalho_corretamente&amp;quot;&amp;gt;matar um trabalho corretamente&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ jobs
[1]-  Stoppé    vim config.yml
[2]+  Stoppé    ./gradlew test

$ kill %2       # envoie SIGTERM au job 2
$ kill -9 %1    # envoie SIGKILL au job 1 (force)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_désown_desanexar_do_shell&amp;quot;&amp;gt;désown : desanexar do shell&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;disown`Remover um trabalho da tabela de trabalhos do shell. O processo continua em execução, mas não pode mais ser trazido de volta com`fg&amp;lt;/code&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ ./gradlew build &amp;amp;amp;
[1] 12345

$ disown %1
# Le processus continue mais n&amp;#39;est plus lié au shell
# Ça survives à la fermeture du terminal&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_combinar_com_nohup_para_os_processos_longos&amp;quot;&amp;gt;Combinar com nohup para os processos longos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para que um processo sobreviva ao encerramento do terminal :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ nohup ./gradlew build &amp;amp;amp;
[1] 12345
$ disown %1
# Le build continue même si vous fermez le terminal&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 8) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

rectangle &amp;quot;fg&amp;quot; as fg_ #SkyBlue {
  card &amp;quot;Traga o trabalho\no mais recente (+)&amp;quot; as f1
  card &amp;quot;Direcione um trabalho
específico: fg %n&amp;quot; as f2
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

rectangle &amp;quot;fg&amp;quot; as fg_ #SkyBlue {
  card &amp;quot;Traga o trabalho\no mais recente (+)&amp;quot; as f1
  card &amp;quot;Direcione um trabalho
específico: fg %n&amp;quot; as f2
  card &amp;quot;Altere : fg %-&amp;quot; as f3
}

rectangle &amp;quot;matar %n&amp;quot; as kill_ #LightCoral {
  card &amp;quot;SIGTERM (limpo)&amp;quot; as k1
  card &amp;quot;SIGKILL -9 (forçar)&amp;quot; as k2
}

rectangle &amp;quot;repudiar&amp;quot; as disown #Lavender {
  card &amp;quot;Desacoplado do shell
(sobrevive à desconexão)&amp;quot; as d1
}

rectangle &amp;quot;nohup + &amp;amp;amp;&amp;quot; as nohup_ #LightGreen {
  card &amp;quot;Processo imunizado
contra SIGHUP
(sobrevive ao fechamento
do terminal)&amp;quot; as n1
}

fg_ -right-&amp;amp;gt; kill_ : si faut\nterminer
kill_ -right-&amp;amp;gt; disown_ : ou détacher
nohup_ -left-&amp;amp;gt; fg_ : fg ramène en\navant-plan

note bottom of disown_
  Après disown, fg ne peut
  plus ramener le job
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_armadilhas_e_erros_comuns&amp;quot;&amp;gt;Armadilhas e erros comuns&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_confundir_ctrlz_e_ctrlc&amp;quot;&amp;gt;Confundir Ctrl+Z e Ctrl+C&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este é o erro mais frequente.`Ctrl+C`mata o processo.`Ctrl+Z`o suspende. Se você pressiona em`Ctrl+C`por reflexo, todo o estado é perdido — arquivos abertos, sessões LLM, compilações em andamento.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/ctrlz-vs-ctrlc.svg&amp;quot; alt=&amp;quot;ctrlz vs ctrlc&amp;quot; width=&amp;quot;1203&amp;quot; height=&amp;quot;129&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_os_trabalhos_são_locais_ao_shell&amp;quot;&amp;gt;Os trabalhos são locais ao shell&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fg&amp;lt;/code&amp;gt; et `bg`funcionam apenas no shell que iniciou os processos. Se você abre um novo terminal, você não verá os trabalhos do outro.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Terminal 1
$ vim file.txt
^Z
[1]+  Stoppé    vim file.txt

# Terminal 2 (nouveau)
$ jobs
# (rien — les jobs sont locaux au shell)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para ver os processos system-wide, utiliza`ps` ou &amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;ps aux | grep vim&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_os_jobs_não_sobrevivem_ao_fechamento_do_shell&amp;quot;&amp;gt;Os jobs não sobrevivem ao fechamento do shell&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se você fechar o terminal, todos os trabalhos suspensos ou em segundo plano são destruídos. Para que um processo sobreviva, utilize`nohup`+&amp;lt;code&amp;gt;disown&amp;lt;/code&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ nohup ./gradlew build &amp;amp;amp;
[1] 12345
$ disown %1
# Fermer le terminal → le build continue&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_os_processos_interativos_e_fg&amp;quot;&amp;gt;Os processos interativos e fg&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Alguns programas interativos não se portam bem depois de um`Ctrl+Z`+&amp;lt;code&amp;gt;fg&amp;lt;/code&amp;gt;. É raro mas acontece :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Alguns programas que utilizam sinais (vim, htop, less lidam bem`Ctrl+Z`)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Os programas de rede longos (ssh, opencode) geralmente recuperam corretamente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Os programas que modificam o terminal (ncurses) podem, às vezes, deixar o terminal num estado inesperado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em caso de terminal corrompido depois de um`fg`, digite :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;reset&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_diagrama_resumo&amp;quot;&amp;gt;Diagrama resumo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 10) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
skinparam ArrowColor #444444

title &amp;quot;fg — O tip que muda tudo&amp;quot;

rectangle &amp;quot;**Problema**&amp;quot; as problem #LightCoral {
  card &amp;quot;Terminal bloqueado
por um processo&amp;quot; as p1
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
skinparam ArrowColor #444444

title &amp;quot;fg — O tip que muda tudo&amp;quot;

rectangle &amp;quot;**Problema**&amp;quot; as problem #LightCoral {
  card &amp;quot;Terminal bloqueado
por um processo&amp;quot; as p1
  card &amp;quot;Abrir um novo
terminal = solução pesada&amp;quot; as p2
}

rectangle &amp;quot;**Solução**&amp;quot; as solution #LightGreen {
  card &amp;quot;Ctrl+Z → suspender&amp;quot; as s1
  card &amp;quot;fg → retoma&amp;quot; as s2
  card &amp;quot;bg → em segundo plano&amp;quot; as s3
  card &amp;quot;jobs → ver os suspensos&amp;quot; as s4
}

rectangle &amp;quot;**Vantagens**&amp;quot; as advantages #SkyBlue {
  card &amp;quot;Sem perda de contexto&amp;quot; as a1
  card &amp;quot;Sem reinicialização&amp;quot; as a2
  card &amp;quot;Um único terminal basta&amp;quot; as a3
}

problem -right-&amp;amp;gt; solution : &amp;quot;A solução nativa
Unix há 40 anos&amp;quot;
solution -right-&amp;amp;gt; advantages : &amp;quot;Resultado&amp;quot;

note bottom of advantages
  fg n&amp;#39;est pas un tips obscur
  C&amp;#39;est un mécanisme fondamental
  du shell Unix
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_fg_é_subestimado&amp;quot;&amp;gt;Por que fg é subestimado&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A maioria dos desenvolvedores modernos descobre`fg`tarde, ou mesmo nunca. Algumas razões:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Os IDEs escondem o terminal&amp;lt;/strong&amp;gt;: VS Code oferece um terminal integrado, mas as abas múltiplas tornam`fg`menos necessário em aparência&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;As ferramentas gráficas substituem a CLI&amp;lt;/strong&amp;gt;: os gerenciadores de arquivos, os monitores de sistema, os clientes Git gráficos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;tmux e screen&amp;lt;/strong&amp;gt;: esses multiplexadores resolvem o mesmo problema de maneira diferente, mas`fg`é mais simples e sempre disponível&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Contudo,`fg`é:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Universal&amp;lt;/strong&amp;gt;: presente em todos os shells POSIX (bash, zsh, fish&amp;amp;#8230;&amp;amp;#8203;)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Sem dependência&amp;lt;/strong&amp;gt;: não é necessário instalar o tmux ou o screen&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Instantâneo&amp;lt;/strong&amp;gt;: duas pancadas (&amp;lt;code&amp;gt;Ctrl+Z`então`fg&amp;lt;/code&amp;gt;) para suspender e retomar&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Preservador de contexto&amp;lt;/strong&amp;gt;: o estado do processo está íntegro&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

rectangle &amp;quot;Por que fg é
subestimado?&amp;quot; as why #LightCoral {
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

rectangle &amp;quot;Por que fg é
subestimado?&amp;quot; as why #LightCoral {
  card &amp;quot;IDE com terminal\nmulti-abas&amp;quot; as w1
  card &amp;quot;Ferramentas gráficas
que substituem a CLI&amp;quot; as w2
  card &amp;quot;tmux/screen
resolvem o problema
de outra forma&amp;quot; as w3
}

rectangle &amp;quot;Por que fg é
superior ?&amp;quot; as superior #LightGreen {
  card &amp;quot;Universal : bash,
zsh, fish, sh...&amp;quot; as s1
  card &amp;quot;Sem dependência:
sem instalação&amp;quot; as s2
  card &amp;quot;Dois golpes :\nCtrl+Z depois fg&amp;quot; as s3
  card &amp;quot;Contexto preservado :
zero perda de estado&amp;quot; as s4
}

why -right-&amp;amp;gt; superior : &amp;quot;A simplicidade
sempre vence&amp;quot;
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_memória_técnica_os_sinais&amp;quot;&amp;gt;Memória técnica: os sinais&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para os curiosos, aqui estão os sinais envolvidos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 14.2857%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 28.5714%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 28.5714%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 28.5715%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;sinal&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Nome&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Enviado por&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;efeito&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;SIGINT&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;Ctrl+C&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Interrupção — termina o processo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;SIGQUIT&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;Ctrl+\&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Encerra com core dump&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;18&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;SIGTSTP&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;Ctrl+Z&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Interativo — suspende o processo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;19&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;SIGCONT&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;fg&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;bg&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Continua — retoma um processo suspenso&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;15&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;kill %n&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;terminação limpa&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;9&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;kill -9 %n&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Terminação forçada (impossível de interceptar)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O fluxo completo :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/signal-flow.svg&amp;quot; alt=&amp;quot;signal flow&amp;quot; width=&amp;quot;431&amp;quot; height=&amp;quot;525&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`fg`não é uma dica obscura — é um mecanismo fundamental do shell Unix, disponível desde os anos 70. O fato de a maioria dos desenvolvedores ignorá-lo é um sintoma da nossa época: as IDEs e os multiplexadores nos afastaram das bases.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas quando você está em um servidor SSH, em um terminal mínimo ou simplesmente usando`opencode`e o LLM está em pleno trabalho —&amp;lt;code&amp;gt;Ctrl+Z&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fg&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bg&amp;lt;/code&amp;gt;, `jobs`são seus melhores amigos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ Ctrl+Z suspende, fg retoma. É o pausa-play do terminal. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_referências&amp;quot;&amp;gt;Referências&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;man bash&amp;lt;/code&amp;gt;— seção JOB CONTROL&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;man signal&amp;lt;/code&amp;gt;— lista de sinais POSIX&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://www.gnu.org/software/bash/manual/bash.html#Job-Control&amp;quot;&amp;gt;Manual de referência do Bash — Controle dos trabalhos&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://man7.org/linux/man-pages/man7/signal.7.html&amp;quot;&amp;gt;man 7 sinal — sinais Linux&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://tldp.org/LDP/abs/html/x312743.htm&amp;quot;&amp;gt;Guia Avançado de Bash-Scripting — Controle de Trabalhos&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>VS Code no Home: instalação local e customização das fontes sem sudo</title>
            <link >https://cheroliv.com//blog/2026/0103_vscode_local_install_custom_fonts_post.html</link>
            <pubDate>Fri, 17 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0103_vscode_local_install_custom_fonts_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_visão_geral_do_percurso&amp;quot;&amp;gt;Visão geral do percurso&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_porquê_vs_code_no_home&amp;quot;&amp;gt;O Porquê : VS Code no Home&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_com_a_instalação_do_sistema&amp;quot;&amp;gt;O problema com a instalação do sistema&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_uma_instalação_local&amp;quot;&amp;gt;A solução: uma instalação local&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_vantagens_concretas&amp;quot;&amp;gt;Vantagens concretas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_customizar_a_fonte_do_menu_principal&amp;quot;&amp;gt;Customizar a fonte do menu principal&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_constatação&amp;quot;&amp;gt;A constatação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_técnica_patch_css_direto&amp;quot;&amp;gt;A técnica: patch CSS direto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_remover_o_aviso_de_integridade&amp;quot;&amp;gt;Remover o aviso de integridade&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_personalizar_a_fonte_do_painel_explorer_a_luta&amp;quot;&amp;gt;Personalizar a fonte do painel Explorer — a luta&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_tentativa_1_um_parâmetro_que_não_existe&amp;quot;&amp;gt;Tentativa 1: um parâmetro que não existe&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_tentativa_2_a_extensão_custom_css_and_js_loader&amp;quot;&amp;gt;Tentativa 2: a extensão Custom CSS and JS Loader&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_armadilha_é_preciso_ativar_a_extensão_a_cada_vez&amp;quot;&amp;gt;A armadilha: é preciso ativar a extensão a cada vez&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_tentativa_3_as_linhas_se_sobrepõem&amp;quot;&amp;gt;Tentativa 3: as linhas se sobrepõem&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_zoom_no_contêiner_pai&amp;quot;&amp;gt;A solução: &amp;lt;code&amp;gt;zoom&amp;lt;/code&amp;gt; no contêiner pai&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_resumo_dos_seletores_e_do_que_funciona&amp;quot;&amp;gt;Resumo dos seletores e do que funciona&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_script_de_atualização_automática&amp;quot;&amp;gt;O script de atualização automática&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema&amp;quot;&amp;gt;O problema&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_script_vscode_update_sh&amp;quot;&amp;gt;O script `vscode-update.sh&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquivos_envolvidos_visão_geral&amp;quot;&amp;gt;Arquivos envolvidos — visão geral&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_lições_aprendidas&amp;quot;&amp;gt;Lições aprendidas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalar_no_home_libera_tudo&amp;quot;&amp;gt;Instalar no home libera tudo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_zoom_font_size_para_os_componentes_de_lista&amp;quot;&amp;gt;zoom` &amp;amp;gt; &amp;lt;code&amp;gt;font-size&amp;lt;/code&amp;gt; para os componentes de lista&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_extensão_custom_css_requer_uma_ativação_explícita&amp;quot;&amp;gt;A extensão Custom CSS requer uma ativação explícita&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_um_script_de_atualização_é_indispensável&amp;quot;&amp;gt;Um script de atualização é indispensável&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referências_rápidas&amp;quot;&amp;gt;Referências rápidas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 8 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Por que instalar o VS Code em`/opt`ou via`apt`quando a gente pode colocá-lo no seu home e transformar seu IDE num terreno de jogo sem digitar nunca`sudo`? Esta é a história de uma instalação local seguida de uma luta feroz para aumentar as fontes do menu e do explorador de arquivos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_visão_geral_do_percurso&amp;quot;&amp;gt;Visão geral do percurso&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 10) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

actor Développeur as dev
actor &amp;quot;opencode\n+ LLM&amp;quot; as agent

package &amp;quot;Home Usuário (~)&amp;quot; #LightGreen {
    package &amp;quot;~/apps/&amp;quot; {
        component &amp;quot;VS Code
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

actor Développeur as dev
actor &amp;quot;opencode\n+ LLM&amp;quot; as agent

package &amp;quot;Home Usuário (~)&amp;quot; #LightGreen {
    package &amp;quot;~/apps/&amp;quot; {
        component &amp;quot;VS Code
(tarball)&amp;quot; as vscode #SkyBlue
        component &amp;quot;vscode-update.sh&amp;quot; as script #LightYellow
    }
    package &amp;quot;~/.config/Code/&amp;quot; {
        component &amp;quot;settings.json&amp;quot; as settings #Lavender
    }
    package &amp;quot;~/.vscode-custom-css/&amp;quot; {
        component &amp;quot;custom.css
(zoom: 1.25)&amp;quot; as customcss #Lavender
    }
}

cloud &amp;quot;code.visualstudio.com&amp;quot; as download

dev --&amp;amp;gt; vscode : Télécharge &amp;amp;amp; installe
dev --&amp;amp;gt; script : Lance mise à jour
script --&amp;amp;gt; download : wget dernière version
script --&amp;amp;gt; vscode : Remplace + re-patch CSS
agent --&amp;amp;gt; settings : Écrit config
agent --&amp;amp;gt; customcss : Écrit zoom CSS
agent --&amp;amp;gt; vscode : Patch workbench CSS

note right of vscode
  Installation dans le home
  = contrôle total sans sudo
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_porquê_vs_code_no_home&amp;quot;&amp;gt;O Porquê : VS Code no Home&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_problema_com_a_instalação_do_sistema&amp;quot;&amp;gt;O problema com a instalação do sistema&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;VS Code instalado via`apt`, &amp;lt;code&amp;gt;dnf`ou em&amp;lt;/code&amp;gt;/opt`bloqueia os arquivos do aplicativo atrás dos privilégios de root. Qualquer modificação do CSS nativo, de`product.json`ou o diretório de instalação requer`sudo`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Or, quando se usa&amp;lt;strong&amp;gt;opencode&amp;lt;/strong&amp;gt;(ferramenta CLI para pilotar um LLM no seu código), o agente precisa poder :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Escrever nos ficheiros de configuração`~/.config/Code/User/settings.json`) — isso, já está no home&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Modificar os assets do IDE (CSS do workbench,&amp;lt;code&amp;gt;product.json&amp;lt;/code&amp;gt;) para as customizações avançadas — isso, está bloqueado sem sudo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_solução_uma_instalação_local&amp;quot;&amp;gt;A solução: uma instalação local&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Baixa o arquivo tarball oficial e o descompacta diretamente em`~/apps/`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p ~/apps
cd ~/apps
wget &amp;quot;https://code.visualstudio.com/sha/download?build=stable&amp;amp;amp;os=linux-x64&amp;quot; -O vscode.tar.gz
tar -xzf vscode.tar.gz
rm vscode.tar.gz&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O binário então se encontra em`~/apps/VSCode-linux-x64/bin/code`. Nenhum`sudo`não é necessário para ler, modificar ou substituir qualquer arquivo sob`~/apps/VSCode-linux-x64/`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_vantagens_concretas&amp;quot;&amp;gt;Vantagens concretas&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/vscode-local-vs-system.svg&amp;quot; alt=&amp;quot;vscode local vs system&amp;quot; width=&amp;quot;1826&amp;quot; height=&amp;quot;5676&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 66.6667%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Vantagem&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Detalhe&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sem sudo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Todos os arquivos do IDE pertencem ao usuário&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Modificação direta do CSS&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Podemos aplicar patches`workbench.desktop.main.css`sem elevação de privilégios&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Script de atualização caseira&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Um simples script de shell baixa, substitui e repatch automaticamente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Isolação&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;A instalação do sistema não está poluída; rollback = excluir a pasta&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_customizar_a_fonte_do_menu_principal&amp;quot;&amp;gt;Customizar a fonte do menu principal&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_constatação&amp;quot;&amp;gt;A constatação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A fonte padrão do menu principal (File, Edit, View&amp;amp;#8230;&amp;amp;#8203;) e dos menus suspensos é muito pequena. O VS Code não oferece nenhum parâmetro para modificá-la.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_técnica_patch_css_direto&amp;quot;&amp;gt;A técnica: patch CSS direto&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Adicionamos CSS ao final do arquivo`workbench.desktop.main.css`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/menubar-patch-sequence.svg&amp;quot; alt=&amp;quot;menubar patch sequence&amp;quot; width=&amp;quot;675&amp;quot; height=&amp;quot;599&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;CSS_FILE=&amp;quot;$HOME/apps/VSCode-linux-x64/resources/app/out/vs/workbench/workbench.desktop.main.css&amp;quot;

cat &amp;amp;gt;&amp;amp;gt; &amp;quot;$CSS_FILE&amp;quot; &amp;amp;lt;&amp;amp;lt; &amp;#39;EOF&amp;#39;
.menubar-menu-title{font-size:23px!important}
.menubar&amp;amp;gt;.menubar-menu-button{font-size:23px!important}
.monaco-menu-option{font-size:22px!important;line-height:34px!important}
.monaco-menu .action-label:not(.codicon){font-size:22px!important}
.menubar-menu-items-holder .monaco-menu .action-item .action-label{font-size:22px!important}
EOF&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_remover_o_aviso_de_integridade&amp;quot;&amp;gt;Remover o aviso de integridade&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;VS Code detecta as modificações de seus arquivos e exibe um banner \&amp;quot;Your installation is corrupt\&amp;quot;. Remove-se os checksums de`product.json` :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-python hljs&amp;quot; data-lang=&amp;quot;python&amp;quot;&amp;gt;import json
product_json = &amp;quot;$HOME/apps/VSCode-linux-x64/resources/app/product.json&amp;quot;
with open(product_json, &amp;#39;r&amp;#39;) as f:
    data = json.load(f)
data.pop(&amp;#39;checksums&amp;#39;, None)
with open(product_json, &amp;#39;w&amp;#39;) as f:
    json.dump(data, f, indent=2)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Após reinicialização, não há mais avisos e os menus finalmente são legíveis.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_personalizar_a_fonte_do_painel_explorer_a_luta&amp;quot;&amp;gt;Personalizar a fonte do painel Explorer — a luta&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_tentativa_1_um_parâmetro_que_não_existe&amp;quot;&amp;gt;Tentativa 1: um parâmetro que não existe&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Primeiro tenta o parâmetro nativo`workbench.tree.fontSize`em`settings.json`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-json hljs&amp;quot; data-lang=&amp;quot;json&amp;quot;&amp;gt;{
    &amp;quot;workbench.tree.fontSize&amp;quot;: 20
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Resultado:&amp;lt;strong&amp;gt;nenhum efeito&amp;lt;/strong&amp;gt;. Este parâmetro não existe no VS Code. A prova de que os parâmetros não reconhecidos são ignorados silenciosamente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_tentativa_2_a_extensão_custom_css_and_js_loader&amp;quot;&amp;gt;Tentativa 2: a extensão Custom CSS and JS Loader&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Instala-se a extensão`be5invis.vscode-custom-css`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;~/apps/VSCode-linux-x64/bin/code --install-extension be5invis.vscode-custom-css&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cria-se um arquivo CSS em`~/.vscode-custom-css/custom.css`com seletores que visam os elementos do explorador :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css hljs&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;.monaco-tree-rows .monaco-tree-row,
.sidebar .monaco-list-row,
.explorer-viewlet .monaco-list-row,
.monaco-list-row .monaco-icon-label {
    font-size: 22px !important;
    line-height: 36px !important;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E referenciamos este arquivo nas configurações:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-json hljs&amp;quot; data-lang=&amp;quot;json&amp;quot;&amp;gt;{
    &amp;quot;vscode_custom_css.imports&amp;quot;: [
        &amp;quot;file:///home/user/.vscode-custom-css/custom.css&amp;quot;
    ]
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Resultado :&amp;lt;strong&amp;gt;as alterações não se aplicam&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_armadilha_é_preciso_ativar_a_extensão_a_cada_vez&amp;quot;&amp;gt;A armadilha: é preciso ativar a extensão a cada vez&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A documentação da extensão é discreta sobre este ponto crucial: após cada modificação do CSS personalizado, é necessário&amp;lt;strong&amp;gt;imperativamente&amp;lt;/strong&amp;gt;executar o comando a partir da paleta :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;Ctrl+Shift+P&amp;lt;/code&amp;gt;→ digitar&amp;lt;strong&amp;gt;Ativar CSS e JS personalizados&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;VS Code pede uma reinicialização → aceitar&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sem esta etapa, o CSS nunca é injetado na IDE.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_tentativa_3_as_linhas_se_sobrepõem&amp;quot;&amp;gt;Tentativa 3: as linhas se sobrepõem&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mesmo com`font-size: 22px` et &amp;lt;code&amp;gt;line-height: 36px&amp;lt;/code&amp;gt;, as letras com haste (g, p, f, b) transbordam para as linhas vizinhas. Tentamos acrescentar&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;amp;lt;/think&amp;amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;, as letras com haste (g, p, f, b) transbordam para as linhas vizinhas. Tentamos acrescentar`height` et &amp;lt;code&amp;gt;min-height&amp;lt;/code&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css hljs&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;.monaco-list-row {
    height: 36px !important;
    min-height: 36px !important;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Resultado :&amp;lt;strong&amp;gt;nenhuma mudança&amp;lt;/strong&amp;gt;. VS Code calcula as alturas das linhas em JavaScript e sobrescreve os valores CSS&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_solução_zoom_no_contêiner_pai&amp;quot;&amp;gt;A solução: &amp;lt;code&amp;gt;zoom&amp;lt;/code&amp;gt; no contêiner pai&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 5) ]

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;Tentativa 1
Parâmetro inexistente&amp;quot; #LightCoral {
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;Tentativa 1
Parâmetro inexistente&amp;quot; #LightCoral {
    card &amp;quot;workbench.tree.fontSize&amp;quot; as t1 {
        [VS Code ignore le paramètre]
    }
}

rectangle &amp;quot;Tentativa 2
Custom CSS + font-size&amp;quot; #LightYellow {
    card &amp;quot;.monaco-list-row { font-size: 22px }&amp;quot; as t2 {
        [Texte plus grand\nmais lignes superposées]
    }
}

rectangle &amp;quot;Tentativa 3\nfont-size + line-height + height&amp;quot; #LightYellow {
    card &amp;quot;height: 36px !important&amp;quot; as t3 {
        [VS Code override\npar JavaScript]
        [Aucun effet]
    }
}

rectangle &amp;quot;Solution\nzoom sur conteneur parent&amp;quot; #LightGreen {
    card &amp;quot;.monaco-list-rows { zoom: 1.25 }&amp;quot; as t4 {
        [Texte + espacement\nproportionnels]
        [Layout JS respecté]
        [✓ Résultat conforme]
    }
}

t1 -down-&amp;amp;gt; t2 : Pas de paramètre ?\nOn essaie le CSS
t2 -down-&amp;amp;gt; t3 : Lignes superposées ?\nOn force la hauteur
t3 -down-&amp;amp;gt; t4 : Override JS ?\nOn utilise zoom

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em vez de lutar contra o layout JavaScript, usa-se a propriedade`zoom`sobre o contêiner das linhas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css hljs&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;.explorer-viewlet .monaco-list-rows,
.sidebar .monaco-list-rows {
    zoom: 1.25;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`zoom`Aumenta uniformemente a renderização do contêiner — texto e espaçamento — sem quebrar o layout calculado pelo motor JavaScript do VS Code. O fator 1.25 corresponde aproximadamente a um aumento de 13 px para 16 px.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Resultado :&amp;lt;strong&amp;gt;os nomes de arquivos são finalmente legíveis, as linhas corretamente espaçadas&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_resumo_dos_seletores_e_do_que_funciona&amp;quot;&amp;gt;Resumo dos seletores e do que funciona&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;abordagem&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;CSS&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Resultado&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;font-size`sobre&amp;lt;/code&amp;gt;.monaco-list-row`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;font-size: 22px !important&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Texto maior mas linhas sobrepostas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;line-height&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;height`sobre&amp;lt;/code&amp;gt;.monaco-list-row`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;line-height: 36px; height: 36px&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nenhum efeito (override JS)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;zoom`sobre&amp;lt;/code&amp;gt;.monaco-list-rows`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;zoom: 1.25&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sucesso — texto + espaçamento proporcional&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_script_de_atualização_automática&amp;quot;&amp;gt;O script de atualização automática&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 5) ]

@startuml
skinparam backgroundColor #FEFEFE
autonumber

actor &amp;quot;Desenvolvedor
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE
autonumber

actor &amp;quot;Desenvolvedor
ou cron&amp;quot; as dev
participant &amp;quot;vscode-update.sh&amp;quot; as script
participant &amp;quot;vscode tarball
(code.visualstudio.com)&amp;quot; as remote
participant &amp;quot;~/apps/\nVSCode-linux-x64/&amp;quot; as local
participant &amp;quot;~/apps/\nvscode-backup&amp;quot; as backup
participant &amp;quot;workbench\n.desktop.main.css&amp;quot; as css
participant &amp;quot;product.json&amp;quot; as product

dev -&amp;amp;gt; script : ./vscode-update.sh
script -&amp;amp;gt; local : Lire version courante
script -&amp;amp;gt; remote : wget dernière version
script -&amp;amp;gt; script : Comparer versions

alt Version identique
    script --&amp;amp;gt; dev : &amp;quot;Já está atualizado!&amp;quot;
else Nouvelle version
    script -&amp;amp;gt; backup : Sauvegarder installation courante\n(horodatée)
    script -&amp;amp;gt; local : rm -rf ancienne installation
    script -&amp;amp;gt; local : mv nouvelle installation
    script -&amp;amp;gt; css : Ajouter patch CSS\n(menubar 23px, dropdown 22px)
    script -&amp;amp;gt; product : Supprimer checksums\n(python3)
    script --&amp;amp;gt; dev : &amp;quot;Atualização concluída
1.xx.yy → 1.zz.ww&amp;quot;
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_problema&amp;quot;&amp;gt;O problema&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando o VS Code se atualiza, ele substitui a pasta de instalação e&amp;lt;strong&amp;gt;Apaga todos os patches de CSS&amp;lt;/strong&amp;gt;. É preciso reaplicar as modificações manualmente a cada vez.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_script_vscode_update_sh&amp;quot;&amp;gt;O script `vscode-update.sh&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Criamos`~/apps/vscode-update.sh`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash
set -e

VSCODE_DIR=&amp;quot;$HOME/apps/VSCode-linux-x64&amp;quot;
BACKUP_DIR=&amp;quot;$HOME/apps/vscode-backup&amp;quot;
TEMP_DIR=&amp;quot;/tmp/vscode-update&amp;quot;

MENUBAR_FONT_SIZE=&amp;quot;23px&amp;quot;
DROPDOWN_FONT_SIZE=&amp;quot;22px&amp;quot;

echo &amp;quot;=== VS Code Local Updater ===&amp;quot;

CURRENT_VERSION=$(&amp;quot;$VSCODE_DIR/bin/code&amp;quot; --version 2&amp;amp;gt;/dev/null | head -1)
echo &amp;quot;Current version: $CURRENT_VERSION&amp;quot;

echo &amp;quot;Downloading latest VS Code...&amp;quot;
mkdir -p &amp;quot;$TEMP_DIR&amp;quot;
wget -q &amp;quot;https://code.visualstudio.com/sha/download?build=stable&amp;amp;amp;os=linux-x64&amp;quot; \
    -O &amp;quot;$TEMP_DIR/vscode.tar.gz&amp;quot;

echo &amp;quot;Extracting...&amp;quot;
mkdir -p &amp;quot;$TEMP_DIR/extracted&amp;quot;
tar -xzf &amp;quot;$TEMP_DIR/vscode.tar.gz&amp;quot; -C &amp;quot;$TEMP_DIR/extracted&amp;quot;

NEW_VERSION=$(&amp;quot;$TEMP_DIR/extracted/VSCode-linux-x64/bin/code&amp;quot; \
    --version 2&amp;amp;gt;/dev/null | head -1)
echo &amp;quot;New version: $NEW_VERSION&amp;quot;

if [ &amp;quot;$CURRENT_VERSION&amp;quot; = &amp;quot;$NEW_VERSION&amp;quot; ]; then
    echo &amp;quot;Already up to date!&amp;quot;
    rm -rf &amp;quot;$TEMP_DIR&amp;quot;
    exit 0
fi

echo &amp;quot;Backing up current installation...&amp;quot;
mkdir -p &amp;quot;$BACKUP_DIR&amp;quot;
cp -r &amp;quot;$VSCODE_DIR&amp;quot; &amp;quot;$BACKUP_DIR/VSCode-linux-x64-$(date +%Y%m%d-%H%M%S)&amp;quot;

echo &amp;quot;Updating...&amp;quot;
rm -rf &amp;quot;$VSCODE_DIR&amp;quot;
mv &amp;quot;$TEMP_DIR/extracted/VSCode-linux-x64&amp;quot; &amp;quot;$VSCODE_DIR&amp;quot;
rm -rf &amp;quot;$TEMP_DIR&amp;quot;

CSS_FILE=&amp;quot;$VSCODE_DIR/resources/app/out/vs/workbench/workbench.desktop.main.css&amp;quot;
echo &amp;quot;Re-applying custom CSS patch \
    (menubar=${MENUBAR_FONT_SIZE}, dropdown=${DROPDOWN_FONT_SIZE})...&amp;quot;
cat &amp;amp;gt;&amp;amp;gt; &amp;quot;$CSS_FILE&amp;quot; &amp;amp;lt;&amp;amp;lt; CSSEOF
.menubar-menu-title{font-size:${MENUBAR_FONT_SIZE}!important}\
.menubar&amp;amp;gt;.menubar-menu-button{font-size:${MENUBAR_FONT_SIZE}!important}\
.monaco-menu-option{font-size:${DROPDOWN_FONT_SIZE}!important;\
line-height:34px!important}\
.monaco-menu .action-label:not(.codicon)\
{font-size:${DROPDOWN_FONT_SIZE}!important}\
.menubar-menu-items-holder .monaco-menu .action-item .action-label\
{font-size:${DROPDOWN_FONT_SIZE}!important}
CSSEOF

PRODUCT_JSON=&amp;quot;$VSCODE_DIR/resources/app/product.json&amp;quot;
if [ -f &amp;quot;$PRODUCT_JSON&amp;quot; ]; then
    echo &amp;quot;Removing checksums to prevent integrity warning...&amp;quot;
    python3 -c &amp;quot;
import json
with open(&amp;#39;$PRODUCT_JSON&amp;#39;,&amp;#39;r&amp;#39;) as f: data=json.load(f)
data.pop(&amp;#39;checksums&amp;#39;, None)
with open(&amp;#39;$PRODUCT_JSON&amp;#39;,&amp;#39;w&amp;#39;) as f: json.dump(data,f,indent=2)
print(&amp;#39;Done.&amp;#39;)
&amp;quot;
fi

echo &amp;quot;=== Update complete: $CURRENT_VERSION -&amp;amp;gt; $NEW_VERSION ===&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O script :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Baixa a última versão estável&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Verifique se a versão difere da atual&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Salva a instalação atual (timestamp) em`~/apps/vscode-backup/`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Substitua a pasta de instalação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Reaplica&amp;lt;/strong&amp;gt;o patch CSS do menu principal&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Apague os checksums&amp;lt;/strong&amp;gt; de `product.json`para evitar o aviso de integridade&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;NOTA: As personalizações do painel Explorer via a extensão Custom CSS e o arquivo`~/.vscode-custom-css/custom.css`sobrevivem às atualizações porque vivem fora da pasta de instalação.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_arquivos_envolvidos_visão_geral&amp;quot;&amp;gt;Arquivos envolvidos — visão geral&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 6) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

rectangle &amp;quot;Instalação VS Code
(~/apps/VSCode-linux-x64/)&amp;quot; #LightCoral {
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

rectangle &amp;quot;Instalação VS Code
(~/apps/VSCode-linux-x64/)&amp;quot; #LightCoral {
    component &amp;quot;workbench.desktop\n.main.css&amp;quot; as css #LightCoral
    component &amp;quot;product.json&amp;quot; as product #LightCoral
    component &amp;quot;binário
bin/code&amp;quot; as binary #LightCoral
}

rectangle &amp;quot;Configuração do utilizador
(~/.config/Code/)&amp;quot; #LightGreen {
    component &amp;quot;settings.json&amp;quot; as settings #LightGreen
    component &amp;quot;extensões/&amp;quot; as extensions #LightGreen
}

rectangle &amp;quot;Personalização persistente
(~/.vscode-custom-css/)&amp;quot; #SkyBlue {
    component &amp;quot;custom.css
(zoom: 1.25)&amp;quot; as customcss #SkyBlue
}

rectangle &amp;quot;Automatização
(~/apps/)&amp;quot; #LightYellow {
    component &amp;quot;vscode-update.sh&amp;quot; as script #LightYellow
    component &amp;quot;vscode-backup/&amp;quot; as backup #LightYellow
}

script ..&amp;amp;gt; css : Re-patch après MAJ
script ..&amp;amp;gt; product : Supprime checksums\naprès MAJ
settings --&amp;amp;gt; customcss : Référence via\nvscode_custom_css.imports
extensions ..&amp;amp;gt; css : Custom CSS Loader\ninjecte custom.css

note bottom of css
  **Ne survit pas** aux mises à jour
  Re-patché par le script
end note

note bottom of customcss
  **Survit** aux mises à jour
  Vit hors du dossier d&amp;#39;installation
end note

note bottom of settings
  **Survit** aux mises à jour
  Vit dans ~/.config/
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 28.5714%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 42.8571%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 28.5715%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Caminho&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Papel&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Sobrevive às atualizações ?&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;~/apps/VSCode-linux-x64/&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Instalação do IDE&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Não (substituída)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;~/apps/VSCode-linux-x64/resources/app/out/vs/workbench/workbench.desktop.main.css&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;CSS do workbench (menu, dropdowns)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Não (re-patchado pelo script)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;~/apps/VSCode-linux-x64/resources/app/product.json&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Somas de verificação de integridade&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Não (re-limpo pelo script)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;~/apps/vscode-update.sh&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Script de atualização automática&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sim&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;~/.config/Code/User/settings.json&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Configurações do usuário (fontSize, CSS personalizado)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sim&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;~/.vscode-custom-css/custom.css&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;CSS personalizado para o Explorer (zoom)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sim&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_lições_aprendidas&amp;quot;&amp;gt;Lições aprendidas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 7) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
skinparam ArrowColor #444444

rectangle &amp;quot;Instalação local
 no home&amp;quot; as install #LightGreen {
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
skinparam ArrowColor #444444

rectangle &amp;quot;Instalação local
 no home&amp;quot; as install #LightGreen {
    card &amp;quot;Sem sudo
 para modificar CSS/config&amp;quot; as i1
    card &amp;quot;opencode+LLM pode\n modificar tudo&amp;quot; as i2
    card &amp;quot;Rollback = restaurar\n o backup horodatado&amp;quot; as i3
}

rectangle &amp;quot;Personalização CSS
 zoom vs font-size&amp;quot; as css #LightYellow {
    card &amp;quot;zoom &amp;amp;gt; font-size
 para lista de componentes&amp;quot; as c1
    card &amp;quot;Sobrescrever layout JS
 altura/altura-da-linha&amp;quot; as c2
    card &amp;quot;Extension Custom CSS
 requer ativação
 manual (Ctrl+Shift+P)&amp;quot; as c3
}

rectangle &amp;quot;Automação
 script de atualização&amp;quot; as auto #LightCoral {
    card &amp;quot;Reaplicar patch CSS após\n cada atualização&amp;quot; as a1
    card &amp;quot;Remover checksums\n product.json&amp;quot; as a2
    card &amp;quot;Backup horodatado
 antes da substituição&amp;quot; as a3
}

install -right-&amp;amp;gt; css : ensuite
css -right-&amp;amp;gt; auto : enfin

note top of install
  Contrôle total sans élévation
  de privilèges
end note

note top of auto
  Sans script, chaque MAJ
  efface les patches CSS
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_instalar_no_home_libera_tudo&amp;quot;&amp;gt;Instalar no home libera tudo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Uma instalação local em`~/apps`dá um controle total sobre o VS Code:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Sem`sudo`para modificar o CSS ou a configuração&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O agente opencode+LLM pode escrever nos arquivos de config e os ativos da IDE&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Rollback trivial: restaurar o backup horodatado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_zoom_font_size_para_os_componentes_de_lista&amp;quot;&amp;gt;zoom` &amp;amp;gt; &amp;lt;code&amp;gt;font-size&amp;lt;/code&amp;gt; para os componentes de lista&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;VS Code controla a altura das linhas via JavaScript. Modificar`font-size`sem poder tocar na altura alocada pelo motor de layout resulta em textos que transbordam. A propriedade`zoom`No contêiner pai amplia tudo proporcionalmente e contorna esse problema.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_extensão_custom_css_requer_uma_ativação_explícita&amp;quot;&amp;gt;A extensão Custom CSS requer uma ativação explícita&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não é um parâmetro &amp;quot;set and forget&amp;quot;. Cada modificação do arquivo CSS personalizado requer reexecutar &amp;quot;Enable Custom CSS and JS&amp;quot; a partir da paleta de comandos`Ctrl+Shift+P`).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_um_script_de_atualização_é_indispensável&amp;quot;&amp;gt;Um script de atualização é indispensável&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sem script, cada atualização do VS Code apaga os patches CSS do workbench. O script`vscode-update.sh`Automatiza o download, a substituição, o re-patch e a limpeza dos checksums.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_referências_rápidas&amp;quot;&amp;gt;Referências rápidas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Download VS Code: &amp;lt;a href=&amp;quot;https://code.visualstudio.com/Download&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://code.visualstudio.com/Download&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Extensão Personalizada de CSS e JS Loader :`be5invis.vscode-custom-css`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Problema VS Code pedindo um parâmetro nativo para a fonte do Explorerhttps://github.com/microsoft/vscode/issues/149[github.com/microsoft/vscode#149]&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Migração de um plugin Asciidoctor RevealJS para buildSrc Kotlin : engenharia reversa de uma API Groovy</title>
            <link >https://cheroliv.com//blog/2026/0102_migration_gradle_script_to_plugin_post.html</link>
            <pubDate>Thu, 16 Apr 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0102_migration_gradle_script_to_plugin_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_contexto_e_objetivo&amp;quot;&amp;gt;Contexto e objetivo&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_ponto_de_partida&amp;quot;&amp;gt;O ponto de partida&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_objetivo&amp;quot;&amp;gt;O objetivo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_primeiro_obstáculo_ruby_gems&amp;quot;&amp;gt;O primeiro obstáculo: `ruby { gems() }&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_o_açúcar_sintático_do_groovy_esconde&amp;quot;&amp;gt;O que o açúcar sintático do Groovy esconde&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_decomposição_do_mecanismo&amp;quot;&amp;gt;Decomposição do mecanismo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_em_três_partes&amp;quot;&amp;gt;A solução em três partes&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_parte_1_o_repositório_ivy_para_rubygems&amp;quot;&amp;gt;Parte 1: o repositório Ivy para rubygems&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_parte_2_a_dependência_asciidoctorgems&amp;quot;&amp;gt;Parte 2 : a dependência asciidoctorGems&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_parte_3_settings_gradle_kts&amp;quot;&amp;gt;Parte 3 : settings.gradle.kts&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introspecção_da_api_por_bytecodes&amp;quot;&amp;gt;Introspecção da API por bytecodes&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_javap&amp;quot;&amp;gt;Por que &amp;lt;code&amp;gt;javap&amp;lt;/code&amp;gt;?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_hierarquia_da_tarefa&amp;quot;&amp;gt;Hierarquia da tarefa&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_descoberta_de_forkoptions&amp;quot;&amp;gt;Descoberta de `forkOptions&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_api_de_javaforkoptions_grolifant&amp;quot;&amp;gt;A API de JavaForkOptions (grolifant)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_extensão_revealjs&amp;quot;&amp;gt;Extensão RevealJS&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configuração_do_toolchain_java&amp;quot;&amp;gt;Configuração do toolchain Java&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_de_javatoolchainservice&amp;quot;&amp;gt;O problema de JavaToolchainService&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_detecção_automática_docker&amp;quot;&amp;gt;Detecção automática Docker&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_contexto&amp;quot;&amp;gt;Contexto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estratégia&amp;quot;&amp;gt;Estratégia&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_resultado_final&amp;quot;&amp;gt;Resultado final&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_buildscript_consumidor&amp;quot;&amp;gt;O buildscript consumidor&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_settings_gradle_kts&amp;quot;&amp;gt;settings.gradle.kts&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_resumo_das_armadilhas_e_soluções&amp;quot;&amp;gt;Resumo das armadilhas e soluções&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_método_de_investigação_ler_uma_api_desconhecida_com_javap&amp;quot;&amp;gt;Método de investigação : ler uma API desconhecida com javap&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_princípio&amp;quot;&amp;gt;Princípio&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_1_localizar_o_jar_no_cache_do_gradle&amp;quot;&amp;gt;Passo 1: localizar o jar no cache do Gradle&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_2_listar_as_classes_do_jar&amp;quot;&amp;gt;Etapa 2 : listar as classes do jar&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_3_inspecionar_uma_classe&amp;quot;&amp;gt;Etapa 3: inspecionar uma classe&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_4_subir_na_hierarquia&amp;quot;&amp;gt;Passo 4: subir na hierarquia&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_5_seguir_os_tipos_desconhecidos&amp;quot;&amp;gt;Passo 5: seguir os tipos desconhecidos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_6_verificar_as_extensões_do_projeto&amp;quot;&amp;gt;Etapa 6 : verificar as extensões do projeto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_resumo_do_método&amp;quot;&amp;gt;Resumo do método&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_próxima_etapa&amp;quot;&amp;gt;próxima etapa&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_contexto_e_objetivo&amp;quot;&amp;gt;Contexto e objetivo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_ponto_de_partida&amp;quot;&amp;gt;O ponto de partida&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O projeto`slider-gradle`gera apresentações Reveal.js a partir de arquivos AsciiDoc via o plugin Gradle`org.asciidoctor.jvm.revealjs`. A configuração da tarefa principal`asciidoctorRevealJs`vivia diretamente no script de construção raiz`build.gradle.kts`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;plugins { id(&amp;quot;org.asciidoctor.jvm.revealjs&amp;quot;) }

apply&amp;amp;lt;slides.SlidesPlugin&amp;amp;gt;()

project.tasks.getByName&amp;amp;lt;AsciidoctorJRevealJSTask&amp;amp;gt;(TASK_ASCIIDOCTOR_REVEALJS) {
    repositories { ruby { gems() } }
    revealjs {
        version = &amp;quot;3.1.0&amp;quot;
        templateGitHub {
            setOrganisation(&amp;quot;hakimel&amp;quot;)
            setRepository(&amp;quot;reveal.js&amp;quot;)
            setTag(&amp;quot;3.9.1&amp;quot;)
        }
    }
    revealjsOptions {
        // ... configuration complète
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_objetivo&amp;quot;&amp;gt;O objetivo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mover toda essa configuração para`buildSrc/src/main/kotlin/slides/SlidesPlugin.kt`para que o buildscript do consumidor se reduza à:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;apply&amp;amp;lt;slides.SlidesPlugin&amp;amp;gt;()&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plugin deve ser totalmente autônomo: ele aplica por si mesmo as dependências de plugins, configura seus repositórios, e gerencia suas gems Ruby.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_primeiro_obstáculo_ruby_gems&amp;quot;&amp;gt;O primeiro obstáculo: `ruby { gems() }&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_que_o_açúcar_sintático_do_groovy_esconde&amp;quot;&amp;gt;O que o açúcar sintático do Groovy esconde&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A linha`repositories { ruby { gems() } }`é uma extensão DSL Groovy disponível apenas no contexto de execução do buildscript. Ela não existe como uma API Kotlin estática acessível a partir de buildSrc.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao tentar chamá-lo desde`SlidesPlugin.kt`, a compilação falha imediatamente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_decomposição_do_mecanismo&amp;quot;&amp;gt;Decomposição do mecanismo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Após análise do cache Gradle (&amp;lt;code&amp;gt;~/.gradle/caches/modules-2/files-2.1/rubygems/&amp;lt;/code&amp;gt;), descobre-se no arquivo`ivy-3.1.0.xml`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-xml hljs&amp;quot; data-lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;amp;lt;artifact type=&amp;#39;gem&amp;#39; url=&amp;#39;https://rubygems.org/gems/asciidoctor-revealjs-3.1.0.gem&amp;#39; /&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`ruby { gems() }`efetuava na realidade&amp;lt;strong&amp;gt;três operações distintas&amp;lt;/strong&amp;gt; :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Salvar um repositório Ivy apontando para`https://rubygems.org/gems/`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Excluir o grupo`rubygems`repositórios Maven para evitar conflitos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Salvar a gema na configuração`asciidoctorGems`para que o JRuby a carregue em tempo de execução&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Essas três responsabilidades devem ser reproduzidas separadamente em Kotlin.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_em_três_partes&amp;quot;&amp;gt;A solução em três partes&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_parte_1_o_repositório_ivy_para_rubygems&amp;quot;&amp;gt;Parte 1: o repositório Ivy para rubygems&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;project.repositories.mavenCentral() {
    content { excludeGroup(&amp;quot;rubygems&amp;quot;) }
}
project.repositories.ivy {
    url = project.uri(&amp;quot;https://rubygems.org/gems/&amp;quot;)
    patternLayout { artifact(&amp;quot;[module]-[revision].gem&amp;quot;) } &amp;lt;i class=&amp;quot;conum&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;b&amp;gt;(1)&amp;lt;/b&amp;gt;
    metadataSources { artifact() }
    content { includeGroup(&amp;quot;rubygems&amp;quot;) }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;colist arabic&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;i class=&amp;quot;conum&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;b&amp;gt;1&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;A extensão`.gem`deve estar hardcodada`[ext]&amp;lt;code&amp;gt;resolve por padrão em&amp;lt;/code&amp;gt;.jar`, que causa um erro`Resource missing`.&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
O repo Ivy é declarado diretamente em`project.repositories`e não em um bloco`repositories { }`porque o receiver deste bloco não é o`RepositoryHandler`padrão do Gradle mas uma API grolifant incompatível com a extensão`ivy`Kotlin DSL.
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_parte_2_a_dependência_asciidoctorgems&amp;quot;&amp;gt;Parte 2 : a dependência asciidoctorGems&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plugin`org.asciidoctor.jvm.gems`deve ser aplicado primeiro — ele cria a configuração`asciidoctorGems`e a tarefa`asciidoctorGemsPrepare` :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;project.plugins.apply(&amp;quot;org.asciidoctor.jvm.gems&amp;quot;)
project.plugins.apply(&amp;quot;org.asciidoctor.jvm.revealjs&amp;quot;) &amp;lt;i class=&amp;quot;conum&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;b&amp;gt;(1)&amp;lt;/b&amp;gt;

project.dependencies {
    add(&amp;quot;asciidoctorGems&amp;quot;, &amp;quot;rubygems:asciidoctor-revealjs:3.1.0@gem&amp;quot;) &amp;lt;i class=&amp;quot;conum&amp;quot; data-value=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;b&amp;gt;(2)&amp;lt;/b&amp;gt;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;colist arabic&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;i class=&amp;quot;conum&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;b&amp;gt;1&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;A ordem de aplicação é importante:`gems`antes`revealjs`. &amp;amp;lt;2&amp;amp;gt; O qualificador`@gem`força a extensão correta na dependência.&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_parte_3_settings_gradle_kts&amp;quot;&amp;gt;Parte 3 : settings.gradle.kts&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`dependencyResolutionManagement`em`settings.gradle.kts`deve permitir que os projetos declarem seus próprios repositórios:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Suppress(&amp;quot;UnstableApiUsage&amp;quot;)
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sem essa linha, o Gradle ignora os repositórios declarados no plugin e a resolução das gems falha.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introspecção_da_api_por_bytecodes&amp;quot;&amp;gt;Introspecção da API por bytecodes&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_por_que_javap&amp;quot;&amp;gt;Por que &amp;lt;code&amp;gt;javap&amp;lt;/code&amp;gt;?&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plugin`asciidoctor-gradle-jvm-slides`está em versão`4.0.0-alpha.1`. Sua documentação é inexistente ou incompleta. A única fonte confiável é a inspeção direta das classes compiladas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_hierarquia_da_tarefa&amp;quot;&amp;gt;Hierarquia da tarefa&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;javap -p -classpath asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar \
  org.asciidoctor.gradle.jvm.slides.AsciidoctorJRevealJSTask&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Resultado :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;public class AsciidoctorJRevealJSTask
  extends org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask
  implements org.asciidoctor.gradle.base.slides.SlidesToExportAware&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_descoberta_de_forkoptions&amp;quot;&amp;gt;Descoberta de `forkOptions&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao inspecionar`AbstractAsciidoctorTask`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;javap -p -classpath asciidoctor-gradle-jvm-4.0.0-alpha.1.jar \
  org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask | grep -i &amp;quot;fork\|exec\|jvm&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Encontra-se:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;final org.ysb33r.grolifant.api.v4.JavaForkOptions javaForkOptions;
public void forkOptions(org.gradle.api.Action&amp;amp;lt;org.ysb33r.grolifant.api.v4.JavaForkOptions&amp;amp;gt;);
public static final org.asciidoctor.gradle.base.process.ProcessMode JAVA_EXEC;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_api_de_javaforkoptions_grolifant&amp;quot;&amp;gt;A API de JavaForkOptions (grolifant)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`javaLauncher`não existe nesta tarefa. A API real de`org.ysb33r.grolifant.api.v4.JavaForkOptions`exposição :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;public void executable(java.lang.Object);  &amp;lt;i class=&amp;quot;conum&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;b&amp;gt;(1)&amp;lt;/b&amp;gt;
public void setExecutable(java.lang.Object);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;colist arabic&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;i class=&amp;quot;conum&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;b&amp;gt;1&amp;lt;/b&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;O método`executable(Object)`substitui a atribuição`executable = &amp;amp;#8230;&amp;amp;#8203;`que não compila (`val`não pode ser reatribuído).&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_extensão_revealjs&amp;quot;&amp;gt;Extensão RevealJS&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`revealjs { }`não é um método da tarefa mas uma&amp;lt;strong&amp;gt;extensão de projeto&amp;lt;/strong&amp;gt; :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;javap -p -classpath asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar \
  org.asciidoctor.gradle.jvm.slides.RevealJSExtension&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É acessada via :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;project.extensions.getByType&amp;amp;lt;RevealJSExtension&amp;amp;gt;().apply {
    version = &amp;quot;3.1.0&amp;quot;
    templateGitHub {
        setOrganisation(&amp;quot;hakimel&amp;quot;)
        setRepository(&amp;quot;reveal.js&amp;quot;)
        setTag(&amp;quot;3.9.1&amp;quot;)
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_configuração_do_toolchain_java&amp;quot;&amp;gt;Configuração do toolchain Java&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_problema_de_javatoolchainservice&amp;quot;&amp;gt;O problema de JavaToolchainService&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`JavaToolchainService`não é uma extensão de projeto. A chamada seguinte falha :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// ERREUR : Extension of type &amp;#39;JavaToolchainService&amp;#39; does not exist
project.extensions.getByType&amp;amp;lt;JavaToolchainService&amp;amp;gt;()&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A boa API é`serviceOf`(There is no text to translate, so output is empty.)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;import org.gradle.kotlin.dsl.support.serviceOf

project.tasks.getByName&amp;amp;lt;AsciidoctorJRevealJSTask&amp;amp;gt;(TASK_ASCIIDOCTOR_REVEALJS) {
    setInProcess(&amp;quot;JAVA_EXEC&amp;quot;)
    forkOptions {
        executable(
            project.serviceOf&amp;amp;lt;JavaToolchainService&amp;amp;gt;()
                .launcherFor {
                    languageVersion.set(JavaLanguageVersion.of(17))
                    vendor.set(JvmVendorSpec.ADOPTIUM)
                }
                .get()
                .executablePath
                .asFile
                .absolutePath
        )
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_detecção_automática_docker&amp;quot;&amp;gt;Detecção automática Docker&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_contexto&amp;quot;&amp;gt;Contexto&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plugin Asciidoctor/JRuby requer Java 17. Kotlin 2.0.x em buildSrc não suporta o Java 25 (o analisador de versão falha em`&amp;quot;25.0.2&amp;quot;`). O daemon Gradle deve portanto rodar no Java 17 ou o Docker deve ser usado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estratégia&amp;quot;&amp;gt;Estratégia&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Docker disponível → execução via contêiner`eclipse-temurin:17`(comportamento padrão)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Docker ausente + Java 17 → execução local&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Docker ausente + Java &amp;amp;gt; 17 → erro explícito&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val isDockerAvailable = try {
    Runtime.getRuntime().exec(arrayOf(&amp;quot;docker&amp;quot;, &amp;quot;info&amp;quot;)).waitFor() == 0
} catch (e: Exception) {
    false
}

val javaVersion = JavaVersion.current().majorVersion.toInt()

when {
    isDockerAvailable -&amp;amp;gt; project.tasks.register&amp;amp;lt;Exec&amp;amp;gt;(TASK_ASCIIDOCTOR_REVEALJS) {
        group = GROUP_TASK_SLIDER
        description = &amp;quot;Slider settings and generation (via Docker)&amp;quot;
        dependsOn(TASK_CLEAN_SLIDES_BUILD)
        finalizedBy(TASK_DASHBOARD_SLIDES_BUILD)
        commandLine(
            &amp;quot;docker&amp;quot;, &amp;quot;run&amp;quot;, &amp;quot;--rm&amp;quot;,
            &amp;quot;-v&amp;quot;, &amp;quot;${project.rootDir.absolutePath}:/workspace&amp;quot;,
            &amp;quot;-v&amp;quot;, &amp;quot;${System.getProperty(&amp;quot;user.home&amp;quot;)}/.gradle:/root/.gradle&amp;quot;,
            &amp;quot;-w&amp;quot;, &amp;quot;/workspace&amp;quot;,
            &amp;quot;eclipse-temurin:17&amp;quot;,
            &amp;quot;./gradlew&amp;quot;, TASK_ASCIIDOCTOR_REVEALJS
        )
        workingDir = project.rootDir
    }
    javaVersion == 17 -&amp;amp;gt; {
        project.repositories.mavenCentral() {
            content { excludeGroup(&amp;quot;rubygems&amp;quot;) }
        }
        project.repositories.ivy {
            url = project.uri(&amp;quot;https://rubygems.org/gems/&amp;quot;)
            patternLayout { artifact(&amp;quot;[module]-[revision].gem&amp;quot;) }
            metadataSources { artifact() }
            content { includeGroup(&amp;quot;rubygems&amp;quot;) }
        }
        project.extensions.getByType&amp;amp;lt;RevealJSExtension&amp;amp;gt;().apply {
            version = &amp;quot;3.1.0&amp;quot;
            templateGitHub {
                setOrganisation(&amp;quot;hakimel&amp;quot;)
                setRepository(&amp;quot;reveal.js&amp;quot;)
                setTag(&amp;quot;3.9.1&amp;quot;)
            }
        }
        project.tasks.getByName&amp;amp;lt;AsciidoctorJRevealJSTask&amp;amp;gt;(TASK_ASCIIDOCTOR_REVEALJS) {
            setInProcess(&amp;quot;JAVA_EXEC&amp;quot;)
            forkOptions {
                executable(
                    project.serviceOf&amp;amp;lt;JavaToolchainService&amp;amp;gt;()
                        .launcherFor {
                            languageVersion.set(JavaLanguageVersion.of(17))
                            vendor.set(JvmVendorSpec.ADOPTIUM)
                        }
                        .get()
                        .executablePath
                        .asFile
                        .absolutePath
                )
            }
            // ... reste de la configuration
        }
    }
    else -&amp;amp;gt; error(
        &amp;quot;Docker est requis pour exécuter $TASK_ASCIIDOCTOR_REVEALJS &amp;quot; +
        &amp;quot;avec Java $javaVersion. Installez Docker ou utilisez Java 17.&amp;quot;
    )
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_resultado_final&amp;quot;&amp;gt;Resultado final&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_buildscript_consumidor&amp;quot;&amp;gt;O buildscript consumidor&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;apply&amp;amp;lt;slides.SlidesPlugin&amp;amp;gt;()&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Isso é tudo. O plugin assume toda a responsabilidade.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_settings_gradle_kts&amp;quot;&amp;gt;settings.gradle.kts&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;pluginManagement {
    repositories {
        mavenLocal()
        gradlePluginPortal()
    }
}

plugins {
    id(&amp;quot;org.gradle.toolchains.foojay-resolver-convention&amp;quot;) version &amp;quot;0.8.0&amp;quot;
}

@Suppress(&amp;quot;UnstableApiUsage&amp;quot;)
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
}

rootProject.name = &amp;quot;slider-gradle&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_resumo_das_armadilhas_e_soluções&amp;quot;&amp;gt;Resumo das armadilhas e soluções&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 37.5%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 37.5%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Problema&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;causa&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Solução&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`ruby { gems() }`não disponível em Kotlin&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Extensão DSL Groovy apenas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Três mecanismos separados: repositório Ivy + exclusão Maven +&amp;lt;code&amp;gt;asciidoctorGems&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Gradle procura um`.jar`em vez de um`.gem`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`[ext]`resolve em`jar`por padrão&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Hardcoder`.gem`no padrão Ivy + qualificador`@gem`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;ivy { }`não compila em`repositories { }&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Receiver grolifant incompatível com o DSL Kotlin&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Chamada direta`project.repositories.ivy { }`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`javaLauncher`não resolvido&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Propriedade inexistente em`AsciidoctorJRevealJSTask`&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;setInProcess(&amp;quot;JAVA_EXEC&amp;quot;)&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;forkOptions { executable(&amp;amp;#8230;&amp;amp;#8203;) }&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`executable = &amp;amp;#8230;&amp;amp;#8203;`não compila&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;propriedade`val`dentro`JavaForkOptions`elefante grande&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Método`executable(Object)`no lugar de&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;JavaToolchainService`não encontrado via`extensions&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Este é um serviço Gradle, não uma extensão&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;project.serviceOf&amp;amp;lt;JavaToolchainService&amp;amp;gt;()&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;`revealjs { }`não resolvido na tarefa&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Extensão do projeto, não método de tarefa&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;project.extensions.getByType&amp;amp;lt;RevealJSExtension&amp;amp;gt;()&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Build falha com o Java 25&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Kotlin 2.0.x não analisa as versões Java de dois dígitos&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Detecção Docker automática + fallback Java 17&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_método_de_investigação_ler_uma_api_desconhecida_com_javap&amp;quot;&amp;gt;Método de investigação : ler uma API desconhecida com javap&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_princípio&amp;quot;&amp;gt;Princípio&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando a documentação está ausente ou incompleta, os bytecodes são a fonte da verdade. &amp;lt;code&amp;gt;javap`é a ferramenta padrão do JDK que descompila os arquivos&amp;lt;/code&amp;gt;.class`em assinaturas Java legíveis, sem precisar do código-fonte&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_passo_1_localizar_o_jar_no_cache_do_gradle&amp;quot;&amp;gt;Passo 1: localizar o jar no cache do Gradle&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Gradle baixa todas as suas dependências em`~/.gradle/caches/modules-2/files-2.1/`. A primeira etapa é encontrar o JAR que contém a classe a ser inspecionada :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;find ~/.gradle/caches -name &amp;quot;asciidoctor-gradle-jvm-slides*.jar&amp;quot; 2&amp;amp;gt;/dev/null&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Resultado :&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;/home/user/.gradle/caches/modules-2/files-2.1/org.asciidoctor/
asciidoctor-gradle-jvm-slides/4.0.0-alpha.1/.../
asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_etapa_2_listar_as_classes_do_jar&amp;quot;&amp;gt;Etapa 2 : listar as classes do jar&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes de inspecionar uma classe, verifica-se que ela realmente existe no jar:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;jar tf asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar | grep -i &amp;quot;RevealJS\|revealjs&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Isso revela todas as classes disponíveis: &amp;lt;code&amp;gt;AsciidoctorJRevealJSTask&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;RevealJSExtension&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;RevealJSOptions&amp;lt;/code&amp;gt;, etc.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_etapa_3_inspecionar_uma_classe&amp;quot;&amp;gt;Etapa 3: inspecionar uma classe&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;javap -p -classpath asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar \
org.asciidoctor.gradle.jvm.slides.AsciidoctorJRevealJSTask&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A opção`-p`mostra todos os membros, inclusive os privados. O resultado mostra imediatamente a linha chave :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;public class AsciidoctorJRevealJSTask
extends org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_passo_4_subir_na_hierarquia&amp;quot;&amp;gt;Passo 4: subir na hierarquia&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A tarefa estende`AbstractAsciidoctorTask`. Inspeciona-se à sua vez localizando primeiro o seu jar :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;find ~/.gradle/caches -name &amp;quot;asciidoctor-gradle-jvm-[0-9]*.jar&amp;quot; 2&amp;amp;gt;/dev/null

javap -p -classpath asciidoctor-gradle-jvm-4.0.0-alpha.1.jar \
org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask | grep -i &amp;quot;fork\|exec\|jvm\|java&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É aí que se descobre`forkOptions`, &amp;lt;code&amp;gt;JAVA_EXEC&amp;lt;/code&amp;gt;, et &amp;lt;code&amp;gt;javaForkOptions&amp;lt;/code&amp;gt; de tipo`org.ysb33r.grolifant.api.v4.JavaForkOptions`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_passo_5_seguir_os_tipos_desconhecidos&amp;quot;&amp;gt;Passo 5: seguir os tipos desconhecidos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`JavaForkOptions`est é uma classe grolifant desconhecida. Localizamos o seu jar :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;find ~/.gradle/caches -name &amp;quot;grolifant*.jar&amp;quot; 2&amp;amp;gt;/dev/null&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Então, inspeciona-se:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;javap -p -classpath grolifant40-legacy-api-2.0.0-alpha.6.jar \
org.ysb33r.grolifant.api.v4.JavaForkOptions&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Lá se encontra`executable(java.lang.Object)&amp;lt;code&amp;gt;— o método correto a ser chamado, ao contrário de`executable = &amp;amp;#8230;&amp;amp;#8203;`que não compila porque é uma propriedade`val&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_etapa_6_verificar_as_extensões_do_projeto&amp;quot;&amp;gt;Etapa 6 : verificar as extensões do projeto&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;para`revealjs { }`, a pergunta era : é um método da tarefa ou uma extensão de projeto? A inspeção de`AsciidoctorJRevealJSTask` não mostra nenhum método`revealjs`. Inspeciona-se então`RevealJSExtension`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;javap -p -classpath asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar \
org.asciidoctor.gradle.jvm.slides.RevealJSExtension | head -5&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;public class RevealJSExtension implements groovy.lang.GroovyObject {
public static final java.lang.String NAME;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A presença de`NAME`confirma que é uma extensão registrada no projeto, acessível via`project.extensions.getByType&amp;amp;lt;RevealJSExtension&amp;amp;gt;()`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_resumo_do_método&amp;quot;&amp;gt;Resumo do método&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 75%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Etapa&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Ação&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;find ~/.gradle/caches -name &amp;quot;*.jar&amp;quot;&amp;lt;/code&amp;gt;— localizar o jar&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;jar tf jar.jar | grep NomClasse&amp;lt;/code&amp;gt;— verificar que a classe existe&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;javap -p -classpath jar.jar NomCompletClasse&amp;lt;/code&amp;gt;— inspecionar a classe&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;4&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Identificar`extends`e subir na hierarquia&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Seguir os tipos desconhecidos nos seus próprios jars&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;6&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Procurar`NAME`para identificar uma extensão de projeto&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este método aplica-se a qualquer plugin Gradle cuja API não está documentada ou cuja versão alfa não corresponde mais à documentação existente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_próxima_etapa&amp;quot;&amp;gt;próxima etapa&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este plugin buildSrc será extraído para um projeto independente publicado no Gradle Plugin Portal ou no Maven Local. O buildscript do consumidor passará a ser:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;plugins { id(&amp;quot;slides&amp;quot;) version &amp;quot;1.0.0&amp;quot; }&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Et &amp;lt;code&amp;gt;settings.gradle.kts`será reduzido ao seu mínimo estrito sem referência a`foojay-resolver-convention&amp;lt;/code&amp;gt;, o provisionamento do JDK sendo gerenciado pelo próprio plugin ou documentado como pré-requisito.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Architecture Portfolio JBake - Visão e Casos de Uso</title>
            <link >https://cheroliv.com//blog/2026/0101_jbake_portfolio_post.html</link>
            <pubDate>Thu, 15 Jan 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0101_jbake_portfolio_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_visão_arquitetural&amp;quot;&amp;gt;Visão arquitetural&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_princípio_orientador&amp;quot;&amp;gt;Princípio orientador&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_escolha_estratégica_asciidoc_para_o_portfólio&amp;quot;&amp;gt;Escolha estratégica : AsciiDoc para o portfólio&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_modelo_de_dados&amp;quot;&amp;gt;modelo de dados&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_casos_de_uso_detalhados&amp;quot;&amp;gt;Casos de Uso Detalhados&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_uc1_adicionar_um_elemento_ao_portfólio&amp;quot;&amp;gt;UC1 : Adicionar um elemento ao portfólio&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fluxo_nominal&amp;quot;&amp;gt;Fluxo nominal&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_modelo_do_arquivo_a_ser_criado&amp;quot;&amp;gt;modelo do arquivo a ser criado&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_pontos_de_validação&amp;quot;&amp;gt;Pontos de validação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_uc2_remover_um_item_do_portfólio&amp;quot;&amp;gt;UC2: Remover um item do portfólio&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fluxo_nominal_2&amp;quot;&amp;gt;fluxo nominal&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estratégia_alternativa_arquivamento&amp;quot;&amp;gt;Estratégia alternativa : arquivamento&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_limpeza_de_recursos&amp;quot;&amp;gt;Limpeza de recursos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_uc3_definir_como_não_publicado_rascunho&amp;quot;&amp;gt;UC3 : Definir como não publicado (rascunho)&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fluxo_nominal_3&amp;quot;&amp;gt;Fluxo nominal&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estados_possíveis&amp;quot;&amp;gt;Estados possíveis&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_casos_de_uso_típicos&amp;quot;&amp;gt;Casos de uso típicos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_de_templating&amp;quot;&amp;gt;Arquitetura de Templating&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estratégia_de_templates_reutilizáveis&amp;quot;&amp;gt;Estratégia de templates reutilizáveis&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_padrão_de_extração_de_dados&amp;quot;&amp;gt;Padrão de extração de dados&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_convenções_de_nomenclatura&amp;quot;&amp;gt;Convenções de nomenclatura&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fluxo_de_publicação&amp;quot;&amp;gt;Fluxo de Publicação&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_pipeline_de_desenvolvimento&amp;quot;&amp;gt;Pipeline de desenvolvimento&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_ambientes&amp;quot;&amp;gt;Ambientes&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_extensibilidade&amp;quot;&amp;gt;extensibilidade&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_adição_de_novos_atributos&amp;quot;&amp;gt;Adição de novos atributos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_categorização_avançada&amp;quot;&amp;gt;Categorização avançada&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_boas_práticas&amp;quot;&amp;gt;Boas Práticas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_organização_dos_arquivos&amp;quot;&amp;gt;Organização dos arquivos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_gestão_de_conteúdo&amp;quot;&amp;gt;Gestão de conteúdo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_desempenho&amp;quot;&amp;gt;Desempenho&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_visão_arquitetural&amp;quot;&amp;gt;Visão arquitetural&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_princípio_orientador&amp;quot;&amp;gt;Princípio orientador&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A arquitetura baseia-se no princípio de &amp;lt;strong&amp;gt;separação de preocupações&amp;lt;/strong&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Conteúdo&amp;lt;/strong&amp;gt; : ficheiros AsciiDoc estruturados com metadados ricos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Apresentação : templates Thymeleaf reutilizáveis&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Dados&amp;lt;/strong&amp;gt; : modelo extraído automaticamente pelo JBake a partir dos atributos AsciiDoc&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Style&amp;lt;/strong&amp;gt; : Bootstrap 5 para consistência visual&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_escolha_estratégica_asciidoc_para_o_portfólio&amp;quot;&amp;gt;Escolha estratégica : AsciiDoc para o portfólio&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;caption class=&amp;quot;title&amp;quot;&amp;gt;Table 1. Por que AsciiDoc ?&amp;lt;/caption&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 33.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 66.6667%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Critério&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;vantagem&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Coerência&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Mesmo formato que os artigos de blog&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Metadados&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Atributos estruturados e extensíveis (&amp;lt;code&amp;gt;project-*&amp;lt;/code&amp;gt;)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Manutenabilidade&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Edição simples em texto, versionável Git&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Flexibilidade&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Pode conter conteúdo rico (tabelas, código, imagens)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Modelagem&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;JBake extrai automaticamente os atributos para o Thymeleaf&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_modelo_de_dados&amp;quot;&amp;gt;modelo de dados&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cada projeto de portfólio é um documento AsciiDoc com:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Metadados de cabeçalho&amp;lt;/strong&amp;gt; : informações estruturadas (cliente, duração, tecnologias, etc.)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Corpo do documento&amp;lt;/strong&amp;gt;: descrição narrativa, desafios, soluções, resultados&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Atributos personalizados&amp;lt;/strong&amp;gt; : prefixados`project-*`para extração automática&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-c4f40eb68af74c037ff02a1abed1ac32.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;386&amp;quot; height=&amp;quot;811&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_casos_de_uso_detalhados&amp;quot;&amp;gt;Casos de Uso Detalhados&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_uc1_adicionar_um_elemento_ao_portfólio&amp;quot;&amp;gt;UC1 : Adicionar um elemento ao portfólio&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_fluxo_nominal&amp;quot;&amp;gt;Fluxo nominal&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 4) ]

@startuml
skinparam backgroundColor #FEFEFE
actor Développeur as dev
participant &amp;quot;Editor
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE
actor Développeur as dev
participant &amp;quot;Editor
Text&amp;quot; as editor
participant &amp;quot;Arquivo\nAsciiDoc&amp;quot; as file
participant &amp;quot;JBake\nEngine&amp;quot; as jbake
participant &amp;quot;Site
Estático&amp;quot; as site

dev -&amp;amp;gt; editor : Créer nouveau fichier
activate editor
editor -&amp;amp;gt; file : portfolio/nouveau-projet.adoc
activate file

dev -&amp;amp;gt; editor : Rédiger en-tête avec métadonnées\n(titre, type=project, status=published,\nattributs project-*)
dev -&amp;amp;gt; editor : Rédiger contenu narratif\n(contexte, défis, solutions)
dev -&amp;amp;gt; editor : Ajouter images dans assets/img/portfolio/

editor -&amp;amp;gt; file : Sauvegarder
deactivate editor

dev -&amp;amp;gt; jbake : Lancer build (jbake -b)
activate jbake
jbake -&amp;amp;gt; file : Lire et parser
jbake -&amp;amp;gt; jbake : Extraire métadonnées
jbake -&amp;amp;gt; jbake : Convertir AsciiDoc → HTML
jbake -&amp;amp;gt; jbake : Appliquer template project.html
jbake -&amp;amp;gt; jbake : Ajouter à la liste portfolio.html
jbake -&amp;amp;gt; site : Générer pages statiques
deactivate jbake

dev -&amp;amp;gt; site : Vérifier résultat
activate site
site --&amp;amp;gt; dev : Afficher projet
deactivate site
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_modelo_do_arquivo_a_ser_criado&amp;quot;&amp;gt;modelo do arquivo a ser criado&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O desenvolvedor cria`content/portfolio/nom-projet.adoc`com uma estrutura padronizada:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Cabeçalho com todos os atributos necessários&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Seções normalizadas (Contexto, Desafios, Soluções, Resultados)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Nomenclatura coerente das imagens&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_pontos_de_validação&amp;quot;&amp;gt;Pontos de validação&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Os atributos obrigatórios estão presentes (&amp;lt;code&amp;gt;jbake-type&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jbake-status&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;project-thumbnail&amp;lt;/code&amp;gt;)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;As imagens referenciadas existem em`assets/img/portfolio/`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O build JBake é bem-sucedido sem erro&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O projeto aparece na página do portfólio&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;A página individual do projeto aparece corretamente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_uc2_remover_um_item_do_portfólio&amp;quot;&amp;gt;UC2: Remover um item do portfólio&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_fluxo_nominal_2&amp;quot;&amp;gt;fluxo nominal&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 5) ]

@startuml
skinparam backgroundColor #FEFEFE
actor Développeur as dev
participant &amp;quot;Sistema\nArquivos&amp;quot; as fs
participant &amp;quot;JBake
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE
actor Développeur as dev
participant &amp;quot;Sistema\nArquivos&amp;quot; as fs
participant &amp;quot;JBake
Motor&amp;quot; as jbake
participant &amp;quot;Site\nestático&amp;quot; as site
database &amp;quot;Cache\nJBake&amp;quot; as cache

dev -&amp;amp;gt; fs : Supprimer portfolio/projet-ancien.adoc
activate fs
fs --&amp;amp;gt; dev : Fichier supprimé
deactivate fs

dev -&amp;amp;gt; fs : (Optionnel) Supprimer images associées\nassets/img/portfolio/projet-ancien-*
activate fs
fs --&amp;amp;gt; dev : Images supprimées
deactivate fs

dev -&amp;amp;gt; cache : Nettoyer cache JBake
activate cache
cache --&amp;amp;gt; dev : Cache vidé
deactivate cache

dev -&amp;amp;gt; jbake : Rebuild complet (jbake -b)
activate jbake
jbake -&amp;amp;gt; jbake : Scanner content/portfolio/
jbake -&amp;amp;gt; jbake : Projet absent → non généré
jbake -&amp;amp;gt; jbake : Régénérer portfolio.html\n(sans le projet supprimé)
jbake -&amp;amp;gt; site : Déployer nouveau build
deactivate jbake

dev -&amp;amp;gt; site : Vérifier
activate site
site --&amp;amp;gt; dev : Projet absent de la liste
deactivate site
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_estratégia_alternativa_arquivamento&amp;quot;&amp;gt;Estratégia alternativa : arquivamento&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em vez de excluir definitivamente, possibilidade de criar uma pasta`content/portfolio/archive/` :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Mover o arquivo em vez de excluí-lo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Permite restaurar facilmente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Mantenha o histórico do Git mais claro&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_limpeza_de_recursos&amp;quot;&amp;gt;Limpeza de recursos&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Verificar imagens órfãs em`assets/img/portfolio/`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Remover as imagens não referenciadas por outros projetos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Limpar o cache JBake para evitar referências fantasma&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_uc3_definir_como_não_publicado_rascunho&amp;quot;&amp;gt;UC3 : Definir como não publicado (rascunho)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_fluxo_nominal_3&amp;quot;&amp;gt;Fluxo nominal&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-ea5a4f20ac88987652487688528eada9.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;783&amp;quot; height=&amp;quot;594&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_estados_possíveis&amp;quot;&amp;gt;Estados possíveis&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-a4b71e9dbf30083d131223257db47469.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;556&amp;quot; height=&amp;quot;448&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_casos_de_uso_típicos&amp;quot;&amp;gt;Casos de uso típicos&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Projeto em fase de redação&amp;lt;/strong&amp;gt; : criar como rascunho, publicar quando pronto&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Projeto confidencial temporariamente&amp;lt;/strong&amp;gt; : passar para draft durante o acordo do cliente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Atualização principal&amp;lt;/strong&amp;gt; : mudar para rascunho, editar, republicar&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A/B testing&amp;lt;/strong&amp;gt; : duplicar em rascunho, testar, publicar a melhor versão&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_arquitetura_de_templating&amp;quot;&amp;gt;Arquitetura de Templating&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estratégia_de_templates_reutilizáveis&amp;quot;&amp;gt;Estratégia de templates reutilizáveis&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-126dbf148ebd98a966dcd20d639eaaec.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;881&amp;quot; height=&amp;quot;350&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_padrão_de_extração_de_dados&amp;quot;&amp;gt;Padrão de extração de dados&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;JBake transforma automaticamente os atributos AsciiDoc em propriedades acessíveis no Thymeleaf :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-8cd09abdf9a8787f0fca59cd6a661fdf.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;891&amp;quot; height=&amp;quot;347&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_convenções_de_nomenclatura&amp;quot;&amp;gt;Convenções de nomenclatura&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Atributos do projeto&amp;lt;/strong&amp;gt; : prefixo`project-*` (ex: &amp;lt;code&amp;gt;project-client&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;project-tech-stack&amp;lt;/code&amp;gt;)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ficheiros&amp;lt;/strong&amp;gt;: kebab-case (ex:`ecommerce-platform.adoc`)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Imagens&amp;lt;/strong&amp;gt; : prefixo nome-projeto (ex:`ecommerce-platform-thumb.jpg`)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;modelos&amp;lt;/strong&amp;gt; : nome funcional (ex:`project-card.html`, &amp;lt;code&amp;gt;tech-badge.html&amp;lt;/code&amp;gt;)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_fluxo_de_publicação&amp;quot;&amp;gt;Fluxo de Publicação&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_pipeline_de_desenvolvimento&amp;quot;&amp;gt;Pipeline de desenvolvimento&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-9b8b335bedc9baf971f28bc1f2776687.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;386&amp;quot; height=&amp;quot;811&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_ambientes&amp;quot;&amp;gt;Ambientes&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 20%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 40%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 40%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Ambiente&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;uso&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Status aceito&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Local&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Desenvolvimento e pré-visualização&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;rascunho, publicado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Encenação&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Validação pré-produção&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;publicado apenas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;produção&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Site público&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;publicado apenas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_extensibilidade&amp;quot;&amp;gt;extensibilidade&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_adição_de_novos_atributos&amp;quot;&amp;gt;Adição de novos atributos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para enriquecer o modelo de dados, simplesmente adicionar novos atributos prefixados`project-*`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;project-awards&amp;lt;/code&amp;gt;: Prêmios e reconhecimentos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;project-testimonial&amp;lt;/code&amp;gt;: Citação do cliente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;project-team-size&amp;lt;/code&amp;gt;: Tamanho da equipe&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;`project-budget-range`faixa orçamentária&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Estes atributos ficam automaticamente disponíveis nos templates sem modificação no motor JBake.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_categorização_avançada&amp;quot;&amp;gt;Categorização avançada&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/diag-plantuml-md5-b98c4bec6882f5ef3e74ce4f202dd934.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;561&amp;quot; height=&amp;quot;259&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_boas_práticas&amp;quot;&amp;gt;Boas Práticas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_organização_dos_arquivos&amp;quot;&amp;gt;Organização dos arquivos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Um arquivo = um projeto&amp;lt;/strong&amp;gt; : evitar misturar vários projetos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Imagens na pasta dedicada&amp;lt;/strong&amp;gt; :`assets/img/portfolio/nom-projet/`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Nomenclatura consistente&amp;lt;/strong&amp;gt; : facilita pesquisa e manutenção&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Versioning Git&amp;lt;/strong&amp;gt; : rastreamento completo das modificações&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_gestão_de_conteúdo&amp;quot;&amp;gt;Gestão de conteúdo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Status de rascunho padrão&amp;lt;/strong&amp;gt; : publicar somente quando pronto&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Revisão pré-publicação&amp;lt;/strong&amp;gt; : validação de qualidade e confidencialidade&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Metadados completos : preencher todos os campos pertinentes&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Conteúdo narrativo rico&amp;lt;/strong&amp;gt; : não se limitar aos metadados&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_desempenho&amp;quot;&amp;gt;Desempenho&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Otimizar imagens&amp;lt;/strong&amp;gt; : compressão antes do commit&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Pagination se necessário&amp;lt;/strong&amp;gt; : se &amp;amp;gt;20 projetos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Lazy loading&amp;lt;/strong&amp;gt; : imagens das galerias carregadas sob demanda&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Cache do navegador&amp;lt;/strong&amp;gt; : cabeçalhos apropriados para assets&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta arquitetura permite :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Simplicidade de uso&amp;lt;/strong&amp;gt; : adicionar um projeto = criar um arquivo de texto&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Flexibilidade&amp;lt;/strong&amp;gt; : extensível via novos atributos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Manutenabilidade&amp;lt;/strong&amp;gt; : separação conteúdo/apresentação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Rastreabilidade&amp;lt;/strong&amp;gt; : versionamento Git completo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Automação&amp;lt;/strong&amp;gt; : build e implantação contínua possível&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A escolha do AsciiDoc garante a coerência com o resto do site, oferecendo ao mesmo tempo a riqueza dos metadados necessários a um portfólio profissional.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Arquitetura Híbrida: O Melhor dos Dois Mundos com Supabase e Spring Boot</title>
            <link >https://cheroliv.com//blog/2026/0100_jbake_supabase_springboot_hybride_post.html</link>
            <pubDate>Wed, 14 Jan 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2026/0100_jbake_supabase_springboot_hybride_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 12 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Em vez de escolher entre a simplicidade do Supabase e a potência do Spring Boot, por que não combinar os dois? Descubra uma arquitetura híbrida que aproveite o melhor de cada tecnologia.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;tic&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;(Note: No characters)&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_falso_dilema_da_arquitetura_moderna&amp;quot;&amp;gt;O Falso Dilema da Arquitetura Moderna&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando se constrói uma aplicação moderna com um frontend estático, muitas vezes nos deparamos com uma escolha binária:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Apostar tudo numa solução Backend-as-a-Service&amp;lt;/strong&amp;gt;(Supabase, Firebase) - simples mas limitada&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Construir um backend completo&amp;lt;/strong&amp;gt;(Spring Boot, Node.js) - poderoso mas complexo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta escolha é um falso dilema. A arquitetura híbrida propõe um terceiro caminho: &amp;lt;strong&amp;gt;usar cada tecnologia onde ela se destaca&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_visão_arquitetural&amp;quot;&amp;gt;Visão arquitetural&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_arquitetura_tradicional_monolítica&amp;quot;&amp;gt;A Arquitetura Tradicional: Monolítica&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;traditional-architecture.svg&amp;quot; alt=&amp;quot;traditional architecture&amp;quot; width=&amp;quot;1738&amp;quot; height=&amp;quot;5652&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nessa abordagem, mesmo as operações mais simples (leitura de um artigo, criação de um comentário) devem passar pelo seu backend. Você paga o custo da complexidade desde o primeiro dia.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_arquitetura_baas_todo_delegado&amp;quot;&amp;gt;A Arquitetura BaaS : Todo Delegado&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;baas-architecture.svg&amp;quot; alt=&amp;quot;baas architecture&amp;quot; width=&amp;quot;504&amp;quot; height=&amp;quot;432&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Do outro lado, delegar tudo a um BaaS é sedutor no início, mas mostra rapidamente seus limites assim que a lógica de negócio se complexifica.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_arquitetura_híbrida_separação_das_responsabilidades&amp;quot;&amp;gt;A Arquitetura Híbrida : Separação das Responsabilidades&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;hybrid-overview.svg&amp;quot; alt=&amp;quot;hybrid overview&amp;quot; width=&amp;quot;1747&amp;quot; height=&amp;quot;5760&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A arquitetura híbrida separa claramente as responsabilidades de acordo com a complexidade e a natureza das operações.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_princípios_de_design&amp;quot;&amp;gt;Princípios de Design&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_princípio_1_começar_simples_evoluir_inteligentemente&amp;quot;&amp;gt;Princípio 1 : Começar Simples, Evoluir Inteligentemente&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;evolution-phases.svg&amp;quot; alt=&amp;quot;evolution phases&amp;quot; width=&amp;quot;458&amp;quot; height=&amp;quot;392&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Não construa o Spring Boot se você não precisar dele.&amp;lt;/strong&amp;gt;Comece com o Supabase, adicione o Spring Boot quando a complexidade o justificar.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_princípio_2_separação_por_natureza_da_operação&amp;quot;&amp;gt;Princípio 2: Separação por natureza da operação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;operation-types.svg&amp;quot; alt=&amp;quot;operation types&amp;quot; width=&amp;quot;1266&amp;quot; height=&amp;quot;241&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_princípio_3_uma_única_fonte_de_verdade&amp;quot;&amp;gt;Princípio 3: Uma Única Fonte de Verdade&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;single-source-truth.svg&amp;quot; alt=&amp;quot;single source truth&amp;quot; width=&amp;quot;1752&amp;quot; height=&amp;quot;5700&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao compartilhar a mesma instância PostgreSQL, Supabase e Spring Boot trabalham nos mesmos dados sem sincronização complexa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_orquestração_dos_fluxos&amp;quot;&amp;gt;Orquestração dos Fluxos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_flux_1_autenticação_centralizada&amp;quot;&amp;gt;Flux 1 : Autenticação Centralizada&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;auth-orchestration.svg&amp;quot; alt=&amp;quot;auth orchestration&amp;quot; width=&amp;quot;1078&amp;quot; height=&amp;quot;950&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ponto-chave&amp;lt;/strong&amp;gt;Um único processo de autenticação, um único JWT, utilizável em qualquer lugar.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_fluxo_2_orquestração_de_um_processo_de_negócio_complexo&amp;quot;&amp;gt;Fluxo 2 : Orquestração de um Processo de Negócio Complexo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;business-orchestration.svg&amp;quot; alt=&amp;quot;business orchestration&amp;quot; width=&amp;quot;1197&amp;quot; height=&amp;quot;990&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O que o Spring Boot traz&amp;lt;/strong&amp;gt;Orquestração fiável de processos complexos envolvendo vários sistemas com garantia transacional.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_flux_3_jobs_agendados_e_sincronizações&amp;quot;&amp;gt;Flux 3 : Jobs Agendados e Sincronizações&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 4) ]

@startuml
skinparam backgroundColor #FEFEFE

participant &amp;quot;Escalonador
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE

participant &amp;quot;Escalonador
(Spring Boot)&amp;quot; as scheduler
participant &amp;quot;Serviço de relatório&amp;quot; as report
participant &amp;quot;PostgreSQL&amp;quot; as db
participant &amp;quot;API externa&amp;quot; as api
participant &amp;quot;Serviço de e-mail&amp;quot; as email
participant &amp;quot;Supabase Storage&amp;quot; as storage

== Job Quotidien : 2h00 ==
activate scheduler
scheduler -&amp;amp;gt; report : Déclenche génération rapport

activate report
report -&amp;amp;gt; db : Récupère données\ndes dernières 24h
db -&amp;amp;gt; report : Dataset

report -&amp;amp;gt; report : Calculs &amp;amp;amp; Agrégations
report -&amp;amp;gt; report : Génération PDF

report -&amp;amp;gt; storage : Upload PDF
storage -&amp;amp;gt; report : URL publique

report -&amp;amp;gt; email : Envoie aux admins\navec lien PDF
deactivate report

== Job Hebdomadaire : Dimanche 3h00 ==
scheduler -&amp;amp;gt; report : Synchronisation externe

activate report
report -&amp;amp;gt; api : Fetch données externes
api -&amp;amp;gt; report : Nouvelles données

report -&amp;amp;gt; db : Mise à jour tables
report -&amp;amp;gt; db : Nettoyage données obsolètes
deactivate report

== Job Mensuel : 1er du mois ==
scheduler -&amp;amp;gt; report : Facturation

activate report
report -&amp;amp;gt; db : Liste clients actifs
report -&amp;amp;gt; report : Calcul montants
report -&amp;amp;gt; api : Génération factures (Stripe)
report -&amp;amp;gt; email : Envoi factures
deactivate report

deactivate scheduler

note right of scheduler
  **Impossible avec Supabase seul**
  Edge Functions non adaptées
  pour jobs longs et récurrents
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O que o Spring Boot traz&amp;lt;/strong&amp;gt;: Trabalhos agendados confiáveis com gerenciamento de estado, nova tentativa automática e execução garantida&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_matriz_de_decisão&amp;quot;&amp;gt;Matriz de Decisão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_quando_usar_supabase&amp;quot;&amp;gt;Quando usar Supabase?&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;supabase-decision.svg&amp;quot; alt=&amp;quot;supabase decision&amp;quot; width=&amp;quot;1030&amp;quot; height=&amp;quot;445&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_quando_usar_o_spring_boot&amp;quot;&amp;gt;Quando usar o Spring Boot?&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;springboot-decision.svg&amp;quot; alt=&amp;quot;springboot decision&amp;quot; width=&amp;quot;1142&amp;quot; height=&amp;quot;485&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_exemplos_de_arquitetura_por_tipo_de_projeto&amp;quot;&amp;gt;Exemplos de Arquitetura por Tipo de Projeto&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_blog_portfólio&amp;quot;&amp;gt;Blog / Portfólio&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;blog-architecture.svg&amp;quot; alt=&amp;quot;blog architecture&amp;quot; width=&amp;quot;1328&amp;quot; height=&amp;quot;242&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;veredicto&amp;lt;/strong&amp;gt;: 100% Supabase basta amplamente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_aplicação_saas&amp;quot;&amp;gt;Aplicação SaaS&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;saas-architecture.svg&amp;quot; alt=&amp;quot;saas architecture&amp;quot; width=&amp;quot;1773&amp;quot; height=&amp;quot;5748&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;veredicto&amp;lt;/strong&amp;gt;: Arquitetura híbrida necessária. Supabase para o essencial, Spring Boot para a parte crítica (pagamentos, faturamento).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_e_comércio&amp;quot;&amp;gt;e-comércio&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;ecommerce-architecture.svg&amp;quot; alt=&amp;quot;ecommerce architecture&amp;quot; width=&amp;quot;1747&amp;quot; height=&amp;quot;5748&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;veredicto&amp;lt;/strong&amp;gt;: Spring Boot indispensável. Supabase apenas para auth e catálogo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_estratégia_de_migração_progressiva&amp;quot;&amp;gt;Estratégia de Migração Progressiva&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;migration-strategy.svg&amp;quot; alt=&amp;quot;migration strategy&amp;quot; width=&amp;quot;2050&amp;quot; height=&amp;quot;954&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_custos_e_considerações_operacionais&amp;quot;&amp;gt;Custos e Considerações Operacionais&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estrutura_dos_custos&amp;quot;&amp;gt;Estrutura dos custos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 13) ]

@startuml
skinparam backgroundColor #FEFEFE

package &amp;quot;Custos Mensais Estimados&amp;quot; {

  rectangle &amp;quot;Fase MVP\n(0-1000 utilizadores)&amp;quot; #LightGreen {
    [JBake (GitHub Pages): 0€]
    [Supabase: 0€]
    [Spring Boot: N/A]
    note bottom: **Total: 0€/mois**
  }

  rectangle &amp;quot;Fase Crescimento
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
skinparam backgroundColor #FEFEFE

package &amp;quot;Custos Mensais Estimados&amp;quot; {

  rectangle &amp;quot;Fase MVP\n(0-1000 utilizadores)&amp;quot; #LightGreen {
    [JBake (GitHub Pages): 0€]
    [Supabase: 0€]
    [Spring Boot: N/A]
    note bottom: **Total: 0€/mois**
  }

  rectangle &amp;quot;Fase Crescimento
(1k-10k usuários)&amp;quot; #LightYellow {
    [JBake (GitHub Pages): 0€]
    [Supabase: 0€]
    [Spring Boot (Cloud Run): 20-50€]
    [PostgreSQL (si séparé): 0-30€]
    note bottom: **Total: 20-80€/mois**
  }

  rectangle &amp;quot;Fase de Escala
(10k-100k utilizadores)&amp;quot; #LightCoral {
    [JBake (Netlify Pro): 19€]
    [Supabase Pro: 25€]
    [Spring Boot (instances multiples): 100-300€]
    [Redis Cache: 20-50€]
    [Monitoring: 20€]
    note bottom: **Total: 184-414€/mois**
  }
}

note right of &amp;quot;Fase de Escala
(10k-100k utilizadores)&amp;quot;
  À ce stade, les revenus
  justifient largement les coûts
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_responsabilidades_operacionais&amp;quot;&amp;gt;Responsabilidades Operacionais&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;operational-responsibilities.svg&amp;quot; alt=&amp;quot;operational responsibilities&amp;quot; width=&amp;quot;1787&amp;quot; height=&amp;quot;5676&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_o_equilíbrio_perfeito&amp;quot;&amp;gt;Conclusão : O Equilíbrio Perfeito&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A arquitetura híbrida Supabase + Spring Boot não é um compromisso, é uma&amp;lt;strong&amp;gt;sinergia&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_os_princípios_a_reter&amp;quot;&amp;gt;Os Princípios a Reter&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;key-principles.svg&amp;quot; alt=&amp;quot;key principles&amp;quot; width=&amp;quot;581&amp;quot; height=&amp;quot;316&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_quando_escolher_esta_arquitetura&amp;quot;&amp;gt;Quando escolher esta arquitetura?&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;�✅ Esta arquitetura é ideal se:&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você quer começar rapidamente (MVP em dias, não em meses)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você antecipa um aumento da complexidade do negócio&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você quer minimizar os custos iniciais&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você gosta do PostgreSQL e quer uma fonte de verdade única&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você aprecia Kotlin e as coroutines para o código de negócios&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você quer evitar o vendor lock-in total&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;❌ Esta arquitetura NÃO é adequada se :&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Seu projeto é simples e permanecerá simples (blog pessoal → 100% Supabase basta)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você já tem uma stack de backend estabelecida que você domina&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você prefere um monolito tradicional&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você precisa de .NET, Python ou outra linguagem de backend&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_visão_geral_final&amp;quot;&amp;gt;Visão Geral Final&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 35) ]

@startuml
skinparam backgroundColor #FEFEFE

package &amp;quot;Arquitetura Híbrida Completa&amp;quot; {

  actor &amp;quot;Usuários&amp;quot; as users

  rectangle &amp;quot;Frontend (Estático)&amp;quot; #SkyBlue {
    [JBake / GitHub Pages]
    note right: Gratuit, CDN global
  }

  rectangle &amp;quot;Backend Simples (Supabase)&amp;quot; #LightGreen {
    [Auth OAuth2]
    [CRUD APIs]
    [Storage]
    [Realtime]
    note right
      Gratuit jusqu&amp;#39;à 50k users
      Zéro configuration serveur
    end note
  }

  rectangle &amp;quot;Backend de negócio (Spring Boot)&amp;quot; #Coral {
    [Orchestration]
    [Jobs Planifiés]
    [Intégrations]
    [Transactions]
    note right
      Activé uniquement
      si nécessaire
    end note
  }

  database &amp;quot;PostgreSQL
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
skinparam backgroundColor #FEFEFE

package &amp;quot;Arquitetura Híbrida Completa&amp;quot; {

  actor &amp;quot;Usuários&amp;quot; as users

  rectangle &amp;quot;Frontend (Estático)&amp;quot; #SkyBlue {
    [JBake / GitHub Pages]
    note right: Gratuit, CDN global
  }

  rectangle &amp;quot;Backend Simples (Supabase)&amp;quot; #LightGreen {
    [Auth OAuth2]
    [CRUD APIs]
    [Storage]
    [Realtime]
    note right
      Gratuit jusqu&amp;#39;à 50k users
      Zéro configuration serveur
    end note
  }

  rectangle &amp;quot;Backend de negócio (Spring Boot)&amp;quot; #Coral {
    [Orchestration]
    [Jobs Planifiés]
    [Intégrations]
    [Transactions]
    note right
      Activé uniquement
      si nécessaire
    end note
  }

  database &amp;quot;PostgreSQL
Fonte única de verdade&amp;quot; as db #LightGray

  users --&amp;amp;gt; [JBake / GitHub Pages]

  [JBake / GitHub Pages] --&amp;amp;gt; [Auth OAuth2]
  [JBake / GitHub Pages] --&amp;amp;gt; [CRUD APIs]
  [JBake / GitHub Pages] --&amp;amp;gt; [Storage]
  [JBake / GitHub Pages] --&amp;amp;gt; [Realtime]
  [JBake / GitHub Pages] --&amp;amp;gt; [Orchestration]

  [Auth OAuth2] --&amp;amp;gt; db
  [CRUD APIs] --&amp;amp;gt; db
  [Orchestration] --&amp;amp;gt; db
  [Jobs Planifiés] --&amp;amp;gt; db
  [Intégrations] --&amp;amp;gt; db
}

note bottom of db
  **Une seule base, deux consommateurs**
  • Supabase pour le simple
  • Spring Boot pour le complexe
  • Aucune synchronisation requise
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_melhor_dos_dois_mundos&amp;quot;&amp;gt;O Melhor dos Dois Mundos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta arquitetura lhe dá :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;🚀 A Velocidade do Supabase&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Início em horas, não em semanas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Autenticação OAuth2 em poucos cliques&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;APIs REST geradas automaticamente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Configuração zero do servidor&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A Potência do Spring Boot&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Kotlin e Corrotinas para código assíncrono elegante&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Orquestração de processos de negócios complexos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Trabalhos agendados fiáveis&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Transações ACID garantidas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ecossistema Spring completo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;�💰 A economia progressiva&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;0€ para iniciar e validar&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Custos que acompanham o crescimento&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Sem sobreengenharia prematura&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;�🎯 Flexibilidade Arquitetural&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Migração progressiva sem reestruturação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Adição do Spring Boot apenas se necessário&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Sem vendor lock-in total&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Arquitetura evolutiva&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_para_ir_mais_longe&amp;quot;&amp;gt;Para Ir Mais Longe&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_recursos_técnicos&amp;quot;&amp;gt;Recursos Técnicos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Documentação Supabase:&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://supabase.com/docs/guides/auth&amp;quot;&amp;gt;Guia de Autenticação&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://supabase.com/docs/guides/database/postgres/row-level-security&amp;quot;&amp;gt;Segurança em nível de linha&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://supabase.com/docs/guides/api&amp;quot;&amp;gt;APIs geradas automaticamente&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Documentação Spring Boot :&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://spring.io/guides/tutorials/spring-boot-kotlin/&amp;quot;&amp;gt;Spring Boot com Kotlin&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://spring.io/blog/2019/04/12/going-reactive-with-spring-coroutines-and-kotlin-flow&amp;quot;&amp;gt;Corrotinas e Spring&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/jwt.html&amp;quot;&amp;gt;Servidor de Recursos JWT&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Artigos Complementares:&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Segurança das APIs com JWT&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Otimização do desempenho do PostgreSQL&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Padrões de migração progressiva&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Gestão de erros em arquitetura distribuída&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_casos_de_uso_reais&amp;quot;&amp;gt;Casos de uso reais&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta arquitetura híbrida é utilizada com sucesso em:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;SaaS B2B&amp;lt;/strong&amp;gt;: Autenticação Supabase, faturação Spring Boot&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Mercados&amp;lt;/strong&amp;gt;: Catálogo Supabase, transações Spring Boot&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Plataformas de conteúdo&amp;lt;/strong&amp;gt;: Artigos Supabase, analytics Spring Boot&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ferramentas internas&amp;lt;/strong&amp;gt;: CRUD Supabase, fluxos de trabalho Spring Boot&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_checklist_de_inicialização&amp;quot;&amp;gt;Checklist de inicialização&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fase 1 - Fundações (Semana 1)&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist checklist&amp;quot;&amp;gt;
&amp;lt;ul class=&amp;quot;checklist&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;i class=&amp;quot;fa fa-square-o&amp;quot;&amp;gt;&amp;lt;/i&amp;gt; Criar conta Supabase - [ ] Configurar projeto PostgreSQL - [ ] Ativar Auth (Google, GitHub) - [ ] Definir esquema inicial - [ ] Configurar Row Level Security Testar APIs desde o JBake&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Phase 2 - MVP (Semana 2-3)&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist checklist&amp;quot;&amp;gt;
&amp;lt;ul class=&amp;quot;checklist&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;i class=&amp;quot;fa fa-square-o&amp;quot;&amp;gt;&amp;lt;/i&amp;gt; Implementar páginas principais - [ ] Integrar autenticação OAuth2 - [ ] Criar formulários CRUD - [ ] Configurar armazenamento para arquivos - [ ] Implantar no GitHub Pages - [ ] Testar em condições reais&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fase 3 - Evolução (Conforme necessidades)&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist checklist&amp;quot;&amp;gt;
&amp;lt;ul class=&amp;quot;checklist&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;i class=&amp;quot;fa fa-square-o&amp;quot;&amp;gt;&amp;lt;/i&amp;gt; Identificar necessidades de lógica complexa - [ ] Criar projeto Spring Boot se necessário - [ ] Configurar validação JWT Supabase - [ ] Conectar ao PostgreSQL Supabase - [ ] Migrar funcionalidades complexas - [ ] Implementar trabalhos programados - [ ] Implantar Spring Boot (Cloud Run, etc.)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_anti_padrões_a_evitar&amp;quot;&amp;gt;Anti-padrões a evitar&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;�❌ Não fazer :&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Duplicar os dados entre Supabase e Spring Boot&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Criar duas bases PostgreSQL separadas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Codificar a autenticação você mesmo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Usar o Spring Boot para um CRUD simples&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Exagerar na arquitetura desde o início&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ignorar o Row Level Security do Supabase&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;�✅ Fazer melhor:&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Compartilhar uma única base PostgreSQL&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Deixar o Supabase gerenciar a autenticação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Usar Spring Boot apenas para a complexidade&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Começar simples, evoluir progressivamente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Explorar as forças de cada tecnologia&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Proteger com RLS no nível da base de dados&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_perspectivas_de_evolução&amp;quot;&amp;gt;Perspectivas de Evolução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_adição_de_novas_capacidades&amp;quot;&amp;gt;Adição de Novas Capacidades&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;future-capabilities.svg&amp;quot; alt=&amp;quot;future capabilities&amp;quot; width=&amp;quot;1806&amp;quot; height=&amp;quot;5688&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_escala_horizontal&amp;quot;&amp;gt;escala horizontal&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando seu aplicativo cresce, a arquitetura híbrida escala naturalmente :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Supabase :&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Escala automaticamente até milhões de operações&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Réplicas de leitura para desempenho de leitura&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Recuperação em um ponto no tempo para segurança&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Spring Boot :&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Múltiplas instâncias atrás do balanceador de carga&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Design sem estado facilita o escalonamento horizontal&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Kubernetes para orquestração, se necessário&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;PostgreSQL:&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Particionamento para tabelas voluminosas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Pooling de conexões (PgBouncer)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Sharding realmente necessário (raro)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_testemunhos_arquitetônicos&amp;quot;&amp;gt;Testemunhos Arquitetônicos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_startup_saas_50k_usuários&amp;quot;&amp;gt;Startup SaaS (50k usuários)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ Começamos com 100% Supabase. Com 5k usuários, adicionamos o Spring Boot apenas para a faturação Stripe e os relatórios mensais. Um ano depois, 80% das nossas operações ainda passam pelo Supabase. O Spring Boot gerencia apenas a parte crítica. Esta separação nos permitiu escalar sem refatoração. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_plataforma_e_learning_10k_utilizadores&amp;quot;&amp;gt;Plataforma E-learning (10k utilizadores)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ A autenticação OAuth2 do Supabase nos fez economizar 3 semanas de desenvolvimento. As APIs auto-geradas gerenciam todo o nosso catálogo de cursos. O Spring Boot serve apenas para os certificados PDF e os e-mails de progresso. Arquitetura simples, manutenível e escalável. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_marketplace_b2b_3k_utilizadores&amp;quot;&amp;gt;Marketplace B2B (3k utilizadores)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ Spring Boot gerencia as transações entre compradores e vendedores (crítico). Supabase cuida de tudo o resto: perfis, mensagens em tempo real, documentos. O fato de eles compartilharem a mesma base PostgreSQL nos evita qualquer sincronização complexa. Melhor escolha arquitetural que já fizemos. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_síntese_um_paradigma_arquitetural_moderno&amp;quot;&amp;gt;Síntese: Um paradigma arquitetural moderno&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A arquitetura híbrida Supabase + Spring Boot representa um novo paradigma:&amp;lt;strong&amp;gt;a especialização arquitetônica&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 20) ]

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;Paradigma Antigo&amp;quot; #LightCoral {
  card &amp;quot;Tudo no Backend&amp;quot; {
    [Auth]
    [CRUD]
    [Logique Métier]
    [Jobs]
    [Storage]
    [Realtime]
  }
  note bottom
    Complexité maximale
    dès le jour 1
  end note
}

rectangle &amp;quot;Novo Paradigma&amp;quot; #LightGreen {
  card &amp;quot;Supabase
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;Paradigma Antigo&amp;quot; #LightCoral {
  card &amp;quot;Tudo no Backend&amp;quot; {
    [Auth]
    [CRUD]
    [Logique Métier]
    [Jobs]
    [Storage]
    [Realtime]
  }
  note bottom
    Complexité maximale
    dès le jour 1
  end note
}

rectangle &amp;quot;Novo Paradigma&amp;quot; #LightGreen {
  card &amp;quot;Supabase
(Conveniência)&amp;quot; {
    [Auth]
    [CRUD Simple]
    [Storage]
    [Realtime]
  }

  card &amp;quot;Spring Boot
(Diferenciação)&amp;quot; {
    [Logique Métier]
    [Jobs]
    [Intégrations]
  }

  note bottom
    Complexité progressive
    ajoutée seulement si nécessaire
  end note
}

[Ancien Paradigme] -right-&amp;amp;gt; [Nouveau Paradigme] : Évolution

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O princípio fondamental :&amp;lt;/strong&amp;gt;Não construa o que já existe na forma de serviço. Concentre-se no que diferencia sua aplicação.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_regra_dos_8020&amp;quot;&amp;gt;A Regra dos 80/20&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Na maioria das aplicações :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;80% das operações&amp;lt;/strong&amp;gt;São CRUD padrão → Supabase&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;20 % das operações&amp;lt;/strong&amp;gt;requerem lógica de negócio → Spring Boot&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta regra natural justifica a arquitetura híbrida.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;8020-rule.svg&amp;quot; alt=&amp;quot;8020 rule&amp;quot; width=&amp;quot;1739&amp;quot; height=&amp;quot;5652&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_final&amp;quot;&amp;gt;Conclusão Final&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A arquitetura híbrida não é um compromisso técnico, é uma&amp;lt;strong&amp;gt;decisão estratégica&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ela permite que você :&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�✅ Começar rapidamente com um MVP funcional&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�✅ Validar seu mercado sem investimento pesado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Evoluir gradualmente quando a complexidade o exige&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�✅ Domine seus custos em cada etapa&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�✅ Aproveitar o melhor de cada tecnologia&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Evitar o over-engineering prematuro&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;✅ Manter a flexibilidade para o futuro&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ela representa :&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�🎯&amp;lt;strong&amp;gt;pragmatismo&amp;lt;/strong&amp;gt;: Cada tecnologia onde ela se destaca&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;🚀&amp;lt;strong&amp;gt;Velocidade&amp;lt;/strong&amp;gt;: Tempo de lançamento mínimo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�💰&amp;lt;strong&amp;gt;economia&amp;lt;/strong&amp;gt;: Custos alinhados ao valor&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�🔮&amp;lt;strong&amp;gt;Escalabilidade&amp;lt;/strong&amp;gt;: Arquitetura que cresce com você&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;🛡️&amp;lt;strong&amp;gt;robustez&amp;lt;/strong&amp;gt;: Serviços comprovados e confiáveis&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_seu_próximo_passo&amp;quot;&amp;gt;Seu Próximo Passo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se esta arquitetura fala com você, aqui está por onde começar :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Crie uma conta Supabase&amp;lt;/strong&amp;gt;(gratuito)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Defina seu esquema de dados mínimo&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Configure a autenticação OAuth2&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Crie sua primeira página JBake que consome a API&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Implante no GitHub Pages&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você terá um MVP funcional em&amp;lt;strong&amp;gt;alguns dias&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Spring Boot virá naturalmente quando você precisar. Não antes.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A arquitetura híbrida é a arte de construir exatamente o que é preciso, quando preciso.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Bom desenvolvimento! 🚀&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Compartilhe este artigo se você pensar que pode ajudar outros desenvolvedores a fazer as boas escolhas arquitetônicas.&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Arquitetura Híbrida: O Melhor dos Dois Mundos com Supabase e Spring Boot</title>
            <link >https://cheroliv.com//blog/pilot-batch/0100_jbake_supabase_springboot_hybride_post.html</link>
            <pubDate>Wed, 14 Jan 2026 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/pilot-batch/0100_jbake_supabase_springboot_hybride_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;tempo de leitura : 12 minutes&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Em vez de escolher entre a simplicidade do Supabase e a potência do Spring Boot, por que não combinar os dois? Descubra uma arquitetura híbrida que aproveita o melhor de cada tecnologia.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;toc&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;[]&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_falso_dilema_da_arquitetura_moderna&amp;quot;&amp;gt;O Falso Dilema da Arquitetura Moderna&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao construir uma aplicação moderna com um frontend estático, frequentemente nos deparamos com uma escolha binária:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Apostar tudo numa solução Backend-as-a-Service&amp;lt;/strong&amp;gt;(Supabase, Firebase) - simples mas limitada&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Construir um backend completo&amp;lt;/strong&amp;gt;(Spring Boot, Node.js) - poderoso mas complexo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta escolha é um falso dilema. A arquitetura híbrida propõe um terceiro caminho: &amp;lt;strong&amp;gt;utilizar cada tecnologia onde ela se destaca&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_visão_arquitetônica&amp;quot;&amp;gt;Visão Arquitetônica&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_arquitetura_tradicional_monolítica&amp;quot;&amp;gt;A Arquitetura Tradicional : Monolítica&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;traditional-architecture.svg&amp;quot; alt=&amp;quot;traditional architecture&amp;quot; width=&amp;quot;1746&amp;quot; height=&amp;quot;5652&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nessa abordagem, até mesmo as operações mais simples (leitura de um artigo, criação de um comentário) precisam passar pelo seu backend. Você paga o custo da complexidade desde o primeiro dia.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_arquitetura_baas_todo_delegado&amp;quot;&amp;gt;A Arquitetura BaaS: Todo Delegado&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;baas-architecture.svg&amp;quot; alt=&amp;quot;baas architecture&amp;quot; width=&amp;quot;504&amp;quot; height=&amp;quot;432&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Do contrário, delegar tudo a um BaaS é sedutor no início, mas mostra rapidamente seus limites assim que a lógica de negócios se complexifica.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_arquitetura_híbrida_separação_das_responsabilidades&amp;quot;&amp;gt;A Arquitetura Híbrida : Separação das Responsabilidades&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;hybrid-overview.svg&amp;quot; alt=&amp;quot;hybrid overview&amp;quot; width=&amp;quot;1742&amp;quot; height=&amp;quot;5760&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A arquitetura híbrida separa claramente as responsabilidades de acordo com a complexidade e a natureza das operações.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_princípios_de_design&amp;quot;&amp;gt;Princípios de Design&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_princípio_1_começar_simples_evoluir_inteligentemente&amp;quot;&amp;gt;Princípio 1 : Começar Simples, Evoluir Inteligentemente&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;evolution-phases.svg&amp;quot; alt=&amp;quot;evolution phases&amp;quot; width=&amp;quot;458&amp;quot; height=&amp;quot;392&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Não construa o Spring Boot se você não precisar.&amp;lt;/strong&amp;gt;Comece com o Supabase, adicione o Spring Boot quando a complexidade justificar.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_princípio_2_separação_por_natureza_da_operação&amp;quot;&amp;gt;Princípio 2: Separação por natureza da operação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;operation-types.svg&amp;quot; alt=&amp;quot;operation types&amp;quot; width=&amp;quot;1266&amp;quot; height=&amp;quot;241&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_princípio_3_uma_única_fonte_de_verdade&amp;quot;&amp;gt;Princípio 3 : Uma Única Fonte de Verdade&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;single-source-truth.svg&amp;quot; alt=&amp;quot;single source truth&amp;quot; width=&amp;quot;1769&amp;quot; height=&amp;quot;5700&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao compartilhar a mesma instância PostgreSQL, o Supabase e o Spring Boot trabalham nos mesmos dados sem sincronização complexa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_orquestração_dos_fluxos&amp;quot;&amp;gt;Orquestração dos Fluxos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_flux_1_autenticação_centralizada&amp;quot;&amp;gt;Flux 1 : Autenticação Centralizada&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;auth-orchestration.svg&amp;quot; alt=&amp;quot;auth orchestration&amp;quot; width=&amp;quot;1085&amp;quot; height=&amp;quot;950&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;ponto-chave&amp;lt;/strong&amp;gt;: Um único processo de autenticação, um único JWT, utilizável em qualquer lugar.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_flux_2_orquestração_de_um_processo_de_negócio_complexo&amp;quot;&amp;gt;Flux 2 : Orquestração de um Processo de Negócio Complexo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;business-orchestration.svg&amp;quot; alt=&amp;quot;business orchestration&amp;quot; width=&amp;quot;1151&amp;quot; height=&amp;quot;990&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O que o Spring Boot traz&amp;lt;/strong&amp;gt;Orquestração fiável de processos complexos envolvendo vários sistemas com garantia transacional.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_flux_3_trabalhos_agendados_e_sincronizações&amp;quot;&amp;gt;Flux 3 : Trabalhos Agendados e Sincronizações&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 4) ]

@startuml
skinparam backgroundColor #FEFEFE

participant &amp;quot;Agendador
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
skinparam backgroundColor #FEFEFE

participant &amp;quot;Agendador
(Spring Boot)&amp;quot; as scheduler
participant &amp;quot;Serviço de Relatório&amp;quot; as report
participant &amp;quot;PostgreSQL&amp;quot; as db
participant &amp;quot;API externa&amp;quot; as api
participant &amp;quot;Serviço de e‑mail&amp;quot; as email
participant &amp;quot;Supabase Storage&amp;quot; as storage

== Job Quotidien : 2h00 ==
activate scheduler
scheduler -&amp;amp;gt; report : Déclenche génération rapport

activate report
report -&amp;amp;gt; db : Récupère données\ndes dernières 24h
db -&amp;amp;gt; report : Dataset

report -&amp;amp;gt; report : Calculs &amp;amp;amp; Agrégations
report -&amp;amp;gt; report : Génération PDF

report -&amp;amp;gt; storage : Upload PDF
storage -&amp;amp;gt; report : URL publique

report -&amp;amp;gt; email : Envoie aux admins\navec lien PDF
deactivate report

== Job Hebdomadaire : Dimanche 3h00 ==
scheduler -&amp;amp;gt; report : Synchronisation externe

activate report
report -&amp;amp;gt; api : Fetch données externes
api -&amp;amp;gt; report : Nouvelles données

report -&amp;amp;gt; db : Mise à jour tables
report -&amp;amp;gt; db : Nettoyage données obsolètes
deactivate report

== Job Mensuel : 1er du mois ==
scheduler -&amp;amp;gt; report : Facturation

activate report
report -&amp;amp;gt; db : Liste clients actifs
report -&amp;amp;gt; report : Calcul montants
report -&amp;amp;gt; api : Génération factures (Stripe)
report -&amp;amp;gt; email : Envoi factures
deactivate report

deactivate scheduler

note right of scheduler
  **Impossible avec Supabase seul**
  Edge Functions non adaptées
  pour jobs longs et récurrents
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O que o Spring Boot traz&amp;lt;/strong&amp;gt;: Trabalhos agendados confiáveis com gerenciamento de estado, retry automático e execução garantida.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_matriz_de_decisão&amp;quot;&amp;gt;Matriz de Decisão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_quando_usar_supabase&amp;quot;&amp;gt;Quando usar Supabase?&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;supabase-decision.svg&amp;quot; alt=&amp;quot;supabase decision&amp;quot; width=&amp;quot;1030&amp;quot; height=&amp;quot;445&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_quando_usar_o_spring_boot&amp;quot;&amp;gt;Quando usar o Spring Boot?&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;springboot-decision.svg&amp;quot; alt=&amp;quot;springboot decision&amp;quot; width=&amp;quot;1142&amp;quot; height=&amp;quot;485&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_exemplos_de_arquitetura_por_tipo_de_projeto&amp;quot;&amp;gt;Exemplos de Arquitetura por Tipo de Projeto&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_blog_portfólio&amp;quot;&amp;gt;Blog / Portfólio&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;blog-architecture.svg&amp;quot; alt=&amp;quot;blog architecture&amp;quot; width=&amp;quot;1328&amp;quot; height=&amp;quot;242&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Veredicto&amp;lt;/strong&amp;gt;: 100% Supabase é mais que suficiente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_aplicação_saas&amp;quot;&amp;gt;Aplicação SaaS&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;saas-architecture.svg&amp;quot; alt=&amp;quot;saas architecture&amp;quot; width=&amp;quot;1763&amp;quot; height=&amp;quot;5748&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Veredito&amp;lt;/strong&amp;gt;: Arquitetura híbrida necessária. Supabase para o essencial, Spring Boot para a parte crítica (pagamentos, faturamento).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_e_commerce&amp;quot;&amp;gt;e-commerce&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;ecommerce-architecture.svg&amp;quot; alt=&amp;quot;ecommerce architecture&amp;quot; width=&amp;quot;1751&amp;quot; height=&amp;quot;5748&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;veredicto&amp;lt;/strong&amp;gt;: Spring Boot indispensável. Supabase para autenticação e catálogo apenas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_estratégia_de_migração_progressiva&amp;quot;&amp;gt;Estratégia de Migração Progressiva&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;migration-strategy.svg&amp;quot; alt=&amp;quot;migration strategy&amp;quot; width=&amp;quot;2050&amp;quot; height=&amp;quot;954&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_custos_e_considerações_operacionais&amp;quot;&amp;gt;Custos e Considerações Operacionais&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estrutura_dos_custos&amp;quot;&amp;gt;Estrutura dos Custos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 7) ]

@startuml
skinparam backgroundColor #FEFEFE

package &amp;quot;Custos Mensais Estimados&amp;quot; {

  rectangle &amp;quot;Fase MVP
(0-1000 usuários)&amp;quot; #LightGreen {
^^^^^
 Syntax Error? (Assumed diagram type: activity)

@startuml
skinparam backgroundColor #FEFEFE

package &amp;quot;Custos Mensais Estimados&amp;quot; {

  rectangle &amp;quot;Fase MVP
(0-1000 usuários)&amp;quot; #LightGreen {
    [JBake (GitHub Pages): 0€]
    [Supabase: 0€]
    [Spring Boot: N/A]
    note bottom: **Total: 0€/mois**
  }

  rectangle &amp;quot;Fase de Crescimento\n(1k-10k utilizadores)&amp;quot; #LightYellow {
    [JBake (GitHub Pages): 0€]
    [Supabase: 0€]
    [Spring Boot (Cloud Run): 20-50€]
    [PostgreSQL (si séparé): 0-30€]
    note bottom: **Total: 20-80€/mois**
  }

  rectangle &amp;quot;Escala da Fase
(10k-100k usuários)&amp;quot; #LightCoral {
    [JBake (Netlify Pro): 19€]
    [Supabase Pro: 25€]
    [Spring Boot (instances multiples): 100-300€]
    [Redis Cache: 20-50€]
    [Monitoring: 20€]
    note bottom: **Total: 184-414€/mois**
  }
}

note right of &amp;quot;Escala da Fase
(10k-100k usuários)&amp;quot;
  À ce stade, les revenus
  justifient largement les coûts
end note
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_responsabilidades_operacionais&amp;quot;&amp;gt;Responsabilidades Operacionais&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;operational-responsibilities.svg&amp;quot; alt=&amp;quot;operational responsibilities&amp;quot; width=&amp;quot;1746&amp;quot; height=&amp;quot;5676&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_o_equilíbrio_perfeito&amp;quot;&amp;gt;Conclusão: O Equilíbrio Perfeito&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A arquitetura híbrida Supabase + Spring Boot não é um compromisso, é uma&amp;lt;strong&amp;gt;sinergia&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_os_princípios_a_reter&amp;quot;&amp;gt;Os princípios a reter&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;key-principles.svg&amp;quot; alt=&amp;quot;key principles&amp;quot; width=&amp;quot;581&amp;quot; height=&amp;quot;316&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_quando_escolher_esta_arquitetura&amp;quot;&amp;gt;Quando escolher esta arquitetura?&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;✅ Esta arquitetura é ideal se :&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você quer começar rapidamente (MVP em dias, não em meses)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você antecipa um crescimento da complexidade de negócio&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você deseja minimizar os custos iniciais&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você gosta de PostgreSQL e quer uma fonte de verdade única&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você aprecia Kotlin e as Coroutines para o código de negócio&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você quer evitar o vendor lock-in total&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;❌ Esta arquitetura NÃO é adequada se:&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Seu projeto é simples e permanecerá assim (blog pessoal → 100% Supabase basta)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você já tem uma stack de backend estabelecida que você domina.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você prefere um monolito tradicional&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Você precisa de .NET, Python, ou outra linguagem do lado do backend&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_visão_geral_final&amp;quot;&amp;gt;Visão Geral Final&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 35) ]

@startuml
skinparam backgroundColor #FEFEFE

package &amp;quot;Arquitetura Híbrida Completa&amp;quot; {

  actor &amp;quot;Usuários&amp;quot; as users

  rectangle &amp;quot;Frontend (Estático)&amp;quot; #SkyBlue {
    [JBake / GitHub Pages]
    note right: Gratuit, CDN global
  }

  rectangle &amp;quot;Backend Simples (Supabase)&amp;quot; #LightGreen {
    [Auth OAuth2]
    [CRUD APIs]
    [Storage]
    [Realtime]
    note right
      Gratuit jusqu&amp;#39;à 50k users
      Zéro configuration serveur
    end note
  }

  rectangle &amp;quot;Backend de Negócio (Spring Boot)&amp;quot; #Coral {
    [Orchestration]
    [Jobs Planifiés]
    [Intégrations]
    [Transactions]
    note right
      Activé uniquement
      si nécessaire
    end note
  }

  database &amp;quot;PostgreSQL
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
skinparam backgroundColor #FEFEFE

package &amp;quot;Arquitetura Híbrida Completa&amp;quot; {

  actor &amp;quot;Usuários&amp;quot; as users

  rectangle &amp;quot;Frontend (Estático)&amp;quot; #SkyBlue {
    [JBake / GitHub Pages]
    note right: Gratuit, CDN global
  }

  rectangle &amp;quot;Backend Simples (Supabase)&amp;quot; #LightGreen {
    [Auth OAuth2]
    [CRUD APIs]
    [Storage]
    [Realtime]
    note right
      Gratuit jusqu&amp;#39;à 50k users
      Zéro configuration serveur
    end note
  }

  rectangle &amp;quot;Backend de Negócio (Spring Boot)&amp;quot; #Coral {
    [Orchestration]
    [Jobs Planifiés]
    [Intégrations]
    [Transactions]
    note right
      Activé uniquement
      si nécessaire
    end note
  }

  database &amp;quot;PostgreSQL
Fonte Única da Verdade&amp;quot; as db #LightGray

  users --&amp;amp;gt; [JBake / GitHub Pages]

  [JBake / GitHub Pages] --&amp;amp;gt; [Auth OAuth2]
  [JBake / GitHub Pages] --&amp;amp;gt; [CRUD APIs]
  [JBake / GitHub Pages] --&amp;amp;gt; [Storage]
  [JBake / GitHub Pages] --&amp;amp;gt; [Realtime]
  [JBake / GitHub Pages] --&amp;amp;gt; [Orchestration]

  [Auth OAuth2] --&amp;amp;gt; db
  [CRUD APIs] --&amp;amp;gt; db
  [Orchestration] --&amp;amp;gt; db
  [Jobs Planifiés] --&amp;amp;gt; db
  [Intégrations] --&amp;amp;gt; db
}

note bottom of db
  **Une seule base, deux consommateurs**
  • Supabase pour le simple
  • Spring Boot pour le complexe
  • Aucune synchronisation requise
end note

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_melhor_dos_dois_mundos&amp;quot;&amp;gt;O Melhor dos Dois Mundos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta arquitetura lhe dá:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;🚀 A Velocidade do Supabase&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Início em horas, não em semanas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Autenticação OAuth2 em poucos cliques&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;APIs REST geradas automaticamente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Zero configuração de servidor&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;�💪 O Poder do Spring Boot&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Kotlin e Coroutines para código assíncrono elegante&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Orquestração de processos de negócio complexos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Jobs agendados confiáveis&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Transações ACID garantidas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ecossistema Spring completo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;💰 A Economia Progressiva&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;0€ para iniciar e validar&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Custos que acompanham o crescimento&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Não há sobre-engenharia prematura&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;�🎯 A Flexibilidade Arquitetônica&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Migração progressiva sem reformulação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Adicionar Spring Boot apenas se necessário&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Sem bloqueio total de fornecedor&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;arquitetura evolutiva&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_para_ir_mais_longe&amp;quot;&amp;gt;Para Ir Mais Longe&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_recursos_técnicos&amp;quot;&amp;gt;Recursos Técnicos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Documentação Supabase :&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://supabase.com/docs/guides/auth&amp;quot;&amp;gt;Guia de Autenticação&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://supabase.com/docs/guides/database/postgres/row-level-security&amp;quot;&amp;gt;Segurança em nível de linha&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://supabase.com/docs/guides/api&amp;quot;&amp;gt;APIs gerados automaticamente&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Documentação Spring Boot:&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://spring.io/guides/tutorials/spring-boot-kotlin/&amp;quot;&amp;gt;Spring Boot com Kotlin&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://spring.io/blog/2019/04/12/going-reactive-with-spring-coroutines-and-kotlin-flow&amp;quot;&amp;gt;corrotinas e Spring&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/jwt.html&amp;quot;&amp;gt;Servidor de Recursos JWT&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Artigos Complementares:&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Proteção das APIs com JWT&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Otimização de desempenho PostgreSQL&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Padrões de migração progressiva&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Gestão de erros em arquitetura distribuída&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_casos_de_uso_reais&amp;quot;&amp;gt;Casos de Uso Reais&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta arquitetura híbrida é utilizada com sucesso em :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;SaaS B2B&amp;lt;/strong&amp;gt;: Autenticação Supabase, faturamento Spring Boot&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Mercados&amp;lt;/strong&amp;gt;: Catálogo Supabase, transações Spring Boot&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Plataformas de conteúdo&amp;lt;/strong&amp;gt;: Artigos Supabase, analítica Spring Boot&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ferramentas internas&amp;lt;/strong&amp;gt;: CRUD Supabase, fluxos de trabalho Spring Boot&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_checklist_de_partida&amp;quot;&amp;gt;Checklist de partida&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fase 1 - Fundações (Semana 1)&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist checklist&amp;quot;&amp;gt;
&amp;lt;ul class=&amp;quot;checklist&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;i class=&amp;quot;fa fa-square-o&amp;quot;&amp;gt;&amp;lt;/i&amp;gt; Criar conta Supabase - [ ] Configurar projeto PostgreSQL - [ ] Ativar Auth (Google, GitHub) - [ ] Definir esquema inicial - [ ] Configurar Row Level Security - [ ] Testar APIs desde JBake&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fase 2 - MVP (Semana 2-3)&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist checklist&amp;quot;&amp;gt;
&amp;lt;ul class=&amp;quot;checklist&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;i class=&amp;quot;fa fa-square-o&amp;quot;&amp;gt;&amp;lt;/i&amp;gt; Implementar páginas principais - [ ] Integrar autenticação OAuth2 - [ ] Criar formulários CRUD - [ ] Configurar Storage para arquivos - [ ] Implantar no GitHub Pages - [ ] Testar em condições reais&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fase 3 - Evolução (Conforme as necessidades)&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist checklist&amp;quot;&amp;gt;
&amp;lt;ul class=&amp;quot;checklist&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;i class=&amp;quot;fa fa-square-o&amp;quot;&amp;gt;&amp;lt;/i&amp;gt; Identificar necessidades de lógica complexa - [ ] Criar projeto Spring Boot se necessário - [ ] Configurar validação JWT Supabase - [ ] Conectar ao PostgreSQL Supabase - [ ] Migrar funcionalidades complexas - [ ] Implementar trabalhos agendados - [ ] Implementar Spring Boot (Cloud Run, etc.)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_anti_padrões_a_evitar&amp;quot;&amp;gt;Anti-Padrões a Evitar&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;�❌ Não fazer:&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Duplicar os dados entre Supabase e Spring Boot&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Criar duas bases PostgreSQL separadas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Codifique a autenticação você mesmo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Utilizar Spring Boot para CRUD simples&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Sobre-arquitetar desde o início&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ignorar a Segurança de Nível de Linha do Supabase&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fazer de preferência :&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Compartilhar um único banco de dados PostgreSQL&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Deixe o Supabase gerenciar a autenticação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Usar o Spring Boot apenas pela complexidade&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Começar simples, evoluir gradualmente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Aproveitar as forças de cada tecnologia&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Proteger com RLS no nível da base de dados&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_perspectivas_de_evolução&amp;quot;&amp;gt;Perspectivas de Evolução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_adição_de_novas_capacidades&amp;quot;&amp;gt;Adição de Novas Capacidades&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;future-capabilities.svg&amp;quot; alt=&amp;quot;future capabilities&amp;quot; width=&amp;quot;1773&amp;quot; height=&amp;quot;5688&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_escalonamento_horizontal&amp;quot;&amp;gt;Escalonamento Horizontal&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando seu aplicativo cresce, a arquitetura híbrida escala naturalmente:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Supabase :&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Escala automaticamente até milhões de operações&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Réplicas de leitura para desempenho de leitura&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Point-in-time recovery para segurança&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Spring Boot:&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Múltiplas instâncias atrás do load balancer&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O design stateless facilita o escalonamento horizontal&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Kubernetes para orquestração se necessário&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;PostgreSQL :&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Particionamento para tabelas volumosas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Pooling de conexões (PgBouncer)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Sharding é realmente necessário (raro)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_testemunhos_arquitetônicos&amp;quot;&amp;gt;Testemunhos Arquitetônicos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_startup_saas_50k_utilizadores&amp;quot;&amp;gt;Startup SaaS (50k utilizadores)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ Começamos 100% Supabase. Aos 5k usuários, adicionamos Spring Boot apenas para a cobrança Stripe e os relatórios mensais. Um ano depois, 80% das nossas operações ainda passam pelo Supabase. Spring Boot cuida apenas da parte crítica. Essa separação nos permitiu escalar sem reformulação. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_plataforma_e_learning_10k_usuários&amp;quot;&amp;gt;Plataforma E-learning (10k usuários)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ A autenticação OAuth2 da Supabase nos fez ganhar 3 semanas de desenvolvimento. As APIs auto-geradas gerenciam todo o nosso catálogo de cursos. Spring Boot serve apenas para os certificados PDF e os e-mails de progresso. Arquitetura simples, manutenível, escalável. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_marketplace_b2b_3k_utilizadores&amp;quot;&amp;gt;Marketplace B2B (3k utilizadores)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ Spring Boot gere as transações entre compradores e vendedores (crítico). Supabase gere o resto: perfis, mensagens em tempo real, documentos. O fato de compartilharem o mesmo banco de dados PostgreSQL nos evita qualquer sincronização complexa. Melhor escolha arquitetônica que já fizemos. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_síntese_um_paradigma_arquitetônico_moderno&amp;quot;&amp;gt;Síntese: Um Paradigma Arquitetônico Moderno&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A arquitetura híbrida Supabase + Spring Boot representa um novo paradigma:&amp;lt;strong&amp;gt;especialização arquitetônica&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 20) ]

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;Paradigma Antigo&amp;quot; #LightCoral {
  card &amp;quot;Tudo no Backend&amp;quot; {
    [Auth]
    [CRUD]
    [Logique Métier]
    [Jobs]
    [Storage]
    [Realtime]
  }
  note bottom
    Complexité maximale
    dès le jour 1
  end note
}

rectangle &amp;quot;Novo Paradigma&amp;quot; #LightGreen {
  card &amp;quot;Supabase
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
skinparam backgroundColor #FEFEFE

rectangle &amp;quot;Paradigma Antigo&amp;quot; #LightCoral {
  card &amp;quot;Tudo no Backend&amp;quot; {
    [Auth]
    [CRUD]
    [Logique Métier]
    [Jobs]
    [Storage]
    [Realtime]
  }
  note bottom
    Complexité maximale
    dès le jour 1
  end note
}

rectangle &amp;quot;Novo Paradigma&amp;quot; #LightGreen {
  card &amp;quot;Supabase
(Conveniência)&amp;quot; {
    [Auth]
    [CRUD Simple]
    [Storage]
    [Realtime]
  }

  card &amp;quot;Spring Boot
(Diferenciação)&amp;quot; {
    [Logique Métier]
    [Jobs]
    [Intégrations]
  }

  note bottom
    Complexité progressive
    ajoutée seulement si nécessaire
  end note
}

[Ancien Paradigme] -right-&amp;amp;gt; [Nouveau Paradigme] : Évolution

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O princípio fundamental:&amp;lt;/strong&amp;gt;Não construa o que já existe como serviço. Concentre-se no que diferencia sua aplicação.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_regra_dos_8020&amp;quot;&amp;gt;A Regra dos 80/20&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Na maioria das aplicações :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;80% das operações&amp;lt;/strong&amp;gt;são CRUD padrão → Supabase&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;20% das operações&amp;lt;/strong&amp;gt;necessitam de lógica de negócio → Spring Boot&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta regra natural justifica a arquitetura híbrida.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;8020-rule.svg&amp;quot; alt=&amp;quot;8020 rule&amp;quot; width=&amp;quot;1826&amp;quot; height=&amp;quot;5652&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_final&amp;quot;&amp;gt;Conclusão Final&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A arquitetura híbrida não é um compromisso técnico, é uma&amp;lt;strong&amp;gt;decisão estratégica&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ela permite que você:&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�✅ Comece rapidamente com um MVP funcional&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�✅ Validar seu mercado sem investimento pesado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;✅ Evoluir gradualmente quando a complexidade o exige&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Domine seus custos em cada etapa&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;✅ Explorar o melhor de cada tecnologia&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�✅ Evitar a sobreengenharia prematura&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;✅ Manter a flexibilidade para o futuro&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ela representa :&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�🎯&amp;lt;strong&amp;gt;Pragmatismo&amp;lt;/strong&amp;gt;: Cada tecnologia onde ela se destaca&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�🚀&amp;lt;strong&amp;gt;velocidade&amp;lt;/strong&amp;gt;: Tempo de lançamento mínimo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�💰&amp;lt;strong&amp;gt;Economia&amp;lt;/strong&amp;gt;Custos alinhados ao valor&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�🔮&amp;lt;strong&amp;gt;Escalabilidade&amp;lt;/strong&amp;gt;: Arquitetura que cresce com você&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�🛡️&amp;lt;strong&amp;gt;Robustez&amp;lt;/strong&amp;gt;: Serviços testados e confiáveis&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_seu_próximo_passo&amp;quot;&amp;gt;Seu Próximo Passo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se essa arquitetura fala com você, aqui está por onde começar:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Crie uma conta Supabase&amp;lt;/strong&amp;gt;(gratuito)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Defina seu esquema de dados mínimo&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Configure a autenticação OAuth2&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Crie sua primeira página JBake que consome a API&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Implante no GitHub Pages&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você terá um MVP funcional em&amp;lt;strong&amp;gt;alguns dias&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Spring Boot virá naturalmente quando você precisar. Não antes.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A arquitetura híbrida é a arte de construir exatamente o que é necessário, quando é necessário.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Bom desenvolvimento! 🚀&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Compartilhe este artigo se você pensar que pode ajudar outros desenvolvedores a fazer as boas escolhas arquitetônicas.&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>A Arte do Pré-carregamento: Eliminando o &quot;Flash&quot; do Tema com JavaScript e CSS</title>
            <link >https://cheroliv.com//blog/2025/0098_preloading_css_variable_post.html</link>
            <pubDate>Sat, 15 Nov 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0098_preloading_css_variable_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_resumo&amp;quot;&amp;gt;resumo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_essa_maldita_piscagem_do_tema&amp;quot;&amp;gt;O Problema: Essa Maldita Piscagem do Tema&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_um_pesadelo_para_a_experiência_do_usuário&amp;quot;&amp;gt;Um pesadelo para a experiência do usuário&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_fouc_um_problema_antigo_da_web&amp;quot;&amp;gt;O FOUC: Um problema antigo da web&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_impacto_na_percepção_de_qualidade&amp;quot;&amp;gt;Impacto na Percepção de Qualidade&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_análise_técnica_do_problema&amp;quot;&amp;gt;Análise Técnica do Problema&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_um_script_clássico_não_é_suficiente&amp;quot;&amp;gt;Por que um script clássico não é suficiente?&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_abordagem_intuitiva_mas_ineficaz&amp;quot;&amp;gt;A Abordagem Intuitiva mas Ineficaz&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_entender_o_evento_domcontentloaded&amp;quot;&amp;gt;Entender o evento DOMContentLoaded&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_do_render_blocking&amp;quot;&amp;gt;O Problema do Render Blocking&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_visualização_do_problema&amp;quot;&amp;gt;Visualização do Problema&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_as_tentativas_de_solução_ineficazes&amp;quot;&amp;gt;As tentativas de solução ineficazes&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_verdadeira_solução_agir_mais_cedo&amp;quot;&amp;gt;A Verdadeira Solução: Agir Mais Cedo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_o_carregamento_précoce_early_loading&amp;quot;&amp;gt;A Solução: O Carregamento Précoce (Early Loading)&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_princípio_fundamental&amp;quot;&amp;gt;O Princípio Fundamental&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_o_head_é_o_local_ideal&amp;quot;&amp;gt;Por que o &amp;lt;code&amp;gt;&amp;amp;lt;head&amp;amp;gt;&amp;lt;/code&amp;gt; é o Local Ideal&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_da_solução_em_três_camadas&amp;quot;&amp;gt;Arquitetura da Solução em Três Camadas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_1_salvar_a_escolha_do_usuário&amp;quot;&amp;gt;Etapa 1 : Salvar a Escolha do Usuário&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_localstorage_sua_memória_persistente&amp;quot;&amp;gt;O localStorage: Sua Memória Persistente&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_implementação_da_cópia_de_segurança_do_tema&amp;quot;&amp;gt;Implementação da Cópia de Segurança do Tema&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_gerenciamento_dos_casos_de_erro&amp;quot;&amp;gt;Gerenciamento dos Casos de Erro&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estratégias_avançadas_de_persistência&amp;quot;&amp;gt;Estratégias Avançadas de Persistência&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_2_o_script_de_pré_carregamento_no_head&amp;quot;&amp;gt;Etapa 2: O Script de Pré-carregamento no `&amp;amp;lt;head&amp;amp;gt;&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_coração_da_solução&amp;quot;&amp;gt;O Coração da Solução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_anatomia_do_script_cada_linha_conta&amp;quot;&amp;gt;Anatomia do Script: Cada Linha Conta&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_detecção_da_preferência_do_sistema_bônus&amp;quot;&amp;gt;Detecção da Preferência do Sistema (Bônus)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_desempenho_por_que_este_script_é_rápido&amp;quot;&amp;gt;Desempenho: Por que este script é rápido&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_colocação_óptima_no_head&amp;quot;&amp;gt;Colocação Óptima no `&amp;amp;lt;head&amp;amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_3_o_poder_dos_seletores_de_atributos_css&amp;quot;&amp;gt;Passo 3 : O Poder dos Seletores de Atributos CSS&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_sistema_de_tema_do_bootstrap_5&amp;quot;&amp;gt;O sistema de tema do Bootstrap 5&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estrutura_css_para_um_sistema_de_tema&amp;quot;&amp;gt;Estrutura CSS para um Sistema de Tema&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_resumo&amp;quot;&amp;gt;resumo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Neste artigo técnico aprofundado, exploramos como implementar um seletor de tema (light/dark) sem o incômodo efeito de piscar que ocorre durante o carregamento da página. Analisamos em detalhe o ciclo de renderização do navegador, as causas profundas do FOUC (Flash of Unstyled Content) e propomos uma solução robusta baseada na pré-carregamento síncrono no`&amp;amp;lt;head&amp;amp;gt;`. Esta técnica garante uma experiência de usuário fluida e profissional.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_problema_essa_maldita_piscagem_do_tema&amp;quot;&amp;gt;O Problema: Essa Maldita Piscagem do Tema&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_um_pesadelo_para_a_experiência_do_usuário&amp;quot;&amp;gt;Um pesadelo para a experiência do usuário&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Imagine a cena: você passou horas projetando um belo tema escuro para o seu aplicativo web. As cores estão perfeitamente equilibradas, o contraste é ótimo e seus usuários adoram essa opção. Mas há um problema constrangedor: a cada recarregamento da página, durante uma fração de segundo, o tema claro padrão aparece antes que o tema escuro assuma o controle.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esse &amp;#39;flash&amp;#39; visual, embora breve (às vezes menos de 100ms), é imediatamente perceptível pelo olho humano e cria uma experiência desagradável. Para os usuários que escolheram o tema escuro por razões de conforto visual ou de acessibilidade, esse piscar pode até ser doloroso, especialmente em um ambiente pouco iluminado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_fouc_um_problema_antigo_da_web&amp;quot;&amp;gt;O FOUC: Um problema antigo da web&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esse fenômeno é uma variante do que se chama &amp;quot;Flash of Unstyled Content&amp;quot; (FOUC), um problema clássico do desenvolvimento web que remonta aos primeiros dias do CSS. O FOUC ocorre quando o navegador exibe temporariamente conteúdo HTML sem os estilos CSS aplicados, criando um flash de conteúdo não estilizado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No nosso caso específico, não estamos a falar de um conteúdo completamente não estilizado, mas sim de um&amp;lt;strong&amp;gt;Flash do Tema Errado&amp;lt;/strong&amp;gt;(FOWT) - o conteúdo está estilizado, mas com o tema errado. Isso é particularmente frustrante porque isso mostra que nosso aplicativo &amp;quot;esquece&amp;quot; a preferência do usuário a cada carregamento de página.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_impacto_na_percepção_de_qualidade&amp;quot;&amp;gt;Impacto na Percepção de Qualidade&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este problema, embora técnico, tem repercussões importantes na percepção da qualidade do seu aplicativo:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Falta de polish&amp;lt;/strong&amp;gt;: O piscar dá a impressão de uma aplicação inacabada ou mal otimizada. Os utilizadores associam frequentemente estes pequenos defeitos visuais a uma falta geral de profissionalismo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Ruptura de coerência&amp;lt;/strong&amp;gt;: O aplicativo parece &amp;#39;esquecer&amp;#39; as preferências do usuário, criando uma sensação de desincronização entre a interface e as expectativas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fadiga visual&amp;lt;/strong&amp;gt;Para os usuários sensíveis à luz ou que sofrem de enxaqueca, esse flash luminoso pode ser mais do que um simples incômodo estético.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Desempenho percebido&amp;lt;/strong&amp;gt;Ironicamente, mesmo que seu site carrega rapidamente, essa piscada pode dar a impressão de uma aplicação lenta ou pouco responsiva.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_análise_técnica_do_problema&amp;quot;&amp;gt;Análise Técnica do Problema&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para entender como resolver esse problema, é preciso primeiro entender por que ele ocorre. O piscar ocorre devido a um atraso temporal entre três eventos críticos no ciclo de vida de uma página web:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O parsing inicial do HTML&amp;lt;/strong&amp;gt;: O navegador lê e analisa a estrutura da sua página&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A aplicação dos estilos CSS&amp;lt;/strong&amp;gt;: O navegador aplica as regras CSS e calcula a renderização visual&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A execução do JavaScript&amp;lt;/strong&amp;gt;: Seu código que altera o tema está sendo executado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O problema ocorre quando o evento nº3 (execução do JavaScript) chega depois que o navegador já começou ou terminou o evento nº2 (aplicação dos estilos). Nesse momento, o navegador já tomou uma decisão sobre qual tema exibir, e seu código chega tarde demais para influenciá-lo antes da primeira renderização.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_um_script_clássico_não_é_suficiente&amp;quot;&amp;gt;Por que um script clássico não é suficiente?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_abordagem_intuitiva_mas_ineficaz&amp;quot;&amp;gt;A Abordagem Intuitiva mas Ineficaz&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A abordagem mais natural para um desenvolvedor seria colocar um script ao final do nosso`&amp;amp;lt;body&amp;amp;gt;`que verifica o tema preferido do usuário e o aplica. Essa abordagem segue as melhores práticas tradicionais da web que recomendam carregar os scripts no final da página para não bloquear a renderização.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;// À la fin de &amp;amp;lt;body&amp;amp;gt; - L&amp;#39;APPROCHE INSUFFISANTE
document.addEventListener(&amp;#39;DOMContentLoaded&amp;#39;, () =&amp;amp;gt; {
  const theme = localStorage.getItem(&amp;#39;preferred-theme&amp;#39;);
  if (theme === &amp;#39;dark&amp;#39;) {
    document.documentElement.setAttribute(&amp;#39;data-bs-theme&amp;#39;, &amp;#39;dark&amp;#39;);
  }
});&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta abordagem parece lógica à primeira vista. Esperamos que o DOM esteja pronto, depois aplicamos o tema. Simples, não? Infelizmente, essa simplicidade esconde um defeito fundamental relacionado ao timing do ciclo de renderização do navegador.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_entender_o_evento_domcontentloaded&amp;quot;&amp;gt;Entender o evento DOMContentLoaded&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O evento`DOMContentLoaded`é acionado quando o documento HTML inicial foi completamente carregado e analisado pelo navegador,&amp;lt;strong&amp;gt;sem esperar&amp;lt;/strong&amp;gt;o fim do carregamento das folhas de estilo, das imagens e dos sub-quadros. É um ponto importante a entender.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está a sequência típica dos eventos :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O navegador começa a baixar o HTML&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ele analisa o HTML conforme ele é recebido&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ele descobre as tags`&amp;amp;lt;link&amp;amp;gt;`para as CSS e começa a baixá-las&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ele descobre as tags`&amp;amp;lt;script&amp;amp;gt;`e os executa (conforme seu tipo e atributos)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ele constrói o DOM (Document Object Model)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O evento &amp;lt;code&amp;gt;DOMContentLoaded&amp;lt;/code&amp;gt; é disparado&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ele continua a aplicar os estilos e a fazer o layout&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O primeiro paint (exibição) ocorre&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O evento`load`se dispara quando todos os recursos são carregados&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O problema? Entre a etapa 6 (DOMContentLoaded) e a etapa 8 (primeiro paint), o navegador já tomou decisões sobre como exibir a página. Se o seu script de alteração de tema for executado na etapa 6, já é tarde demais para evitar uma primeira exibição com os estilos padrão.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_problema_do_render_blocking&amp;quot;&amp;gt;O Problema do Render Blocking&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Na verdade, o timing é ainda mais complexo. Os navegadores modernos utilizam técnicas de otimização sofisticadas para melhorar a performance percebida. Eles tentam fazer o primeiro paint (First Contentful Paint) o mais rápido possível para que o usuário veja algo na tela.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os CSS são &amp;quot;render-blocking&amp;quot; por padrão, o que significa que o navegador espera ter baixado e analisado as folhas de estilo antes de fazer o primeiro paint. É lógico: não queremos exibir conteúdo não estilizado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mas aqui está a armadilha: quando o navegador aplica esses estilos CSS pela primeira vez, ele o faz com base no estado atual do DOM. Se o atributo`data-bs-theme`ainda não está definido na tag`&amp;amp;lt;html&amp;amp;gt;`, o navegador aplicará os estilos padrão (geralmente o tema claro).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Então, quando seu script é executado e altera esse atributo, o navegador deve :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Recalcular todos os estilos afetados por essa mudança&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Refazer o layout, se necessário&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Repintar os elementos afetados&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este processo de recálculo e de repintura é o que causa o piscar visível.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_visualização_do_problema&amp;quot;&amp;gt;Visualização do Problema&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para melhor compreender esta sequência problemática, vamos examinar um diagrama de sequência detalhado:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;diag-flux-incorrect&amp;quot; class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 27) ]

@startuml
actor Utilisateur
participant Navigateur
participant &amp;quot;Analisador HTML&amp;quot; as Parser
participant &amp;quot;Modelo de Objeto CSS&amp;quot; as CSS
participant &amp;quot;Script.js (fim do body)&amp;quot; as Script
participant &amp;quot;Motor de renderização&amp;quot; as Render

Utilisateur -&amp;amp;gt; Navigateur: Demande la page
activate Navigateur

Navigateur -&amp;amp;gt; Parser: Parse le HTML
activate Parser
Parser -&amp;amp;gt; CSS: Découvre et charge les CSS
activate CSS
Parser -&amp;amp;gt; Parser: Construit le DOM
Parser -&amp;amp;gt; Navigateur: DOM Ready (DOMContentLoaded)
deactivate Parser

CSS -&amp;amp;gt; CSS: Parse les feuilles de style
CSS -&amp;amp;gt; Render: CSSOM Ready
deactivate CSS

activate Render
Render -&amp;amp;gt; Render: Calcule les styles avec data-bs-theme=&amp;quot;&amp;quot;nota direita: Tema padrão (claro)
Render -&amp;amp;gt; Navegador: Primeira pintura (Tema Claro)
nota direita: O usuário vê o tema claro
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
actor Utilisateur
participant Navigateur
participant &amp;quot;Analisador HTML&amp;quot; as Parser
participant &amp;quot;Modelo de Objeto CSS&amp;quot; as CSS
participant &amp;quot;Script.js (fim do body)&amp;quot; as Script
participant &amp;quot;Motor de renderização&amp;quot; as Render

Utilisateur -&amp;amp;gt; Navigateur: Demande la page
activate Navigateur

Navigateur -&amp;amp;gt; Parser: Parse le HTML
activate Parser
Parser -&amp;amp;gt; CSS: Découvre et charge les CSS
activate CSS
Parser -&amp;amp;gt; Parser: Construit le DOM
Parser -&amp;amp;gt; Navigateur: DOM Ready (DOMContentLoaded)
deactivate Parser

CSS -&amp;amp;gt; CSS: Parse les feuilles de style
CSS -&amp;amp;gt; Render: CSSOM Ready
deactivate CSS

activate Render
Render -&amp;amp;gt; Render: Calcule les styles avec data-bs-theme=&amp;quot;&amp;quot;nota direita: Tema padrão (claro)
Render -&amp;amp;gt; Navegador: Primeira pintura (Tema Claro)
nota direita: O usuário vê o tema claro
desativar Render

Navegador -&amp;amp;gt; Script: Executa o script
ativar Script
Script -&amp;amp;gt; Script: Lê localStorage
Script -&amp;amp;gt; Navegador: setAttribute(&amp;#39;data-bs-theme&amp;#39;, &amp;#39;dark&amp;#39;)
desativar Script

ativar Render
Render -&amp;amp;gt; Render: Recalcular os estilos com data-bs-theme=&amp;quot;dark&amp;quot;
note right: Thème sombre
Render -&amp;amp;gt; Navigateur: Repaint (Thème Sombre)
note right: Clignotement visible
deactivate Render

deactivate Navigateur
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este diagrama ilustra claramente o problema : a primeira pintura ocorre antes que nosso script tenha tido a oportunidade de definir o tema correto. A repintura subsequente cria a intermitência visível.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_as_tentativas_de_solução_ineficazes&amp;quot;&amp;gt;As tentativas de solução ineficazes&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Várias abordagens foram tentadas para resolver esse problema, mas a maioria tem seus próprios inconvenientes:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Abordagem 1 : Ocultar o conteúdo até o carregamento&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;body {
  opacity: 0;
  transition: opacity 0.3s;
}

body.loaded {
  opacity: 1;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta abordagem esconde todo o conteúdo até que o JavaScript defina o tema correto. O problema? Isso atrasa artificialmente a exibição do conteúdo, dando a impressão de um site mais lento. Além disso, se o JavaScript estiver desativado, o usuário não vê nada de todo!&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Abordagem 2: Usar um loader/spinner&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Semelhante à abordagem 1, mas com um spinner de carregamento. Isso mascara o problema, mas não melhora o desempenho real e adiciona um atraso percebido desnecessário.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Abordagem 3: Padrão para tema escuro&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Alguns desenvolvedores definem o tema escuro como padrão no CSS. Isso evita o piscar para os usuários do tema escuro, mas cria o problema inverso para os usuários do tema claro!&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nenhuma dessas abordagens é satisfatória porque elas tratam o sintoma em vez da causa raiz do problema.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_verdadeira_solução_agir_mais_cedo&amp;quot;&amp;gt;A Verdadeira Solução: Agir Mais Cedo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A chave para resolver este problema é perceber que devemos definir o atributo`data-bs-theme` &amp;lt;strong&amp;gt;antes&amp;lt;/strong&amp;gt;que o navegador não começa a aplicar os estilos CSS. Isso significa que nosso script deve ser executado mais cedo no ciclo de vida da página, e isso é exatamente o que vamos explorar na seção seguinte.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_o_carregamento_précoce_early_loading&amp;quot;&amp;gt;A Solução: O Carregamento Précoce (Early Loading)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_princípio_fundamental&amp;quot;&amp;gt;O Princípio Fundamental&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A solução elegante para o nosso problema de piscar baseia-se num princípio simples mas poderoso :&amp;lt;strong&amp;gt;sincronizar o estado da aplicação com o processo de renderização do navegador&amp;lt;/strong&amp;gt;. Em vez de esperar que a página seja carregada para definir o tema, devemos defini-lo&amp;lt;strong&amp;gt;durante&amp;lt;/strong&amp;gt;o carregamento, mesmo antes de os estilos CSS serem aplicados.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta abordagem chama-se &amp;#39;Early Loading&amp;#39; ou &amp;#39;Synchronous Preloading&amp;#39; no jargão do desenvolvimento web. A ideia é executar nossa lógica de detecção de tema o mais cedo possível no ciclo de vida da página, idealmente na tag`&amp;amp;lt;head&amp;amp;gt;`, mesmo antes de o navegador começar a baixar os arquivos CSS.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_por_que_o_head_é_o_local_ideal&amp;quot;&amp;gt;Por que o &amp;lt;code&amp;gt;&amp;amp;lt;head&amp;amp;gt;&amp;lt;/code&amp;gt; é o Local Ideal&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le `&amp;amp;lt;head&amp;amp;gt;`Um documento HTML é processado sequencialmente pelo navegador, de cima para baixo. Cada elemento é processado na ordem em que aparece. Essa característica é crucial para nossa solução.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando o navegador encontra uma tag`&amp;amp;lt;script&amp;amp;gt;`no`&amp;amp;lt;head&amp;amp;gt;`sem os atributos`async` ou &amp;lt;code&amp;gt;defer&amp;lt;/code&amp;gt;, il :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Interrompe o parsing do HTML&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Baixe o script&amp;lt;/strong&amp;gt;(se externo) ou a cama (se inline)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Executa imediatamente o script&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;retoma o parsing do HTML&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este comportamento, frequentemente considerado um problema de desempenho (daí a recomendação habitual de colocar os scripts no final da página), torna-se nosso aliado neste caso específico. Ao posicionar nosso script de detecção de tema no início do`&amp;amp;lt;head&amp;amp;gt;`, nós garantimos que ele execute antes que o navegador encontre as etiquetas`&amp;amp;lt;link&amp;amp;gt;`das nossas folhas de estilo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_arquitetura_da_solução_em_três_camadas&amp;quot;&amp;gt;Arquitetura da Solução em Três Camadas&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nossa solução completa é composta por três camadas interdependentes, cada uma desempenhando um papel específico:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Camada 1 : Persistência (localStorage)&amp;lt;/strong&amp;gt; Esta camada é responsável pela cópia de segurança e recuperação da escolha do utilizador entre as sessões.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Camada 2 : Sincronização Precoce (Script embutido em &amp;lt;code&amp;gt;&amp;amp;lt;head&amp;amp;gt;&amp;lt;/code&amp;gt;)&amp;lt;/strong&amp;gt; Esta camada sincroniza o estado da aplicação com o DOM antes da renderização inicial.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Camada 3 : Estilos Responsivos (CSS com seletores de atributos)&amp;lt;/strong&amp;gt; Esta camada define os estilos visuais baseados no estado definido pela camada 2.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exploramos agora cada camada em detalhe.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_etapa_1_salvar_a_escolha_do_usuário&amp;quot;&amp;gt;Etapa 1 : Salvar a Escolha do Usuário&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_localstorage_sua_memória_persistente&amp;quot;&amp;gt;O localStorage: Sua Memória Persistente&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Le &amp;lt;code&amp;gt;localStorage`é uma API Web Storage que permite armazenar pares chave-valor no navegador de forma persistente. Diferente dos cookies, os dados do`localStorage&amp;lt;/code&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Nunca são enviadas automaticamente para o servidor&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Têm uma capacidade de armazenamento maior (geralmente 5-10 MB)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Não têm data de expiração (persistente até exclusão explícita)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;São limitadas ao protocolo e ao domínio (Same-Origin Policy)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para o nosso caso de uso, o`localStorage`Está perfeito porque :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Não precisamos compartilhar esta informação com o servidor&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Queremos que a preferência persista indefinidamente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O tamanho de armazenamento necessário é mínimo (alguns bytes)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_implementação_da_cópia_de_segurança_do_tema&amp;quot;&amp;gt;Implementação da Cópia de Segurança do Tema&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Como salvamos a escolha do usuário quando ele muda de tema:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;// Fonction complète pour changer le thème
function setTheme(newTheme) {
  // Validation de l&amp;#39;entrée
  if (![&amp;#39;light&amp;#39;, &amp;#39;dark&amp;#39;, &amp;#39;auto&amp;#39;].includes(newTheme)) {
    console.error(&amp;#39;Thème invalide:&amp;#39;, newTheme);
    return;
  }

  try {
    // Sauvegarde dans localStorage
    localStorage.setItem(&amp;#39;preferred-theme&amp;#39;, newTheme);

    // Application immédiate dans le DOM
    document.documentElement.setAttribute(&amp;#39;data-bs-theme&amp;#39;, newTheme);

    // Dispatch d&amp;#39;un événement personnalisé pour notifier d&amp;#39;autres composants
    window.dispatchEvent(new CustomEvent(&amp;#39;theme-changed&amp;#39;, {
      detail: { theme: newTheme }
    }));

    console.log(&amp;#39;Thème changé:&amp;#39;, newTheme);
  } catch (error) {
    console.error(&amp;#39;Erreur lors de la sauvegarde du thème:&amp;#39;, error);
    // Fallback : on applique quand même le thème visuellement
    document.documentElement.setAttribute(&amp;#39;data-bs-theme&amp;#39;, newTheme);
  }
}

// Exemple d&amp;#39;utilisation avec un bouton
document.getElementById(&amp;#39;theme-toggle&amp;#39;).addEventListener(&amp;#39;click&amp;#39;, () =&amp;amp;gt; {
  const currentTheme = document.documentElement.getAttribute(&amp;#39;data-bs-theme&amp;#39;) || &amp;#39;light&amp;#39;;
  const newTheme = currentTheme === &amp;#39;light&amp;#39; ? &amp;#39;dark&amp;#39; : &amp;#39;light&amp;#39;;
  setTheme(newTheme);
});&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_gerenciamento_dos_casos_de_erro&amp;quot;&amp;gt;Gerenciamento dos Casos de Erro&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É crucial gerir os casos em que o`localStorage`não está disponível ou acessível. Vários cenários podem impedir o acesso ao`localStorage`: (Note: The actual output should be exactly a space, colon, and a space, with no additional characters or commentary.)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Navegação privada estrita&amp;lt;/strong&amp;gt;Safari em modo de navegação privada lança uma exceção`QuotaExceededError`durante tentativas de escrita no`localStorage`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Configurações de privacidade&amp;lt;/strong&amp;gt;Alguns navegadores ou extensões de privacidade podem bloquear o acesso ao`localStorage`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Limitações de domínio&amp;lt;/strong&amp;gt; : Le `localStorage`não é acessível no protocolo`file://`em alguns navegadores.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Espaço de armazenamento saturado&amp;lt;/strong&amp;gt;Embora raro, o espaço de armazenamento pode estar completamente preenchido.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É por isso que nosso código usa um bloco`try&amp;amp;#8230;&amp;amp;#8203;catch`para lidar com esses casos com elegância, continuando a oferecer a funcionalidade de alteração de tema mesmo que a persistência não esteja disponível&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estratégias_avançadas_de_persistência&amp;quot;&amp;gt;Estratégias Avançadas de Persistência&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para aplicações mais sofisticadas, você pode considerar estratégias adicionais:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Sincronização do servidor (opcional)&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;async function setTheme(newTheme) {
  // Sauvegarde locale immédiate
  localStorage.setItem(&amp;#39;preferred-theme&amp;#39;, newTheme);
  document.documentElement.setAttribute(&amp;#39;data-bs-theme&amp;#39;, newTheme);

  // Synchronisation serveur en arrière-plan (si l&amp;#39;utilisateur est connecté)
  if (userIsAuthenticated()) {
    try {
      await fetch(&amp;#39;/api/user/preferences&amp;#39;, {
        method: &amp;#39;POST&amp;#39;,
        headers: { &amp;#39;Content-Type&amp;#39;: &amp;#39;application/json&amp;#39; },
        body: JSON.stringify({ theme: newTheme })
      });
    } catch (error) {
      console.warn(&amp;#39;Échec de la synchronisation serveur:&amp;#39;, error);
      // L&amp;#39;échec n&amp;#39;est pas critique car la préférence est déjà sauvegardée localement
    }
  }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta abordagem permite sincronizar as preferências entre dispositivos para os utilizadores ligados, mantendo a capacidade de resposta local imediata.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_etapa_2_o_script_de_pré_carregamento_no_head&amp;quot;&amp;gt;Etapa 2: O Script de Pré-carregamento no `&amp;amp;lt;head&amp;amp;gt;&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_coração_da_solução&amp;quot;&amp;gt;O Coração da Solução&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É aqui que a magia opera realmente. Vamos colocar um pequeno script&amp;lt;strong&amp;gt;em linha&amp;lt;/strong&amp;gt;diretamente no nosso`&amp;amp;lt;head&amp;amp;gt;`, antes de todas as nossas tags`&amp;amp;lt;link&amp;amp;gt;`de folhas de estilo. Este script é voluntariamente minimalista, autônomo, e concebido para ser executado o mais rápido possível.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html lang=&amp;quot;fr&amp;quot;&amp;amp;gt;
&amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;amp;gt;
    &amp;amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&amp;amp;gt;
    &amp;amp;lt;title&amp;amp;gt;Mon Site Incroyable&amp;amp;lt;/title&amp;amp;gt;

    &amp;amp;lt;!-- ==========================================
         NOTRE SCRIPT MAGIQUE DE PRÉ-CHARGEMENT
         Ce script DOIT être le premier élément
         dans le &amp;amp;lt;head&amp;amp;gt; après les meta tags
         ========================================== --&amp;amp;gt;
    &amp;amp;lt;script&amp;amp;gt;
      // IIFE pour ne pas polluer le scope global
      (function() {
        &amp;#39;use strict&amp;#39;;

        try {
          // Lecture de la préférence sauvegardée
          const savedTheme = localStorage.getItem(&amp;#39;preferred-theme&amp;#39;);

          // Si une préférence existe, on l&amp;#39;applique immédiatement
          if (savedTheme) {
            document.documentElement.setAttribute(&amp;#39;data-bs-theme&amp;#39;, savedTheme);
          }
          // Optionnel : Détecter la préférence système si aucune sauvegarde
          else if (window.matchMedia &amp;amp;amp;&amp;amp;amp; window.matchMedia(&amp;#39;(prefers-color-scheme: dark)&amp;#39;).matches) {
            document.documentElement.setAttribute(&amp;#39;data-bs-theme&amp;#39;, &amp;#39;dark&amp;#39;);
          }
          // Sinon, le thème par défaut du CSS sera utilisé (généralement &amp;#39;light&amp;#39;)

        } catch (error) {
          // En cas d&amp;#39;erreur (localStorage bloqué, etc.), on log discrètement
          // et on laisse le thème par défaut s&amp;#39;appliquer
          console.warn(&amp;#39;Impossible de charger la préférence de thème:&amp;#39;, error);
        }
      })();
    &amp;amp;lt;/script&amp;amp;gt;
    &amp;amp;lt;!-- FIN DU SCRIPT MAGIQUE --&amp;amp;gt;

    &amp;amp;lt;!-- Les feuilles de style sont chargées APRÈS le script --&amp;amp;gt;
    &amp;amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;css/bootstrap.min.css&amp;quot;&amp;amp;gt;
    &amp;amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;css/styles.css&amp;quot;&amp;amp;gt;

    &amp;amp;lt;!-- Autres ressources du head --&amp;amp;gt;
    &amp;amp;lt;link rel=&amp;quot;icon&amp;quot; href=&amp;quot;favicon.ico&amp;quot;&amp;amp;gt;
&amp;amp;lt;/head&amp;amp;gt;
&amp;amp;lt;body&amp;amp;gt;
    &amp;amp;lt;!-- Contenu de la page --&amp;amp;gt;
&amp;amp;lt;/body&amp;amp;gt;
&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_anatomia_do_script_cada_linha_conta&amp;quot;&amp;gt;Anatomia do Script: Cada Linha Conta&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Vamos analisar este script linha por linha para entender cada decisão de design:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A IIFE (expressão de função imediatamente invocada)&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;(function() {
  // ...
})();&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta estrutura cria uma função que é executada imediatamente. Por quê? Para isolar nossas variáveis em um escopo local e evitar poluir o escopo global. Mesmo que usemos apenas`const`(que tem um escopo de bloco), a IIFE é uma boa prática que deixa nossas intenções claras e protege contra possíveis conflitos de nomes.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O Modo Estrito&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;&amp;#39;use strict&amp;#39;;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta diretiva ativa o modo estrito do JavaScript, que : - Proíbe o uso de variáveis não declaradas - Gera erros para as operações perigosas - Melhora o desempenho em alguns motores JavaScript&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para um script crítico como este, queremos a máxima segurança.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O bloco try&amp;amp;#8230;&amp;amp;#8203;catch&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;try {
  // Code principal
} catch (error) {
  console.warn(&amp;#39;Impossible de charger la préférence de thème:&amp;#39;, error);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este bloco é absolutamente crucial. Garante que se algo correr mal (localStorage bloqueado, erro de sintaxe improvável, etc.), o nosso script não bloqueará o carregamento de toda a página. A utilização de`console.warn`em vez de`console.error`indica que é um problema não crítico&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A Leitura do localStorage&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;const savedTheme = localStorage.getItem(&amp;#39;preferred-theme&amp;#39;);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta linha pode lançar uma exceção em alguns contextos (navegação privada rígida do Safari). Por isso, ela está em um bloco try&amp;amp;#8230;&amp;amp;#8203;catch.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A Aplicação Condicional&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;if (savedTheme) {
  document.documentElement.setAttribute(&amp;#39;data-bs-theme&amp;#39;, savedTheme);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aplicamos o tema apenas se encontrarmos um salvo. Caso contrário, deixamos o CSS usar seu tema padrão. Essa abordagem é mais robusta do que um valor padrão fixo no JavaScript.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_detecção_da_preferência_do_sistema_bônus&amp;quot;&amp;gt;Detecção da Preferência do Sistema (Bônus)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Uma melhoria opcional, mas elegante, consiste em detectar a preferência de tema do sistema operacional do usuário se ele ainda não fez uma escolha explícita em seu aplicativo:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;else if (window.matchMedia &amp;amp;amp;&amp;amp;amp; window.matchMedia(&amp;#39;(prefers-color-scheme: dark)&amp;#39;).matches) {
  document.documentElement.setAttribute(&amp;#39;data-bs-theme&amp;#39;, &amp;#39;dark&amp;#39;);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta funcionalidade utiliza a Media Query`prefers-color-scheme`para consultar o sistema. No macOS, Windows 10+, iOS, e Android moderno, esta consulta retorna a preferência do sistema do usuário.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Vantagens :&amp;lt;/strong&amp;gt; - Experiência personalizada desde a primeira visita - Coerência com o ambiente do sistema do usuário - Nenhum armazenamento necessário para a primeira visita&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Considerações :&amp;lt;/strong&amp;gt; - Nem todos os navegadores suportam esta funcionalidade (mas o suporte é excelente desde 2020) - A verificação`window.matchMedia`garante a compatibilidade - O utilizador pode sempre sobrescrever esta escolha&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_desempenho_por_que_este_script_é_rápido&amp;quot;&amp;gt;Desempenho: Por que este script é rápido&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nosso script de pré-carregamento foi projetado para ser extremamente rápido:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;tamanho mínimo&amp;lt;/strong&amp;gt;Cerca de 300 bytes não minificados, 200 bytes minificados. É insignificante comparado a qualquer imagem ou biblioteca JavaScript.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Em linha&amp;lt;/strong&amp;gt;: Sem requisição HTTP adicional. O script está no HTML, portanto está imediatamente disponível.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Operações síncronas simples&amp;lt;/strong&amp;gt;: Leitura de uma chave no localStorage (operação ultra-rápida) e modificação de um atributo DOM (operação nativa do navegador).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Nenhuma dependência&amp;lt;/strong&amp;gt;: Sem framework, sem biblioteca, apenas JavaScript puro. Sem tempo de inicialização, sem análise de dependências.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Execução única&amp;lt;/strong&amp;gt;: Este script é executado uma única vez no carregamento. Sem listeners de eventos, sem loops, sem cálculos complexos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Na prática, em hardware moderno, esse script é executado em menos de 1 milissegundo, um tempo imperceptível que não tem impacto no desempenho de carregamento da página.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_colocação_óptima_no_head&amp;quot;&amp;gt;Colocação Óptima no `&amp;amp;lt;head&amp;amp;gt;&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A ordem dos elementos no`&amp;amp;lt;head&amp;amp;gt;`É importante. Eis a ordem recomendada:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;!-- 1. Métadonnées critiques --&amp;amp;gt;
    &amp;amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;amp;gt;
    &amp;amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&amp;amp;gt;

    &amp;amp;lt;!-- 2. Notre script de pré-chargement (IMMÉDIATEMENT après les meta) --&amp;amp;gt;
    &amp;amp;lt;script&amp;amp;gt;
      (function() { /* notre code */ })();
    &amp;amp;lt;/script&amp;amp;gt;

    &amp;amp;lt;!-- 3. Titre de la page --&amp;amp;gt;
    &amp;amp;lt;title&amp;amp;gt;Mon Site&amp;amp;lt;/title&amp;amp;gt;

    &amp;amp;lt;!-- 4. Feuilles de style --&amp;amp;gt;
    &amp;amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;styles.css&amp;quot;&amp;amp;gt;

    &amp;amp;lt;!-- 5. Autres ressources (fonts, favicons, etc.) --&amp;amp;gt;
    &amp;amp;lt;link rel=&amp;quot;preconnect&amp;quot; href=&amp;quot;https://fonts.googleapis.com&amp;quot;&amp;amp;gt;
    &amp;amp;lt;link rel=&amp;quot;icon&amp;quot; href=&amp;quot;favicon.ico&amp;quot;&amp;amp;gt;

    &amp;amp;lt;!-- 6. Autres scripts avec defer ou async --&amp;amp;gt;
    &amp;amp;lt;script src=&amp;quot;app.js&amp;quot; defer&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
&amp;amp;lt;/head&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta ordem garante que :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O charset é definido antes de qualquer processamento de texto&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Nosso script é executado antes do carregamento dos CSS&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Os CSS são carregados depois e aplicam diretamente o tema correto&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Os outros recursos não críticos são carregados por último&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_passo_3_o_poder_dos_seletores_de_atributos_css&amp;quot;&amp;gt;Passo 3 : O Poder dos Seletores de Atributos CSS&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_sistema_de_tema_do_bootstrap_5&amp;quot;&amp;gt;O sistema de tema do Bootstrap 5&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Bootstrap 5 introduziu um sistema elegante de gerenciamento de temas baseado nas propriedades personalizadas CSS (variáveis CSS) e nos seletores de atributos. Este sistema usa o atributo`data-bs-theme`no elemento`&amp;amp;lt;html&amp;amp;gt;`para determinar qual conjunto de variáveis de cor aplicar.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A beleza deste sistema reside na sua simplicidade: em vez de carregar diferentes folhas de estilo ou de alternar as classes em milhares de elementos, alteramos simplesmente um atributo em um único elemento, e o CSS faz o resto graças à cascata.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estrutura_css_para_um_sistema_de_tema&amp;quot;&amp;gt;Estrutura CSS para um Sistema de Tema&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está uma estrutura CSS completa para implementar um sistema de tema robusto:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;/**
 * SYSTÈME DE THÈME COMPLET
 * Utilise les Custom Properties CSS pour une maintenance facile
 */

/* ============================================
   THÈME PAR DÉFAUT (LIGHT)
   Défini sur :root pour être le fallback
   ============================================ */
:root {
  /* Couleurs de base */
  --color-primary: #0d6efd;
  --color-secondary: #6c757d;
  --color-success: #198754;
  --color-danger: #dc3545;
  --color-warning: #ffc107;
  --color-info: #0dcaf0;

  /* Couleurs de fond */
  --bg-primary: #ffffff;
  --bg-secondary: #f8f9fa;
  --bg-tertiary: #e9ecef;

  /* Couleurs de texte */
  --text-primary: #212529;
  --text-secondary: #6c757d;
  --text-tertiary: #adb5bd;

  /* Couleurs de bordure */
  --border-color: #dee2e6;
  --border-color-subtle: #e9ecef;

  /* Couleurs d&amp;#39;ombre */
  --shadow-sm: rgba(0, 0, 0, 0.075);
  --shadow-md: rgba(0, 0, 0, 0.15);
  --shadow-lg: rgba(0, 0, 0, 0.25&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Logging Eficaz nos Testes Funcionais de Plugins Gradle</title>
            <link >https://cheroliv.com//blog/2025/0097_gradle_plugin_dev_func_test_logger_post.html</link>
            <pubDate>Fri, 7 Nov 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0097_gradle_plugin_dev_func_test_logger_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table of Contents&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_dependência_ao_gradlerunner&amp;quot;&amp;gt;O Problema : Dependência ao GradleRunner&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_contexto_do_desenvolvimento_de_plugin&amp;quot;&amp;gt;Contexto do Desenvolvimento de Plugin&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_dilema_do_logging&amp;quot;&amp;gt;O Dilema do Logging&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_as_falsas_boas_ideias&amp;quot;&amp;gt;As falsas boas ideias&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_logger_independente&amp;quot;&amp;gt;A Solução: Logger Independente&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_recomendada&amp;quot;&amp;gt;Arquitetura Recomendada&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_fluxo_de_execução&amp;quot;&amp;gt;Fluxo de Execução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_implementação_completa&amp;quot;&amp;gt;Implementação Completa&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_dependências_gradle&amp;quot;&amp;gt;Dependências Gradle&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configuração_logback&amp;quot;&amp;gt;Configuração Logback&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_classe_de_teste_completa&amp;quot;&amp;gt;Classe de teste completa&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_esquema_de_logging_multi_nível&amp;quot;&amp;gt;Esquema de Logging Multi-Nível&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_boas_práticas&amp;quot;&amp;gt;Boas Práticas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estrutura_de_logging_recomendada&amp;quot;&amp;gt;Estrutura de Logging Recomendada&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_símbolos_visuais_para_a_legibilidade&amp;quot;&amp;gt;Símbolos Visuais para a Legibilidade&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_extração_de_informações_da_saída&amp;quot;&amp;gt;Extração de Informações da Saída&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_comparação_antesdepois&amp;quot;&amp;gt;Comparação Antes/Depois&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_antes_abordagem_problemática&amp;quot;&amp;gt;Antes : Abordagem Problemática&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_depois_abordagem_robusta&amp;quot;&amp;gt;Depois: Abordagem Robusta&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_resumo&amp;quot;&amp;gt;Diagrama Resumo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_armadilhas_a_evitar&amp;quot;&amp;gt;armadilhas a evitar&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_armadilha_nº1_esquecer_forwardoutput&amp;quot;&amp;gt;Armadilha nº1 : Esquecer `.forwardOutput()&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_armadilha_n2_logger_não_inicializado&amp;quot;&amp;gt;❌ Armadilha n°2: Logger Não Inicializado&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_armadilha_nº_3_log_excessivo&amp;quot;&amp;gt;Armadilha nº 3 : Log excessivo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_resultados_e_métricas&amp;quot;&amp;gt;Resultados e Métricas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_recursos&amp;quot;&amp;gt;Recursos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_ir_mais_longe&amp;quot;&amp;gt;Ir mais longe&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Durante o desenvolvimento de um plugin Gradle, os testes funcionais são essenciais para validar o comportamento do plugin em um ambiente Gradle real. No entanto, uma questão crucial se coloca rapidamente:&amp;lt;strong&amp;gt;como configurar um sistema de logs eficaz sem criar dependências problemáticas com o &amp;lt;code&amp;gt;GradleRunner&amp;lt;/code&amp;gt;?&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Neste artigo, vamos explorar :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;A problemática de logging nos testes funcionais do Gradle&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;A arquitetura de uma solução robusta com SLF4J e Logback&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;A implementação completa com exemplos concretos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;As boas práticas e armadilhas a evitar&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_problema_dependência_ao_gradlerunner&amp;quot;&amp;gt;O Problema : Dependência ao GradleRunner&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_contexto_do_desenvolvimento_de_plugin&amp;quot;&amp;gt;Contexto do Desenvolvimento de Plugin&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao desenvolver um plugin Gradle, geralmente usamos o`GradleRunner`do &amp;lt;em&amp;gt;Gradle TestKit&amp;lt;/em&amp;gt; para executar dos builds de teste:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test
fun `test my plugin`() {
    val result = GradleRunner.create()
        .withProjectDir(projectDir)
        .withArguments(&amp;quot;myTask&amp;quot;)
        .build()

    // Comment logger efficacement ici ?
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_dilema_do_logging&amp;quot;&amp;gt;O Dilema do Logging&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O problema principal é o seguinte:&amp;lt;strong&amp;gt;O logging não deve depender de um objeto do qual não se tem certeza da inicialização.&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-0c5781202c395a4b87cd761f90af9413.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;442&amp;quot; height=&amp;quot;262&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_as_falsas_boas_ideias&amp;quot;&amp;gt;As falsas boas ideias&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock warning&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-warning&amp;quot; title=&amp;quot;Warning&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Não façam isso:&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// ❌ MAUVAIS: Tentative d&amp;#39;obtenir un logger du runner
val runner = GradleRunner.create()
val logger = runner.logger // N&amp;#39;existe pas !
// ❌ MAUVAIS: Logger partagé initialisé tardivement
lateinit var logger: Logger
@BeforeEach
fun setup() {
    logger = LoggerFactory.getLogger(...)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_logger_independente&amp;quot;&amp;gt;A Solução: Logger Independente&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_arquitetura_recomendada&amp;quot;&amp;gt;Arquitetura Recomendada&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A solução consiste em usar um logger&amp;lt;strong&amp;gt;totalmente independente&amp;lt;/strong&amp;gt; du &amp;lt;code&amp;gt;GradleRunner&amp;lt;/code&amp;gt;, inicializado no nível da classe de teste.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-09f9be2507b1dd768e2766422b70c50e.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;601&amp;quot; height=&amp;quot;448&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_fluxo_de_execução&amp;quot;&amp;gt;Fluxo de Execução&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-a52adcce9939984a9c8e8a4fc1953e2d.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;786&amp;quot; height=&amp;quot;686&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_implementação_completa&amp;quot;&amp;gt;Implementação Completa&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_dependências_gradle&amp;quot;&amp;gt;Dependências Gradle&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Adicione as dependências necessárias no seu`build.gradle.kts` :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;dependencies {
    // Pour les tests fonctionnels
    testImplementation(gradleTestKit())
    testImplementation(&amp;quot;org.junit.jupiter:junit-jupiter:5.10.2&amp;quot;)

    // Pour le logging
    testImplementation(&amp;quot;org.slf4j:slf4j-api:2.0.17&amp;quot;)
    testRuntimeOnly(&amp;quot;ch.qos.logback:logback-classic:1.4.14&amp;quot;)

    // Pour les assertions
    testImplementation(&amp;quot;org.assertj:assertj-core:3.27.6&amp;quot;)
}

tasks.test {
    useJUnitPlatform()
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_configuração_logback&amp;quot;&amp;gt;Configuração Logback&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Crie o arquivo`src/test/resources/logback-test.xml` :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-xml hljs&amp;quot; data-lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;amp;gt;
&amp;amp;lt;configuration&amp;amp;gt;
    &amp;amp;lt;!-- Appender console avec format lisible --&amp;amp;gt;
    &amp;amp;lt;appender name=&amp;quot;STDOUT&amp;quot; class=&amp;quot;ch.qos.logback.core.ConsoleAppender&amp;quot;&amp;amp;gt;
        &amp;amp;lt;encoder&amp;amp;gt;
            &amp;amp;lt;pattern&amp;amp;gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&amp;amp;lt;/pattern&amp;amp;gt;
        &amp;amp;lt;/encoder&amp;amp;gt;
    &amp;amp;lt;/appender&amp;amp;gt;

    &amp;amp;lt;!-- Appender fichier pour conservation --&amp;amp;gt;
    &amp;amp;lt;appender name=&amp;quot;FILE&amp;quot; class=&amp;quot;ch.qos.logback.core.FileAppender&amp;quot;&amp;amp;gt;
        &amp;amp;lt;file&amp;amp;gt;build/test-results/functional-tests.log&amp;amp;lt;/file&amp;amp;gt;
        &amp;amp;lt;encoder&amp;amp;gt;
            &amp;amp;lt;pattern&amp;amp;gt;%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n&amp;amp;lt;/pattern&amp;amp;gt;
        &amp;amp;lt;/encoder&amp;amp;gt;
    &amp;amp;lt;/appender&amp;amp;gt;

    &amp;amp;lt;!-- Logger pour vos tests --&amp;amp;gt;
    &amp;amp;lt;logger name=&amp;quot;com.cheroliv.bakery&amp;quot; level=&amp;quot;DEBUG&amp;quot;/&amp;amp;gt;

    &amp;amp;lt;!-- Réduire le bruit de Gradle --&amp;amp;gt;
    &amp;amp;lt;logger name=&amp;quot;org.gradle&amp;quot; level=&amp;quot;WARN&amp;quot;/&amp;amp;gt;

    &amp;amp;lt;!-- Configuration racine --&amp;amp;gt;
    &amp;amp;lt;root level=&amp;quot;INFO&amp;quot;&amp;amp;gt;
        &amp;amp;lt;appender-ref ref=&amp;quot;STDOUT&amp;quot;/&amp;amp;gt;
        &amp;amp;lt;appender-ref ref=&amp;quot;FILE&amp;quot;/&amp;amp;gt;
    &amp;amp;lt;/root&amp;amp;gt;
&amp;amp;lt;/configuration&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_classe_de_teste_completa&amp;quot;&amp;gt;Classe de teste completa&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;package com.cheroliv.bakery

import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.TaskOutcome
import org.junit.jupiter.api.*
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
import org.junit.jupiter.api.io.TempDir
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.File
import kotlin.test.assertEquals
import kotlin.test.assertTrue

@TestInstance(PER_CLASS)
class BakeryPluginFunctionalTest {

    @field:TempDir
    lateinit var projectDir: File

    private val buildFile by lazy { projectDir.resolve(&amp;quot;build.gradle.kts&amp;quot;) }
    private val settingsFile by lazy { projectDir.resolve(&amp;quot;settings.gradle.kts&amp;quot;) }

    companion object {
        // ✓ Logger indépendant, initialisé au chargement de la classe
        private val logger: Logger = LoggerFactory.getLogger(
            BakeryPluginFunctionalTest::class.java
        )

        @JvmStatic
        @BeforeAll
        fun globalSetup() {
            logger.info(&amp;quot;═&amp;quot;.repeat(60))
            logger.info(&amp;quot;DÉMARRAGE DE LA SUITE DE TESTS FONCTIONNELS&amp;quot;)
            logger.info(&amp;quot;═&amp;quot;.repeat(60))
        }

        @JvmStatic
        @AfterAll
        fun globalTeardown() {
            logger.info(&amp;quot;═&amp;quot;.repeat(60))
            logger.info(&amp;quot;FIN DE LA SUITE DE TESTS&amp;quot;)
            logger.info(&amp;quot;═&amp;quot;.repeat(60))
        }
    }

    @BeforeEach
    fun setup() {
        logger.info(&amp;quot;─&amp;quot;.repeat(60))
        logger.info(&amp;quot;Préparation de l&amp;#39;environnement de test&amp;quot;)
        logger.debug(&amp;quot;Répertoire de test: ${projectDir.absolutePath}&amp;quot;)

        settingsFile.writeText(&amp;quot;&amp;quot;&amp;quot;
            rootProject.name = &amp;quot;test-bakery-project&amp;quot;
        &amp;quot;&amp;quot;&amp;quot;.trimIndent())

        logger.debug(&amp;quot;✓ settings.gradle.kts créé&amp;quot;)
    }

    @AfterEach
    fun teardown(testInfo: TestInfo) {
        logger.info(&amp;quot;✓ Test terminé: ${testInfo.displayName}&amp;quot;)
        logger.info(&amp;quot;─&amp;quot;.repeat(60))
    }

    @Test
    fun `plugin can be applied successfully`() {
        logger.info(&amp;quot;TEST: Application du plugin Bakery&amp;quot;)

        buildFile.writeText(&amp;quot;&amp;quot;&amp;quot;
            plugins {
                id(&amp;quot;com.cheroliv.bakery&amp;quot;)
            }
        &amp;quot;&amp;quot;&amp;quot;.trimIndent())

        logger.debug(&amp;quot;Fichier build.gradle.kts créé&amp;quot;)
        logger.debug(&amp;quot;Lancement du build Gradle...&amp;quot;)

        val startTime = System.currentTimeMillis()

        val result = GradleRunner.create()
            .forwardOutput() // ← Important : capture la sortie
            .withPluginClasspath()
            .withArguments(&amp;quot;tasks&amp;quot;, &amp;quot;--group=bakery&amp;quot;, &amp;quot;--stacktrace&amp;quot;)
            .withProjectDir(projectDir)
            .build()

        val duration = System.currentTimeMillis() - startTime
        logger.info(&amp;quot;Build terminé en ${duration}ms&amp;quot;)

        // Analyse de la sortie
        logger.debug(&amp;quot;Analyse de la sortie Gradle...&amp;quot;)
        val outputLines = result.output.lines()
        logger.debug(&amp;quot;Nombre de lignes capturées: ${outputLines.size}&amp;quot;)

        // Validation
        val expectedTasks = listOf(&amp;quot;bake&amp;quot;, &amp;quot;printConfigPath&amp;quot;, &amp;quot;printJBakeClasspath&amp;quot;)
        logger.info(&amp;quot;Vérification des tâches attendues:&amp;quot;)

        expectedTasks.forEach { task -&amp;amp;gt;
            val found = result.output.contains(task)
            logger.debug(&amp;quot;  ${if (found) &amp;quot;✓&amp;quot; else &amp;quot;✗&amp;quot;} $task&amp;quot;)
            assertTrue(found, &amp;quot;Task &amp;#39;$task&amp;#39; should be available&amp;quot;)
        }

        logger.info(&amp;quot;✓ Plugin appliqué avec succès - ${expectedTasks.size} tâches trouvées&amp;quot;)
    }

    @Test
    fun `bake task executes and prints version`() {
        logger.info(&amp;quot;TEST: Exécution de la tâche &amp;#39;bake&amp;#39;&amp;quot;)

        buildFile.writeText(&amp;quot;&amp;quot;&amp;quot;
            plugins {
                id(&amp;quot;com.cheroliv.bakery&amp;quot;)
            }
        &amp;quot;&amp;quot;&amp;quot;.trimIndent())

        logger.debug(&amp;quot;Exécution de &amp;#39;gradle bake&amp;#39;...&amp;quot;)

        val result = GradleRunner.create()
            .forwardOutput()
            .withPluginClasspath()
            .withArguments(&amp;quot;bake&amp;quot;, &amp;quot;--info&amp;quot;) // --info pour plus de détails
            .withProjectDir(projectDir)
            .build()

        // Extraction d&amp;#39;informations depuis la sortie
        logger.debug(&amp;quot;Recherche du message &amp;#39;Baking site&amp;#39;...&amp;quot;)
        val hasBakingMessage = result.output.contains(&amp;quot;Baking site with Bakery Plugin!&amp;quot;)

        logger.debug(&amp;quot;Recherche de la version JBake...&amp;quot;)
        val hasVersionMessage = result.output.contains(&amp;quot;Using JBake version:&amp;quot;)

        // Extraction de la version (si présente)
        val versionRegex = Regex(&amp;quot;Using JBake version: (.+)&amp;quot;)
        val versionMatch = versionRegex.find(result.output)
        if (versionMatch != null) {
            val version = versionMatch.groupValues[1]
            logger.info(&amp;quot;Version JBake détectée: $version&amp;quot;)
        }

        // Validation
        assertTrue(hasBakingMessage, &amp;quot;Message &amp;#39;Baking site&amp;#39; attendu&amp;quot;)
        assertTrue(hasVersionMessage, &amp;quot;Message de version attendu&amp;quot;)
        assertEquals(TaskOutcome.SUCCESS, result.task(&amp;quot;:bake&amp;quot;)?.outcome)

        logger.info(&amp;quot;✓ Tâche &amp;#39;bake&amp;#39; exécutée avec succès&amp;quot;)
    }

    @Test
    fun `analyze output with structured logging`() {
        logger.info(&amp;quot;TEST: Analyse structurée de la sortie&amp;quot;)

        buildFile.writeText(&amp;quot;&amp;quot;&amp;quot;
            plugins {
                id(&amp;quot;com.cheroliv.bakery&amp;quot;)
            }

            task(&amp;quot;diagnostics&amp;quot;) {
                doLast {
                    println(&amp;quot;[DIAG] Project: ${&amp;#39;$&amp;#39;}{project.name}&amp;quot;)
                    println(&amp;quot;[DIAG] Build dir: ${&amp;#39;$&amp;#39;}{project.buildDir}&amp;quot;)
                    println(&amp;quot;[DIAG] Plugin applied: true&amp;quot;)

                    val config = configurations.findByName(&amp;quot;jbakeRuntime&amp;quot;)
                    println(&amp;quot;[DIAG] JBake config exists: ${&amp;#39;$&amp;#39;}{config != null}&amp;quot;)

                    if (config != null) {
                        println(&amp;quot;[DIAG] Dependencies count: ${&amp;#39;$&amp;#39;}{config.dependencies.size}&amp;quot;)
                    }
                }
            }
        &amp;quot;&amp;quot;&amp;quot;.trimIndent())

        logger.debug(&amp;quot;Exécution de la tâche diagnostics...&amp;quot;)

        val result = GradleRunner.create()
            .forwardOutput()
            .withPluginClasspath()
            .withArguments(&amp;quot;diagnostics&amp;quot;, &amp;quot;--quiet&amp;quot;)
            .withProjectDir(projectDir)
            .build()

        // Analyse structurée
        logger.info(&amp;quot;Extraction des informations de diagnostic:&amp;quot;)

        val diagLines = result.output.lines()
            .filter { it.startsWith(&amp;quot;[DIAG]&amp;quot;) }

        logger.debug(&amp;quot;Nombre de lignes de diagnostic: ${diagLines.size}&amp;quot;)

        diagLines.forEach { line -&amp;amp;gt;
            logger.info(&amp;quot;  $line&amp;quot;)

            // Analyse spécifique par type de ligne
            when {
                line.contains(&amp;quot;Project:&amp;quot;) -&amp;amp;gt; {
                    val projectName = line.substringAfter(&amp;quot;Project:&amp;quot;).trim()
                    logger.debug(&amp;quot;    → Nom du projet extrait: &amp;#39;$projectName&amp;#39;&amp;quot;)
                }
                line.contains(&amp;quot;Dependencies count:&amp;quot;) -&amp;amp;gt; {
                    val count = line.substringAfter(&amp;quot;count:&amp;quot;).trim()
                    logger.debug(&amp;quot;    → Nombre de dépendances: $count&amp;quot;)
                }
            }
        }

        assertTrue(diagLines.isNotEmpty(), &amp;quot;Des lignes de diagnostic devraient être présentes&amp;quot;)
        logger.info(&amp;quot;✓ Analyse structurée terminée - ${diagLines.size} lignes analysées&amp;quot;)
    }

    @Test
    fun `test with error handling`() {
        logger.info(&amp;quot;TEST: Gestion d&amp;#39;erreurs avec logging&amp;quot;)

        buildFile.writeText(&amp;quot;&amp;quot;&amp;quot;
            plugins {
                id(&amp;quot;com.cheroliv.bakery&amp;quot;)
            }
        &amp;quot;&amp;quot;&amp;quot;.trimIndent())

        try {
            logger.debug(&amp;quot;Tentative d&amp;#39;exécution...&amp;quot;)

            val result = GradleRunner.create()
                .forwardOutput()
                .withPluginClasspath()
                .withArguments(&amp;quot;printJBakeClasspath&amp;quot;)
                .withProjectDir(projectDir)
                .build()

            val outcome = result.task(&amp;quot;:printJBakeClasspath&amp;quot;)?.outcome
            logger.info(&amp;quot;Outcome: $outcome&amp;quot;)

            if (outcome == TaskOutcome.SUCCESS) {
                logger.info(&amp;quot;✓ Exécution réussie&amp;quot;)
            } else {
                logger.warn(&amp;quot;⚠ Outcome inattendu: $outcome&amp;quot;)
            }

            assertEquals(TaskOutcome.SUCCESS, outcome)

        } catch (e: Exception) {
            logger.error(&amp;quot;✗ Échec du test&amp;quot;, e)
            logger.error(&amp;quot;Type d&amp;#39;exception: ${e.javaClass.simpleName}&amp;quot;)
            logger.error(&amp;quot;Message: ${e.message}&amp;quot;)

            // Log de la stack trace si nécessaire
            if (logger.isDebugEnabled) {
                logger.debug(&amp;quot;Stack trace complète:&amp;quot;, e)
            }

            throw e
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_esquema_de_logging_multi_nível&amp;quot;&amp;gt;Esquema de Logging Multi-Nível&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-341e65e1225cc2d3c65be818c3f39e2e.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;314&amp;quot; height=&amp;quot;911&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_boas_práticas&amp;quot;&amp;gt;Boas Práticas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estrutura_de_logging_recomendada&amp;quot;&amp;gt;Estrutura de Logging Recomendada&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test
fun `mon test`() {
    // 1. LOG: Début du test
    logger.info(&amp;quot;TEST: Description du test&amp;quot;)

    // 2. LOG: Préparation
    logger.debug(&amp;quot;Préparation des fichiers...&amp;quot;)
    buildFile.writeText(&amp;quot;...&amp;quot;)
    logger.debug(&amp;quot;✓ Fichiers préparés&amp;quot;)

    // 3. LOG: Exécution
    logger.debug(&amp;quot;Lancement du build...&amp;quot;)
    val startTime = System.currentTimeMillis()

    val result = GradleRunner.create()
        .forwardOutput()
        .withArguments(&amp;quot;task&amp;quot;)
        .build()

    val duration = System.currentTimeMillis() - startTime
    logger.info(&amp;quot;Build terminé en ${duration}ms&amp;quot;)

    // 4. LOG: Analyse
    logger.debug(&amp;quot;Analyse des résultats...&amp;quot;)
    // ... validations ...

    // 5. LOG: Conclusion
    logger.info(&amp;quot;✓ Test réussi&amp;quot;)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_símbolos_visuais_para_a_legibilidade&amp;quot;&amp;gt;Símbolos Visuais para a Legibilidade&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Use símbolos Unicode para melhorar a legibilidade dos logs:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;logger.info(&amp;quot;✓ Succès&amp;quot;)
logger.info(&amp;quot;✗ Échec&amp;quot;)
logger.info(&amp;quot;⚠ Attention&amp;quot;)
logger.info(&amp;quot;→ Étape suivante&amp;quot;)
logger.info(&amp;quot;═&amp;quot;.repeat(60)) // Séparateur principal
logger.info(&amp;quot;─&amp;quot;.repeat(60)) // Séparateur secondaire&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_extração_de_informações_da_saída&amp;quot;&amp;gt;Extração de Informações da Saída&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// Recherche simple
val hasMessage = result.output.contains(&amp;quot;Expected message&amp;quot;)

// Extraction avec regex
val versionRegex = Regex(&amp;quot;Version: (.+)&amp;quot;)
val version = versionRegex.find(result.output)?.groupValues?.get(1)

// Filtrage de lignes
val errorLines = result.output.lines()
    .filter { it.contains(&amp;quot;ERROR&amp;quot;) }

errorLines.forEach { logger.error(&amp;quot;Gradle error: $it&amp;quot;) }&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_comparação_antesdepois&amp;quot;&amp;gt;Comparação Antes/Depois&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_antes_abordagem_problemática&amp;quot;&amp;gt;Antes : Abordagem Problemática&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;class MyTest {
    lateinit var logger: Logger // ❌ Initialisé tardivement

    @BeforeEach
    fun setup() {
        logger = LoggerFactory.getLogger(...) // ❌ Dépendance
    }

    @Test
    fun test() {
        val runner = GradleRunner.create()
        logger.info(&amp;quot;Testing...&amp;quot;) // ⚠ Peut échouer
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_depois_abordagem_robusta&amp;quot;&amp;gt;Depois: Abordagem Robusta&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;class MyTest {
    companion object {
        private val logger = LoggerFactory.getLogger(...) // ✓ Indépendant
    }

    @Test
    fun test() {
        logger.info(&amp;quot;TEST: Starting...&amp;quot;) // ✓ Toujours disponible

        val result = GradleRunner.create()
            .forwardOutput() // ✓ Capture la sortie
            .build()

        logger.debug(&amp;quot;Output: ${result.output}&amp;quot;) // ✓ Analyse
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_diagrama_resumo&amp;quot;&amp;gt;Diagrama Resumo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 9) ]

@startuml




...
... ( skipping 129 lines )
...
}
skinparam StereotypeI {
  BackgroundColor white
  BorderColor black
}
skinparam StereotypeN {
  BackgroundColor white
  BorderColor black
}
skinparam UseCaseStereoType {
  FontColor black
  FontName Verdana
}
skinparam backgroundColor #FEFEFE

title Fluxo Completo de Logging nos Testes Funcionais
actor &amp;quot;desenvolvedor&amp;quot; as dev
participant &amp;quot;JUnit&amp;quot; as junit
participant &amp;quot;ClasseTeste&amp;quot; as test
participant &amp;quot;Logger
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
!theme plain
skinparam backgroundColor #FEFEFE

title Fluxo Completo de Logging nos Testes Funcionais
actor &amp;quot;desenvolvedor&amp;quot; as dev
participant &amp;quot;JUnit&amp;quot; as junit
participant &amp;quot;ClasseTeste&amp;quot; as test
participant &amp;quot;Logger
(SLF4J)&amp;quot; as logger
participant &amp;quot;Logback&amp;quot; as logback
participant &amp;quot;GradleRunner&amp;quot; as runner
participant &amp;quot;Console/fichier&amp;quot; as output

dev -&amp;amp;gt; junit : lance les tests

activate junit
junit -&amp;amp;gt; test : charge la classe
activate test

test -&amp;amp;gt; logger : initialize (companion)
activate logger
logger -&amp;amp;gt; logback : configure
activate logback
logback --&amp;amp;gt; logger : ready
deactivate logback
logger --&amp;amp;gt; test : ✓
deactivate logger

== Phase de Setup ==

junit -&amp;amp;gt; test : @BeforeAll
test -&amp;amp;gt; logger : info(&amp;quot;Inicialização&amp;quot;)
logger -&amp;amp;gt; output : [INFO] Initialisation

junit -&amp;amp;gt; test : @BeforeEach
test -&amp;amp;gt; logger : debug(&amp;quot;Configurar teste&amp;quot;)
logger -&amp;amp;gt; output : [DEBUG] Setup test

== Phase de Test ==

junit -&amp;amp;gt; test : @Test
test -&amp;amp;gt; logger : info(&amp;quot;TEST: Iniciando&amp;quot;)
logger -&amp;amp;gt; output : [INFO] TEST: Starting

test -&amp;amp;gt; runner : create()
activate runner

test -&amp;amp;gt; runner : .forwardOutput()
test -&amp;amp;gt; runner : .build()

runner -&amp;amp;gt; runner : execute Gradle
runner --&amp;amp;gt; test : BuildResult + output text
deactivate runner

test -&amp;amp;gt; logger : debug(&amp;quot;Analisando saída&amp;quot;)
logger -&amp;amp;gt; output : [DEBUG] Analyzing output

test -&amp;amp;gt; test : assertions
test -&amp;amp;gt; logger : info(&amp;quot;�✓ Teste aprovado&amp;quot;)
logger -&amp;amp;gt; output : [INFO] ✓ Test passed

== Phase de Cleanup ==

junit -&amp;amp;gt; test : @AfterEach
test -&amp;amp;gt; logger : debug(&amp;quot;Limpeza&amp;quot;)
logger -&amp;amp;gt; output : [DEBUG] Cleanup

junit -&amp;amp;gt; test : @AfterAll
test -&amp;amp;gt; logger : info(&amp;quot;Fim dos testes&amp;quot;)
logger -&amp;amp;gt; output : [INFO] Fin des tests

test --&amp;amp;gt; junit : ✓ all tests passed
deactivate test
deactivate junit

dev &amp;amp;lt;-- junit : rapport de tests

@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_armadilhas_a_evitar&amp;quot;&amp;gt;armadilhas a evitar&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_armadilha_nº1_esquecer_forwardoutput&amp;quot;&amp;gt;Armadilha nº1 : Esquecer `.forwardOutput()&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// MAUVAIS: La sortie Gradle n&amp;#39;est pas capturée
val result = GradleRunner.create()
    .withArguments(&amp;quot;task&amp;quot;)
    .build()

// result.output sera vide !&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// BON: La sortie est capturée
val result = GradleRunner.create()
    .forwardOutput() // ✓
    .withArguments(&amp;quot;task&amp;quot;)
    .build()&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_armadilha_n2_logger_não_inicializado&amp;quot;&amp;gt;❌ Armadilha n°2: Logger Não Inicializado&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// MAUVAIS
class Test {
    lateinit var logger: Logger // Peut ne pas être initialisé
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// BON
class Test {
    companion object {
        private val logger = LoggerFactory.getLogger(...) // Toujours initialisé
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_armadilha_nº_3_log_excessivo&amp;quot;&amp;gt;Armadilha nº 3 : Log excessivo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// MAUVAIS: Trop de logs DEBUG en production
logger.debug(&amp;quot;Variable a = $a&amp;quot;)
logger.debug(&amp;quot;Variable b = $b&amp;quot;)
logger.debug(&amp;quot;Variable c = $c&amp;quot;)
// ... 100 lignes de logs&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// BON: Logging ciblé
logger.info(&amp;quot;Traitement de ${items.size} éléments&amp;quot;)
logger.debug(&amp;quot;Détails: ${items.take(5)}...&amp;quot;) // Échantillon seulement&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_resultados_e_métricas&amp;quot;&amp;gt;Resultados e Métricas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com essa abordagem, você obtém :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 28.5714%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 42.8571%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 28.5715%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Métrico&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Antes&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;depois&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Linhas de código&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;~50 linhas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;~80 linhas (+60%)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tempo de depuração&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;aproximadamente 30 min&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;~5 min (-83%)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Erros detectados&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;básico&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;detalhado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Manutenção&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Difícil&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Simples&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Independência&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;�❌ Acoplada&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Independente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A implementação de um sistema de registro robusto nos testes funcionais de plugins Gradle baseia-se em um princípio simples:&amp;lt;strong&amp;gt;independência&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Pontos-chave a reter:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Logger independente&amp;lt;/strong&amp;gt;: Inicializado em um`companion object`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;SLF4J + Logback&amp;lt;/strong&amp;gt;: Stack testada e configurável&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;.forwardOutput()&amp;lt;/strong&amp;gt;: Captura da saída do Gradle&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Logging estruturado&amp;lt;/strong&amp;gt;INFO para as etapas, DEBUG para os detalhes&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Análise de saída&amp;lt;/strong&amp;gt;: Extração de informações desde`result.output`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta abordagem garante testes&amp;lt;strong&amp;gt;mantível&amp;lt;/strong&amp;gt;, &amp;lt;strong&amp;gt;depuráveis&amp;lt;/strong&amp;gt; et &amp;lt;strong&amp;gt;robustos&amp;lt;/strong&amp;gt;, respeitando os princípios de design sólidos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_recursos&amp;quot;&amp;gt;Recursos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.gradle.org/current/userguide/test_kit.html&amp;quot;&amp;gt;Documentação do Gradle TestKit&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://www.slf4j.org/manual.html&amp;quot;&amp;gt;Manual SLF4J&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://logback.qos.ch/manual/configuration.html&amp;quot;&amp;gt;Logback Configuration&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/gradle/gradle/tree/master/subprojects/test-kit&amp;quot;&amp;gt;Código Fonte do Gradle TestKit&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_ir_mais_longe&amp;quot;&amp;gt;Ir mais longe&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em um próximo artigo, vamos explorar :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Integração contínua com logging estruturado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Os relatórios HTML de testes com logs integrados&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Depuração avançada com JVM attach&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O desempenho e as otimizações de logging&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Este artigo faz parte da série &amp;quot;Desenvolvimento de Plugins Gradle&amp;quot; no meu blog técnico._&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Exibir quadrados coloridos no AsciiDoc: Guia prático</title>
            <link >https://cheroliv.com//blog/2025/0096_afficher-couleurs-asciidoc_post.html</link>
            <pubDate>Sat, 1 Nov 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0096_afficher-couleurs-asciidoc_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table of Contents&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_problemática&amp;quot;&amp;gt;A problemática&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_contexto_de_uso&amp;quot;&amp;gt;Contexto de uso&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_as_abordagens_possíveis&amp;quot;&amp;gt;As abordagens possíveis&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_opção_1_caracteres_unicode&amp;quot;&amp;gt;Opção 1: Caracteres Unicode&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_opção_2_imagens_externas&amp;quot;&amp;gt;Opção 2: Imagens externas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_opção_3_papéis_css_personalizados&amp;quot;&amp;gt;Opção 3 : Papéis CSS personalizados&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_opção_4_injeção_html_solução_retenida&amp;quot;&amp;gt;Opção 4: Injeção HTML (solução retenida)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_injeção_de_html_com&amp;quot;&amp;gt;A solução : Injeção de HTML com &amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_princípio_de_funcionamento&amp;quot;&amp;gt;Princípio de funcionamento&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_implementação&amp;quot;&amp;gt;Implementação&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_explicação_das_propriedades_css&amp;quot;&amp;gt;Explicação das propriedades CSS&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_exemplo_completo&amp;quot;&amp;gt;Exemplo completo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_optimizações_e_boas_práticas&amp;quot;&amp;gt;Optimizações e boas práticas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_gerir_as_cores_claras&amp;quot;&amp;gt;Gerir as cores claras&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_ajustar_o_tamanho_dos_quadrados&amp;quot;&amp;gt;Ajustar o tamanho dos quadrados&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_criar_variantes&amp;quot;&amp;gt;Criar variantes&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_círculo_colorido&amp;quot;&amp;gt;Círculo colorido&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_quadrado_com_borda_colorida&amp;quot;&amp;gt;Quadrado com borda colorida&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_da_solução&amp;quot;&amp;gt;Arquitetura da solução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_vantagens_e_limitações&amp;quot;&amp;gt;Vantagens e limitações&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_vantagens&amp;quot;&amp;gt;Vantagens&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_limitações&amp;quot;&amp;gt;Limitações&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_melhoria_da_acessibilidade&amp;quot;&amp;gt;Melhoria da acessibilidade&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_exemplo_concreto_documentação_de_temas&amp;quot;&amp;gt;Exemplo concreto: Documentação de temas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_recursos&amp;quot;&amp;gt;Recursos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao redigir documentação técnica, especialmente para guias de estilo ou especificações de interface do usuário, é comum precisar exibir paletas de cores. AsciiDoc, embora muito poderoso para documentação, não oferece sintaxe nativa para exibir amostras de cor. Este artigo explora o problema e propõe uma solução elegante baseada na injeção HTML.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_problemática&amp;quot;&amp;gt;A problemática&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_contexto_de_uso&amp;quot;&amp;gt;Contexto de uso&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Imagine que você está documentando as variáveis CSS de um tema de aplicação web. Você precisa de :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Listar os códigos de cores hexadecimais&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Mostrar um exemplo visual de cada cor&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Manter a legibilidade do documento fonte&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Garantir a compatibilidade com os geradores de sites estáticos como JBake&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;use-case-diagram.svg&amp;quot; alt=&amp;quot;use case diagram&amp;quot; width=&amp;quot;1744&amp;quot; height=&amp;quot;5616&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_as_abordagens_possíveis&amp;quot;&amp;gt;As abordagens possíveis&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Várias soluções podem ser consideradas para exibir cores em um documento AsciiDoc :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;component-diagram.svg&amp;quot; alt=&amp;quot;component diagram&amp;quot; width=&amp;quot;1742&amp;quot; height=&amp;quot;5688&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_opção_1_caracteres_unicode&amp;quot;&amp;gt;Opção 1: Caracteres Unicode&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O uso de caracteres Unicode como`&amp;amp;#9632;`(■) é simples, mas limitada:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;* ■ --accent-color: #7952b3 (violet Bootstrap)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Limitações&amp;lt;/strong&amp;gt; : Sem controle sobre a cor, depende da fonte do sistema.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_opção_2_imagens_externas&amp;quot;&amp;gt;Opção 2: Imagens externas&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Criar imagens PNG ou SVG para cada cor:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;* image:colors/violet.svg[width=16] --accent-color: #7952b3&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Limitations&amp;lt;/strong&amp;gt; : Manutenção pesada, multiplicação dos arquivos, não há sincronização automática.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_opção_3_papéis_css_personalizados&amp;quot;&amp;gt;Opção 3 : Papéis CSS personalizados&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Definir classes CSS e aplicá-las por meio de papéis AsciiDoc:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;* [.color-violet]##■## --accent-color: #7952b3&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Limitações&amp;lt;/strong&amp;gt; : Necessita de uma folha de estilo externa, definição prévia de todas as cores possíveis.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_opção_4_injeção_html_solução_retenida&amp;quot;&amp;gt;Opção 4: Injeção HTML (solução retenida)&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Utilizar a macro``para injetar HTML com estilos em linha :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;
background:#7952b3;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;]
--accent-color: #7952b3 (violet Bootstrap)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_injeção_de_html_com&amp;quot;&amp;gt;A solução : Injeção de HTML com &amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_princípio_de_funcionamento&amp;quot;&amp;gt;Princípio de funcionamento&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A macro``O AsciiDoc permite inserir conteúdo bruto que não será interpretado pelo processador AsciiDoc. Isso nos permite injetar diretamente HTML com estilos CSS inline.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 5) ]

@startuml
participant &amp;quot;Document\nAsciiDoc&amp;quot; as doc
participant &amp;quot;Processador\nAsciiDoc&amp;quot; as processor
participant &amp;quot;pass:[] macro&amp;quot; as pass
participant &amp;quot;HTML
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
participant &amp;quot;Document\nAsciiDoc&amp;quot; as doc
participant &amp;quot;Processador\nAsciiDoc&amp;quot; as processor
participant &amp;quot;pass:[] macro&amp;quot; as pass
participant &amp;quot;HTML
final&amp;quot; as html

doc -&amp;amp;gt; processor : Analyse document
processor -&amp;amp;gt; pass : Détecte pass:[]
pass -&amp;amp;gt; processor : Retourne HTML brut
processor -&amp;amp;gt; html : Insère sans transformation
html -&amp;amp;gt; html : Navigateur applique styles
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_implementação&amp;quot;&amp;gt;Implementação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está a estrutura HTML para injetar:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html hljs&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;span style=&amp;quot;display:inline-block;
             width:1em;
             height:1em;
             background:#7952b3;
             vertical-align:middle;
             margin-right:0.5em&amp;quot;&amp;amp;gt;
&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_explicação_das_propriedades_css&amp;quot;&amp;gt;Explicação das propriedades CSS&amp;lt;/h4&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 75%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Propriedade&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Função&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;display:inline-block&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Permite definir width/height permanecendo no fluxo inline&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;width:1em; height:1em&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tamanho do quadrado relativo ao tamanho da fonte (responsivo)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;background:#7952b3&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;A cor a ser exibida (variável de acordo com suas necessidades)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;vertical-align:middle&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Alinhe o quadrado com o texto adjacente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;margin-right:0.5em&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Espaçamento entre o quadrado e o texto&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_exemplo_completo&amp;quot;&amp;gt;Exemplo completo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este é um exemplo documentando uma paleta de tema:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;== Thème Clair

* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#f8f9fa;border:1px solid #ccc;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --bg-primary: `#f8f9fa` (blanc cassé)
* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#212529;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --text-primary: `#212529` (noir très foncé)
* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#7952b3;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --accent-color: `#7952b3` (violet Bootstrap)
* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#61428f;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --accent-hover: `#61428f` (violet plus foncé)
* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#ffffff;border:1px solid #ccc;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --card-bg: `#ffffff` (blanc)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quem produz a seguinte saída:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Tema Claro&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#f8f9fa;border:1px solid #ccc;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; --bg-primary:`#f8f9fa`(branco quebrado)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#212529;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; --text-primary:`#212529`(preto muito escuro)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#7952b3;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; --cor-de-destaque:`#7952b3`(violeta Bootstrap)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#61428f;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; --accent-hover:`#61428f`(violeta mais escuro)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#ffffff;border:1px solid #ccc;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; --card-bg:`#ffffff`(branco)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_optimizações_e_boas_práticas&amp;quot;&amp;gt;Optimizações e boas práticas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_gerir_as_cores_claras&amp;quot;&amp;gt;Gerir as cores claras&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para cores muito claras (branco, cinza muito claro), adicione uma borda para torná-las visíveis em fundo branco :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html hljs&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;
background:#ffffff;border:1px solid #ccc;
vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A borda cinza (&amp;lt;code&amp;gt;border:1px solid #ccc&amp;lt;/code&amp;gt;) permite delimitar o quadrado branco sobre um fundo branco.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_ajustar_o_tamanho_dos_quadrados&amp;quot;&amp;gt;Ajustar o tamanho dos quadrados&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você pode ajustar o tamanho dos quadrados de acordo com suas necessidades:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1.5em;height:1.5em;background:#7952b3;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] Grand carré
* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:0.8em;height:0.8em;background:#7952b3;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] Petit carré&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O uso da unidade`em`garante que os quadrados se adaptem ao tamanho da fonte.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_criar_variantes&amp;quot;&amp;gt;Criar variantes&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para necessidades específicas, você pode criar diferentes formas:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_círculo_colorido&amp;quot;&amp;gt;Círculo colorido&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#7952b3;border-radius:50%;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] Cercle violet&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_quadrado_com_borda_colorida&amp;quot;&amp;gt;Quadrado com borda colorida&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#ffffff;border:3px solid #7952b3;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] Bordure violette&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_arquitetura_da_solução&amp;quot;&amp;gt;Arquitetura da solução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;architecture-diagram.svg&amp;quot; alt=&amp;quot;architecture diagram&amp;quot; width=&amp;quot;1761&amp;quot; height=&amp;quot;5432&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_vantagens_e_limitações&amp;quot;&amp;gt;Vantagens e limitações&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_vantagens&amp;quot;&amp;gt;Vantagens&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;✓ &amp;lt;strong&amp;gt;Autonomia&amp;lt;/strong&amp;gt; : Não há dependência de arquivos externos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�✓ &amp;lt;strong&amp;gt;Portabilidade&amp;lt;/strong&amp;gt; : Funciona com todos os processadores AsciiDoc&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;✓ &amp;lt;strong&amp;gt;Sincronização&amp;lt;/strong&amp;gt; : O código de cores está diretamente no HTML&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;✓ &amp;lt;strong&amp;gt;Flexibilidade&amp;lt;/strong&amp;gt; : Personalização completa do estilo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;✓ &amp;lt;strong&amp;gt;Manutenção&amp;lt;/strong&amp;gt; : Modificação simples do código hexadecimal&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;✓ &amp;lt;strong&amp;gt;Compatibilidade JBake&amp;lt;/strong&amp;gt; : Funciona perfeitamente com os geradores de sites estáticos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_limitações&amp;quot;&amp;gt;Limitações&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;✗ &amp;lt;strong&amp;gt;Verbosité&amp;lt;/strong&amp;gt; : Código HTML repetitivo no código-fonte AsciiDoc&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;�✗ &amp;lt;strong&amp;gt;Legibilidade da fonte&amp;lt;/strong&amp;gt; : O documento fonte é menos limpo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;✗ &amp;lt;strong&amp;gt;Acessibilidade&amp;lt;/strong&amp;gt; : Sem texto alternativo nativo (a ser adicionado manualmente)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_melhoria_da_acessibilidade&amp;quot;&amp;gt;Melhoria da acessibilidade&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para tornar os quadrados acessíveis aos leitores de tela:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;* pass:[&amp;amp;lt;span role=&amp;quot;img&amp;quot; aria-label=&amp;quot;Couleur violet Bootstrap&amp;quot;
style=&amp;quot;display:inline-block;width:1em;height:1em;background:#7952b3;
vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;]
--accent-color: `#7952b3` (violet Bootstrap)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os atributos`role=&amp;quot;img&amp;quot;` et `aria-label`Permitem que as tecnologias assistivas compreendam e descrevam o conteúdo visual.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_exemplo_concreto_documentação_de_temas&amp;quot;&amp;gt;Exemplo concreto: Documentação de temas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está um exemplo completo documentando vários temas:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;= Guide des couleurs de l&amp;#39;application

== Thème Clair (`:root`)

* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#f8f9fa;border:1px solid #ccc;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --bg-primary: `#f8f9fa` (blanc cassé)
* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#212529;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --text-primary: `#212529` (noir très foncé)
* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#7952b3;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --accent-color: `#7952b3` (violet Bootstrap)

== Thème Sombre (`[data-bs-theme=&amp;quot;dark&amp;quot;]`)

* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#212529;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --bg-primary: `#212529` (noir très foncé)
* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#ffffff;border:1px solid #999;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --text-primary: `#ffffff` (blanc)
* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#0d6efd;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --accent-color: `#0d6efd` (bleu Bootstrap)

== Thème Contraste Élevé (`[data-bs-theme=&amp;quot;high-contrast&amp;quot;]`)

* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#000000;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --bg-primary: `#000000` (noir)
* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#ffffff;border:1px solid #000;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --text-primary: `#ffffff` (blanc)
* pass:[&amp;amp;lt;span style=&amp;quot;display:inline-block;width:1em;height:1em;background:#00ff00;vertical-align:middle;margin-right:0.5em&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;] --accent-color: `#00ff00` (vert vif)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A injeção HTML via a macro``O AsciiDoc oferece uma solução elegante e pragmática para exibir amostras de cor na documentação técnica. Embora seja ligeiramente verbosa, essa abordagem garante uma portabilidade máxima e uma manutenção simplificada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta técnica não requer nenhuma configuração externa, nenhuma folha de estilo adicional, e funciona imediatamente com JBake e todos os processadores AsciiDoc padrões.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Depois de você ter criado seu primeiro quadrado colorido, basta copiar e colar o código HTML e alterar o código hexadecimal para criar rapidamente uma paleta completa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta técnica pode ser estendida a outros casos de uso que necessitam de renderização visual personalizada: ícones, badges, gráficos simples, ou todo elemento visual que necessita de um controle preciso do estilo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_recursos&amp;quot;&amp;gt;Recursos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.asciidoctor.org/asciidoc/latest/pass/pass-macro/&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener&amp;quot;&amp;gt;Documentação AsciiDoc : pass macro&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://jbake.org/docs/2.6.7/&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener&amp;quot;&amp;gt;JBake Documentação&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://developer.mozilla.org/fr/docs/Web/CSS/color_value&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener&amp;quot;&amp;gt;MDN : Valores de cor CSS&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Artigo publicado em 2025-11-01&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Internacionalização de um site estático JBake com Thymeleaf</title>
            <link >https://cheroliv.com//blog/2025/0095_i18n-site-statique-jbake-thymeleaf_post.html</link>
            <pubDate>Mon, 20 Oct 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0095_i18n-site-statique-jbake-thymeleaf_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table of Contents&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_caso_de_uso_use_case&amp;quot;&amp;gt;Diagrama de caso de uso (Use Case)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_da_internacionalização&amp;quot;&amp;gt;Arquitetura da internacionalização&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_estrutura_organização_dos_arquivos&amp;quot;&amp;gt;Diagrama de estrutura (Organização dos arquivos)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_essa_abordagem&amp;quot;&amp;gt;Por que essa abordagem?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_componentes&amp;quot;&amp;gt;Diagrama de componentes&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_i18n_do_templating_com_thymeleaf&amp;quot;&amp;gt;I18n do templating com Thymeleaf&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estrutura_dos_arquivos_de_mensagens&amp;quot;&amp;gt;Estrutura dos arquivos de mensagens&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conteúdo_dos_arquivos_de_mensagens&amp;quot;&amp;gt;Conteúdo dos arquivos de mensagens&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_utilização_nos_templates&amp;quot;&amp;gt;Utilização nos templates&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_sequência_resolução_i18n&amp;quot;&amp;gt;Diagrama de sequência - Resolução i18n&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configuração_da_localidade_no_jbake&amp;quot;&amp;gt;Configuração da localidade no JBake&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_i18n_dos_artigos_organização_por_pastas&amp;quot;&amp;gt;I18n dos artigos: organização por pastas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_fluxo_fluxo_geração&amp;quot;&amp;gt;Diagrama de fluxo (Fluxo) - Geração&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estrutura_das_pastas&amp;quot;&amp;gt;Estrutura das pastas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_vantagens_desta_abordagem&amp;quot;&amp;gt;Vantagens desta abordagem&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_metadados_dos_artigos&amp;quot;&amp;gt;Metadados dos artigos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configuração_das_urls&amp;quot;&amp;gt;Configuração das URLs&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_modelos_para_a_exibição_multilíngue&amp;quot;&amp;gt;Modelos para a exibição multilíngue&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_template_de_artigo_com_seletor_de_idioma&amp;quot;&amp;gt;Template de artigo com seletor de idioma&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_índice_filtrado_por_idioma&amp;quot;&amp;gt;Índice filtrado por idioma&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_navegação_entre_idiomas&amp;quot;&amp;gt;Navegação entre idiomas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_seletor_de_idioma_global&amp;quot;&amp;gt;Seletor de idioma global&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_fluxo_leitura_do_usuário&amp;quot;&amp;gt;Diagrama de fluxo - Leitura do usuário&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estilo_css_para_o_seletor&amp;quot;&amp;gt;Estilo CSS para o seletor&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_feed_rss_por_idioma&amp;quot;&amp;gt;Feed RSS por idioma&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_boas_práticas_e_dicas&amp;quot;&amp;gt;Boas práticas e dicas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_1_coerência_dos_identificadores_de_artigo&amp;quot;&amp;gt;1. Coerência dos identificadores de artigo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_2_datas_coerentes&amp;quot;&amp;gt;2. Datas coerentes&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_3_tags_multilíngues&amp;quot;&amp;gt;3. Tags multilíngues&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_4_gerenciamento_dos_artigos_não_traduzidos&amp;quot;&amp;gt;4. Gerenciamento dos artigos não traduzidos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_5_mapa_do_site_multilíngue&amp;quot;&amp;gt;5. Mapa do site multilíngue&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_implantação&amp;quot;&amp;gt;Diagrama de implantação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A internacionalização (i18n) de um site estático pode parecer complexa à primeira vista, mas o JBake combinado com o Thymeleaf oferece soluções elegantes para criar um site multilíngue. Neste artigo, vou mostrar como implementei o i18n no meu blog, cobrindo tanto o templating quanto a gestão dos artigos em vários idiomas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_de_caso_de_uso_use_case&amp;quot;&amp;gt;Diagrama de caso de uso (Use Case)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-89e91fc926dae6c5891c8e4a1e4301d9.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;643&amp;quot; height=&amp;quot;452&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_arquitetura_da_internacionalização&amp;quot;&amp;gt;Arquitetura da internacionalização&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nossa abordagem baseia-se em dois pilares :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O i18n do templating&amp;lt;/strong&amp;gt;: utilização dos arquivos de mensagens Thymeleaf para os elementos da interface&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O i18n do conteúdo&amp;lt;/strong&amp;gt;: organização dos artigos por língua em uma estrutura de pastas dedicada&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_de_estrutura_organização_dos_arquivos&amp;quot;&amp;gt;Diagrama de estrutura (Organização dos arquivos)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-2f100fc3ec2ec37f9556818cdc246602.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;188&amp;quot; height=&amp;quot;513&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_por_que_essa_abordagem&amp;quot;&amp;gt;Por que essa abordagem?&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta separação permite:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Manter a consistência na interface, independentemente do idioma.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Gerir independentemente o conteúdo e as traduções de artigos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Facilitar a adição de novas línguas sem refatoração maior&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Permitir artigos disponíveis apenas em certas línguas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_de_componentes&amp;quot;&amp;gt;Diagrama de componentes&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-c4f46c86d4d34e9b58b816b38348f050.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;1771&amp;quot; height=&amp;quot;477&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_i18n_do_templating_com_thymeleaf&amp;quot;&amp;gt;I18n do templating com Thymeleaf&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estrutura_dos_arquivos_de_mensagens&amp;quot;&amp;gt;Estrutura dos arquivos de mensagens&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O primeiro passo consiste em criar os arquivos de propriedades para cada idioma suportado :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;src/jbake/templates/
├── messages.properties        # Fallback par défaut
├── messages_fr.properties     # Français
├── messages_en.properties     # Anglais
└── messages_de.properties     # Allemand&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_conteúdo_dos_arquivos_de_mensagens&amp;quot;&amp;gt;Conteúdo dos arquivos de mensagens&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está um exemplo de arquivo`messages_fr.properties`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-properties hljs&amp;quot; data-lang=&amp;quot;properties&amp;quot;&amp;gt;# Navigation
nav.home=Accueil
nav.blog=Blog
nav.about=À propos
nav.contact=Contact

# Articles
article.readmore=Lire la suite
article.published=Publié le
article.tags=Étiquettes
article.also.available=Également disponible en

# Interface
site.title=Mon Blog Technique
site.description=Partage de connaissances et expériences
footer.copyright=© 2025 Tous droits réservés
search.placeholder=Rechercher un article...&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E seu equivalente em inglês`messages_en.properties`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-properties hljs&amp;quot; data-lang=&amp;quot;properties&amp;quot;&amp;gt;# Navigation
nav.home=Home
nav.blog=Blog
nav.about=About
nav.contact=Contact

# Articles
article.readmore=Read more
article.published=Published on
article.tags=Tags
article.also.available=Also available in

# Interface
site.title=My Tech Blog
site.description=Sharing knowledge and experiences
footer.copyright=© 2025 All rights reserved
search.placeholder=Search articles...&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_utilização_nos_templates&amp;quot;&amp;gt;Utilização nos templates&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nos seus templates Thymeleaf, utilize a sintaxe`#{}`para acessar as mensagens :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html hljs&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html xmlns:th=&amp;quot;http://www.thymeleaf.org&amp;quot;&amp;amp;gt;
&amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;title th:text=&amp;quot;#{site.title}&amp;quot;&amp;amp;gt;Mon Blog&amp;amp;lt;/title&amp;amp;gt;
    &amp;amp;lt;meta name=&amp;quot;description&amp;quot; th:content=&amp;quot;#{site.description}&amp;quot; /&amp;amp;gt;
&amp;amp;lt;/head&amp;amp;gt;
&amp;amp;lt;body&amp;amp;gt;
    &amp;amp;lt;nav&amp;amp;gt;
        &amp;amp;lt;a th:href=&amp;quot;@{/}&amp;quot; th:text=&amp;quot;#{nav.home}&amp;quot;&amp;amp;gt;Accueil&amp;amp;lt;/a&amp;amp;gt;
        &amp;amp;lt;a th:href=&amp;quot;@{/blog/}&amp;quot; th:text=&amp;quot;#{nav.blog}&amp;quot;&amp;amp;gt;Blog&amp;amp;lt;/a&amp;amp;gt;
        &amp;amp;lt;a th:href=&amp;quot;@{/about.html}&amp;quot; th:text=&amp;quot;#{nav.about}&amp;quot;&amp;amp;gt;À propos&amp;amp;lt;/a&amp;amp;gt;
    &amp;amp;lt;/nav&amp;amp;gt;

    &amp;amp;lt;footer&amp;amp;gt;
        &amp;amp;lt;p th:text=&amp;quot;#{footer.copyright}&amp;quot;&amp;amp;gt;Copyright&amp;amp;lt;/p&amp;amp;gt;
    &amp;amp;lt;/footer&amp;amp;gt;
&amp;amp;lt;/body&amp;amp;gt;
&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_de_sequência_resolução_i18n&amp;quot;&amp;gt;Diagrama de sequência - Resolução i18n&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 5) ]

@startuml
actor Auteur
participant JBake
participant &amp;quot;AsciiDoc\nAnalisador&amp;quot; as parser
participant &amp;quot;Thymeleaf
^^^^^
 Syntax Error? (Assumed diagram type: sequence)

@startuml
actor Auteur
participant JBake
participant &amp;quot;AsciiDoc\nAnalisador&amp;quot; as parser
participant &amp;quot;Thymeleaf
Motor&amp;quot; as thymeleaf
participant &amp;quot;I18n\nResolver&amp;quot; as i18n
database &amp;quot;messages_*.properties&amp;quot; as messages

Auteur -&amp;amp;gt; JBake: jbake -b
activate JBake

JBake -&amp;amp;gt; parser: Lire article.fr.adoc
activate parser
parser --&amp;amp;gt; JBake: Contenu + métadonnées\n(lang=fr, article-id=xyz)
deactivate parser

JBake -&amp;amp;gt; parser: Lire article.en.adoc
activate parser
parser --&amp;amp;gt; JBake: Contenu + métadonnées\n(lang=en, article-id=xyz)
deactivate parser

JBake -&amp;amp;gt; thymeleaf: Générer page FR
activate thymeleaf

thymeleaf -&amp;amp;gt; i18n: Résoudre #{nav.home}
activate i18n
i18n -&amp;amp;gt; messages: Lire messages_fr.properties
messages --&amp;amp;gt; i18n: &amp;quot;Início&amp;quot;
i18n --&amp;amp;gt; thymeleaf: &amp;quot;Início&amp;quot;
deactivate i18n

thymeleaf -&amp;amp;gt; JBake: Rechercher traductions\n(article-id=xyz, lang!=fr)
JBake --&amp;amp;gt; thymeleaf: article.en.html trouvé

thymeleaf --&amp;amp;gt; JBake: /fr/blog/article.html
deactivate thymeleaf

JBake -&amp;amp;gt; thymeleaf: Générer page EN
activate thymeleaf

thymeleaf -&amp;amp;gt; i18n: Résoudre #{nav.home}
activate i18n
i18n -&amp;amp;gt; messages: Lire messages_en.properties
messages --&amp;amp;gt; i18n: &amp;quot;Início&amp;quot;
i18n --&amp;amp;gt; thymeleaf: &amp;quot;Início&amp;quot;
deactivate i18n

thymeleaf -&amp;amp;gt; JBake: Rechercher traductions\n(article-id=xyz, lang!=en)
JBake --&amp;amp;gt; thymeleaf: article.fr.html trouvé

thymeleaf --&amp;amp;gt; JBake: /en/blog/article.html
deactivate thymeleaf

JBake --&amp;amp;gt; Auteur: Site généré
deactivate JBake
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_configuração_da_localidade_no_jbake&amp;quot;&amp;gt;Configuração da localidade no JBake&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No seu arquivo`jbake.properties`, defina a localidade padrão :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-properties hljs&amp;quot; data-lang=&amp;quot;properties&amp;quot;&amp;gt;# Locale par défaut
thymeleaf.locale=fr

# Encodage
template.encoding=UTF-8&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_i18n_dos_artigos_organização_por_pastas&amp;quot;&amp;gt;I18n dos artigos: organização por pastas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_de_fluxo_fluxo_geração&amp;quot;&amp;gt;Diagrama de fluxo (Fluxo) - Geração&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-c91cb37b7790833828a90d2610465f57.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;285&amp;quot; height=&amp;quot;1034&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estrutura_das_pastas&amp;quot;&amp;gt;Estrutura das pastas&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em vez de usar sufixos nos nomes de arquivos, optei por uma organização por pastas que oferece mais clareza e manutenibilidade :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;content/blog/
├── 2024/
│   ├── fr/
│   │   ├── introduction-jbake.adoc
│   │   ├── guide-thymeleaf.adoc
│   │   └── astuces-asciidoc.adoc
│   └── en/
│       ├── introduction-jbake.adoc
│       ├── thymeleaf-guide.adoc
│       └── asciidoc-tips.adoc
└── 2025/
    ├── fr/
    │   └── internationalisation-jbake.adoc
    └── en/
        └── jbake-internationalization.adoc&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_vantagens_desta_abordagem&amp;quot;&amp;gt;Vantagens desta abordagem&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta estrutura apresenta várias vantagens:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Separação clara&amp;lt;/strong&amp;gt;: cada língua tem seu próprio espaço&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Nomenclatura flexível&amp;lt;/strong&amp;gt;: os arquivos podem ter nomes diferentes dependendo do idioma&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Escalabilidade&amp;lt;/strong&amp;gt;fácil de adicionar um novo idioma&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Organização natural&amp;lt;/strong&amp;gt;segue a lógica temporal de JBake&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_metadados_dos_artigos&amp;quot;&amp;gt;Metadados dos artigos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cada artigo deve conter metadados para permitir a ligação entre traduções. Aqui está um exemplo:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Versão francesa&amp;lt;/strong&amp;gt;(&amp;lt;code&amp;gt;2025/fr/internationalisation-jbake.adoc&amp;lt;/code&amp;gt;) :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;= Internationalisation d&amp;#39;un site statique JBake
:jbake-type: post
:jbake-status: published
:jbake-date: 2025-10-20
:jbake-lang: fr
:jbake-article-id: jbake-i18n-thymeleaf
:jbake-tags: jbake, thymeleaf, i18n
:jbake-description: Guide pour mettre en place l&amp;#39;i18n avec JBake&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Versão inglesa&amp;lt;/strong&amp;gt;(&amp;lt;code&amp;gt;2025/en/jbake-internationalization.adoc&amp;lt;/code&amp;gt;) :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;= Internationalizing a JBake Static Site
:jbake-type: post
:jbake-status: published
:jbake-date: 2025-10-20
:jbake-lang: en
:jbake-article-id: jbake-i18n-thymeleaf
:jbake-tags: jbake, thymeleaf, i18n
:jbake-description: Guide to implement i18n with JBake&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;NOTA: O atributo`:jbake-article-id:`é crucial: ele permite ligar as diferentes traduções do mesmo artigo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_configuração_das_urls&amp;quot;&amp;gt;Configuração das URLs&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Dentro`jbake.properties`, configure o padrão de URL para incluir o idioma :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-properties hljs&amp;quot; data-lang=&amp;quot;properties&amp;quot;&amp;gt;# Pattern d&amp;#39;URL avec langue
post.permalink.pattern=:lang/blog/:year/:name.html

# Langue par défaut
site.default.lang=fr&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Isso gerará URLs do tipo:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;/fr/blog/2025/internationalisation-jbake.html&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;/en/blog/2025/jbake-internationalization.html&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_modelos_para_a_exibição_multilíngue&amp;quot;&amp;gt;Modelos para a exibição multilíngue&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_template_de_artigo_com_seletor_de_idioma&amp;quot;&amp;gt;Template de artigo com seletor de idioma&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Crie um modelo`post.html`exibe as traduções disponíveis :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html hljs&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html xmlns:th=&amp;quot;http://www.thymeleaf.org&amp;quot;&amp;amp;gt;
&amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;title th:text=&amp;quot;${content.title}&amp;quot;&amp;amp;gt;Article&amp;amp;lt;/title&amp;amp;gt;
&amp;amp;lt;/head&amp;amp;gt;
&amp;amp;lt;body&amp;amp;gt;
    &amp;amp;lt;article&amp;amp;gt;
        &amp;amp;lt;header&amp;amp;gt;
            &amp;amp;lt;h1 th:text=&amp;quot;${content.title}&amp;quot;&amp;amp;gt;Titre&amp;amp;lt;/h1&amp;amp;gt;

            &amp;amp;lt;div class=&amp;quot;article-meta&amp;quot;&amp;amp;gt;
                &amp;amp;lt;time th:text=&amp;quot;${#dates.format(content.date, &amp;#39;dd MMMM yyyy&amp;#39;)}&amp;quot;
                      th:attr=&amp;quot;datetime=${#dates.format(content.date, &amp;#39;yyyy-MM-dd&amp;#39;)}&amp;quot;&amp;amp;gt;
                    Date
                &amp;amp;lt;/time&amp;amp;gt;

                &amp;amp;lt;!-- Sélecteur de traductions --&amp;amp;gt;
                &amp;amp;lt;div class=&amp;quot;translations&amp;quot; th:if=&amp;quot;${content[&amp;#39;article-id&amp;#39;]}&amp;quot;&amp;amp;gt;
                    &amp;amp;lt;span th:text=&amp;quot;#{article.also.available}&amp;quot;&amp;amp;gt;Aussi disponible en :&amp;amp;lt;/span&amp;amp;gt;
                    &amp;amp;lt;ul class=&amp;quot;language-list&amp;quot;&amp;amp;gt;
                        &amp;amp;lt;li th:each=&amp;quot;post : ${published_posts}&amp;quot;
                            th:if=&amp;quot;${post[&amp;#39;article-id&amp;#39;] == content[&amp;#39;article-id&amp;#39;] and post.lang != content.lang}&amp;quot;&amp;amp;gt;
                            &amp;amp;lt;a th:href=&amp;quot;${post.uri}&amp;quot;
                               th:text=&amp;quot;${post.lang.toUpperCase()}&amp;quot;&amp;amp;gt;
                                LANG
                            &amp;amp;lt;/a&amp;amp;gt;
                        &amp;amp;lt;/li&amp;amp;gt;
                    &amp;amp;lt;/ul&amp;amp;gt;
                &amp;amp;lt;/div&amp;amp;gt;
            &amp;amp;lt;/div&amp;amp;gt;
        &amp;amp;lt;/header&amp;amp;gt;

        &amp;amp;lt;div class=&amp;quot;content&amp;quot; th:utext=&amp;quot;${content.body}&amp;quot;&amp;amp;gt;
            Contenu de l&amp;#39;article
        &amp;amp;lt;/div&amp;amp;gt;

        &amp;amp;lt;footer class=&amp;quot;article-footer&amp;quot;&amp;amp;gt;
            &amp;amp;lt;div class=&amp;quot;tags&amp;quot; th:if=&amp;quot;${content.tags}&amp;quot;&amp;amp;gt;
                &amp;amp;lt;span th:text=&amp;quot;#{article.tags}&amp;quot;&amp;amp;gt;Étiquettes :&amp;amp;lt;/span&amp;amp;gt;
                &amp;amp;lt;span th:each=&amp;quot;tag : ${content.tags}&amp;quot;&amp;amp;gt;
                    &amp;amp;lt;a th:href=&amp;quot;@{/tags/{tag}.html(tag=${tag})}&amp;quot;
                       th:text=&amp;quot;${tag}&amp;quot;&amp;amp;gt;tag&amp;amp;lt;/a&amp;amp;gt;
                &amp;amp;lt;/span&amp;amp;gt;
            &amp;amp;lt;/div&amp;amp;gt;
        &amp;amp;lt;/footer&amp;amp;gt;
    &amp;amp;lt;/article&amp;amp;gt;
&amp;amp;lt;/body&amp;amp;gt;
&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_índice_filtrado_por_idioma&amp;quot;&amp;gt;Índice filtrado por idioma&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Crie modelos de índice para cada idioma :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;index.html&amp;lt;/strong&amp;gt;(índice français) :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html hljs&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html xmlns:th=&amp;quot;http://www.thymeleaf.org&amp;quot;&amp;amp;gt;
&amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;title th:text=&amp;quot;#{site.title}&amp;quot;&amp;amp;gt;Mon Blog&amp;amp;lt;/title&amp;amp;gt;
&amp;amp;lt;/head&amp;amp;gt;
&amp;amp;lt;body&amp;amp;gt;
    &amp;amp;lt;main&amp;amp;gt;
        &amp;amp;lt;h1 th:text=&amp;quot;#{nav.blog}&amp;quot;&amp;amp;gt;Blog&amp;amp;lt;/h1&amp;amp;gt;

        &amp;amp;lt;div class=&amp;quot;articles-list&amp;quot;&amp;amp;gt;
            &amp;amp;lt;article th:each=&amp;quot;post : ${published_posts}&amp;quot;
                     th:if=&amp;quot;${post.lang == &amp;#39;fr&amp;#39;}&amp;quot;&amp;amp;gt;
                &amp;amp;lt;h2&amp;amp;gt;
                    &amp;amp;lt;a th:href=&amp;quot;${post.uri}&amp;quot; th:text=&amp;quot;${post.title}&amp;quot;&amp;amp;gt;Titre&amp;amp;lt;/a&amp;amp;gt;
                &amp;amp;lt;/h2&amp;amp;gt;
                &amp;amp;lt;time th:text=&amp;quot;${#dates.format(post.date, &amp;#39;dd MMMM yyyy&amp;#39;)}&amp;quot;&amp;amp;gt;
                    Date
                &amp;amp;lt;/time&amp;amp;gt;
                &amp;amp;lt;p th:text=&amp;quot;${post.description}&amp;quot;&amp;amp;gt;Description&amp;amp;lt;/p&amp;amp;gt;
                &amp;amp;lt;a th:href=&amp;quot;${post.uri}&amp;quot; th:text=&amp;quot;#{article.readmore}&amp;quot;&amp;amp;gt;
                    Lire la suite
                &amp;amp;lt;/a&amp;amp;gt;
            &amp;amp;lt;/article&amp;amp;gt;
        &amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;/main&amp;amp;gt;
&amp;amp;lt;/body&amp;amp;gt;
&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;index_en.html&amp;lt;/strong&amp;gt;(índice anglais) :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html hljs&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html xmlns:th=&amp;quot;http://www.thymeleaf.org&amp;quot;&amp;amp;gt;
&amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;title th:text=&amp;quot;#{site.title}&amp;quot;&amp;amp;gt;My Blog&amp;amp;lt;/title&amp;amp;gt;
&amp;amp;lt;/head&amp;amp;gt;
&amp;amp;lt;body&amp;amp;gt;
    &amp;amp;lt;main&amp;amp;gt;
        &amp;amp;lt;h1 th:text=&amp;quot;#{nav.blog}&amp;quot;&amp;amp;gt;Blog&amp;amp;lt;/h1&amp;amp;gt;

        &amp;amp;lt;div class=&amp;quot;articles-list&amp;quot;&amp;amp;gt;
            &amp;amp;lt;article th:each=&amp;quot;post : ${published_posts}&amp;quot;
                     th:if=&amp;quot;${post.lang == &amp;#39;en&amp;#39;}&amp;quot;&amp;amp;gt;
                &amp;amp;lt;h2&amp;amp;gt;
                    &amp;amp;lt;a th:href=&amp;quot;${post.uri}&amp;quot; th:text=&amp;quot;${post.title}&amp;quot;&amp;amp;gt;Title&amp;amp;lt;/a&amp;amp;gt;
                &amp;amp;lt;/h2&amp;amp;gt;
                &amp;amp;lt;time th:text=&amp;quot;${#dates.format(post.date, &amp;#39;dd MMMM yyyy&amp;#39;)}&amp;quot;&amp;amp;gt;
                    Date
                &amp;amp;lt;/time&amp;amp;gt;
                &amp;amp;lt;p th:text=&amp;quot;${post.description}&amp;quot;&amp;amp;gt;Description&amp;amp;lt;/p&amp;amp;gt;
                &amp;amp;lt;a th:href=&amp;quot;${post.uri}&amp;quot; th:text=&amp;quot;#{article.readmore}&amp;quot;&amp;amp;gt;
                    Read more
                &amp;amp;lt;/a&amp;amp;gt;
            &amp;amp;lt;/article&amp;amp;gt;
        &amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;/main&amp;amp;gt;
&amp;amp;lt;/body&amp;amp;gt;
&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_navegação_entre_idiomas&amp;quot;&amp;gt;Navegação entre idiomas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_seletor_de_idioma_global&amp;quot;&amp;gt;Seletor de idioma global&amp;lt;/h3&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_de_fluxo_leitura_do_usuário&amp;quot;&amp;gt;Diagrama de fluxo - Leitura do usuário&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-35c457cfbd1b9be0156f6bbbfb2925d1.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;494&amp;quot; height=&amp;quot;761&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Adicione um seletor de idioma no seu template principal :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html hljs&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;nav class=&amp;quot;language-switcher&amp;quot;&amp;amp;gt;
    &amp;amp;lt;a href=&amp;quot;/index.html&amp;quot;
       th:classappend=&amp;quot;${content.lang == &amp;#39;fr&amp;#39;} ? &amp;#39;active&amp;#39;&amp;quot;
       title=&amp;quot;Français&amp;quot;&amp;amp;gt;
        🇫🇷 FR
    &amp;amp;lt;/a&amp;amp;gt;
    &amp;amp;lt;a href=&amp;quot;/en/index.html&amp;quot;
       th:classappend=&amp;quot;${content.lang == &amp;#39;en&amp;#39;} ? &amp;#39;active&amp;#39;&amp;quot;
       title=&amp;quot;English&amp;quot;&amp;amp;gt;
        🇬🇧 EN
    &amp;amp;lt;/a&amp;amp;gt;
&amp;amp;lt;/nav&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estilo_css_para_o_seletor&amp;quot;&amp;gt;Estilo CSS para o seletor&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css hljs&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;.language-switcher {
    display: flex;
    gap: 1rem;
    padding: 0.5rem;
    background: #f5f5f5;
    border-radius: 4px;
}

.language-switcher a {
    padding: 0.5rem 1rem;
    text-decoration: none;
    color: #333;
    border-radius: 4px;
    transition: background 0.2s;
}

.language-switcher a:hover {
    background: #e0e0e0;
}

.language-switcher a.active {
    background: #007bff;
    color: white;
}

.translations {
    margin: 1rem 0;
    padding: 1rem;
    background: #f8f9fa;
    border-left: 4px solid #007bff;
}

.language-list {
    display: inline-flex;
    gap: 0.5rem;
    list-style: none;
    padding: 0;
    margin: 0;
}

.language-list li::after {
    content: &amp;quot;•&amp;quot;;
    margin-left: 0.5rem;
}

.language-list li:last-child::after {
    content: &amp;quot;&amp;quot;;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_feed_rss_por_idioma&amp;quot;&amp;gt;Feed RSS por idioma&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para ter feeds RSS separados por idioma, crie modelos distintos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;feed.xml&amp;lt;/strong&amp;gt;(fluxo francês) :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-xml hljs&amp;quot; data-lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;amp;gt;
&amp;amp;lt;rss version=&amp;quot;2.0&amp;quot; xmlns:th=&amp;quot;http://www.thymeleaf.org&amp;quot;&amp;amp;gt;
    &amp;amp;lt;channel&amp;amp;gt;
        &amp;amp;lt;title th:text=&amp;quot;#{site.title}&amp;quot;&amp;amp;gt;Mon Blog&amp;amp;lt;/title&amp;amp;gt;
        &amp;amp;lt;link th:text=&amp;quot;${config.site_host}&amp;quot;&amp;amp;gt;http://example.com&amp;amp;lt;/link&amp;amp;gt;
        &amp;amp;lt;description th:text=&amp;quot;#{site.description}&amp;quot;&amp;amp;gt;Description&amp;amp;lt;/description&amp;amp;gt;
        &amp;amp;lt;language&amp;amp;gt;fr&amp;amp;lt;/language&amp;amp;gt;

        &amp;amp;lt;item th:each=&amp;quot;post : ${published_posts}&amp;quot;
              th:if=&amp;quot;${post.lang == &amp;#39;fr&amp;#39;}&amp;quot;&amp;amp;gt;
            &amp;amp;lt;title th:text=&amp;quot;${post.title}&amp;quot;&amp;amp;gt;Titre&amp;amp;lt;/title&amp;amp;gt;
            &amp;amp;lt;link th:text=&amp;quot;${config.site_host + post.uri}&amp;quot;&amp;amp;gt;Lien&amp;amp;lt;/link&amp;amp;gt;
            &amp;amp;lt;pubDate th:text=&amp;quot;${#dates.format(post.date, &amp;#39;EEE, dd MMM yyyy HH:mm:ss Z&amp;#39;)}&amp;quot;&amp;amp;gt;
                Date
            &amp;amp;lt;/pubDate&amp;amp;gt;
            &amp;amp;lt;description th:text=&amp;quot;${post.description}&amp;quot;&amp;amp;gt;Description&amp;amp;lt;/description&amp;amp;gt;
        &amp;amp;lt;/item&amp;amp;gt;
    &amp;amp;lt;/channel&amp;amp;gt;
&amp;amp;lt;/rss&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_boas_práticas_e_dicas&amp;quot;&amp;gt;Boas práticas e dicas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_1_coerência_dos_identificadores_de_artigo&amp;quot;&amp;gt;1. Coerência dos identificadores de artigo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Certifique-se de que`:jbake-article-id:`é idêntico para todas as traduções de um mesmo artigo. Utilize um formato consistente:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Prefira os identificadores em inglês para universalidade.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Use hífens para separar as palavras&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Evite os caracteres especiais&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_2_datas_coerentes&amp;quot;&amp;gt;2. Datas coerentes&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Todas as traduções de um artigo devem ter a mesma data de publicação (&amp;lt;code&amp;gt;:jbake-date:&amp;lt;/code&amp;gt;) Isso facilita a classificação e a exibição cronológica.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_3_tags_multilíngues&amp;quot;&amp;gt;3. Tags multilíngues&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para as tags, você tem duas opções:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Opção 1: Tags universais em inglês&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;:jbake-tags: java, spring-boot, microservices&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Opção 2 : Tags traduzidas com mapeamento&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;# Version française
:jbake-tags: java, spring-boot, microservices

# Version anglaise
:jbake-tags: java, spring-boot, microservices&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_4_gerenciamento_dos_artigos_não_traduzidos&amp;quot;&amp;gt;4. Gerenciamento dos artigos não traduzidos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não é obrigatório traduzir todos os artigos. Se um artigo existir apenas em uma língua, ele simplesmente não aparecerá nas listas da outra língua.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_5_mapa_do_site_multilíngue&amp;quot;&amp;gt;5. Mapa do site multilíngue&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Gere um sitemap que inclua todas as línguas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-xml hljs&amp;quot; data-lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;amp;gt;
&amp;amp;lt;urlset xmlns=&amp;quot;http://www.sitemaps.org/schemas/sitemap/0.9&amp;quot;
        xmlns:xhtml=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;
        xmlns:th=&amp;quot;http://www.thymeleaf.org&amp;quot;&amp;amp;gt;
    &amp;amp;lt;url th:each=&amp;quot;post : ${published_posts}&amp;quot;&amp;amp;gt;
        &amp;amp;lt;loc th:text=&amp;quot;${config.site_host + post.uri}&amp;quot;&amp;amp;gt;URL&amp;amp;lt;/loc&amp;amp;gt;
        &amp;amp;lt;lastmod th:text=&amp;quot;${#dates.format(post.date, &amp;#39;yyyy-MM-dd&amp;#39;)}&amp;quot;&amp;amp;gt;Date&amp;amp;lt;/lastmod&amp;amp;gt;

        &amp;amp;lt;!-- Liens alternatifs pour les traductions --&amp;amp;gt;
        &amp;amp;lt;xhtml:link th:each=&amp;quot;translation : ${published_posts}&amp;quot;
                    th:if=&amp;quot;${translation[&amp;#39;article-id&amp;#39;] == post[&amp;#39;article-id&amp;#39;] and translation.lang != post.lang}&amp;quot;
                    rel=&amp;quot;alternate&amp;quot;
                    th:attr=&amp;quot;hreflang=${translation.lang},href=${config.site_host + translation.uri}&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;/url&amp;amp;gt;
&amp;amp;lt;/urlset&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A internacionalização de um site JBake com Thymeleaf é uma abordagem robusta e manutenível. Separando o i18n do templating (via os arquivos de mensagens) e o i18n do conteúdo (via a organização em pastas), você obtém um sistema flexível que pode evoluir facilmente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os pontos-chave a reter :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Arquivos de mensagens Thymeleaf&amp;lt;/strong&amp;gt;para a interface do usuário&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Organização por pastas&amp;lt;/strong&amp;gt;(ano/idioma) para os artigos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Identificadores do artigo&amp;lt;/strong&amp;gt;para vincular as traduções&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Modelos dedicados&amp;lt;/strong&amp;gt;para cada língua&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;URLs explícitas&amp;lt;/strong&amp;gt;incluindo o código de idioma&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_de_implantação&amp;quot;&amp;gt;Diagrama de implantação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;Failed to generate image: PlantUML preprocessing failed: [From &amp;amp;lt;input&amp;amp;gt; (line 14) ]

@startuml
node &amp;quot;Máquina de desenvolvimento&amp;quot; {
  artifact &amp;quot;Fontes&amp;quot; {
    folder &amp;quot;contenu/&amp;quot;
    folder &amp;quot;modelos/&amp;quot;
    folder &amp;quot;ativos/&amp;quot;
  }

  component &amp;quot;JBake CLI&amp;quot; as jbake

  Sources --&amp;amp;gt; jbake : jbake -b
}

node &amp;quot;Servidor de build
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
node &amp;quot;Máquina de desenvolvimento&amp;quot; {
  artifact &amp;quot;Fontes&amp;quot; {
    folder &amp;quot;contenu/&amp;quot;
    folder &amp;quot;modelos/&amp;quot;
    folder &amp;quot;ativos/&amp;quot;
  }

  component &amp;quot;JBake CLI&amp;quot; as jbake

  Sources --&amp;amp;gt; jbake : jbake -b
}

node &amp;quot;Servidor de build
(CI/CD)&amp;quot; {
  component &amp;quot;GitHub Actions
ou GitLab CI&amp;quot; as ci

  jbake --&amp;amp;gt; ci : push
}

cloud &amp;quot;CDN / Hospedagem&amp;quot; {
  node &amp;quot;Servidor Web Estático&amp;quot; {
    artifact &amp;quot;Site gerado&amp;quot; {
      folder &amp;quot;/pt/blog/&amp;quot;
      folder &amp;quot;/en/blog/&amp;quot;
      folder &amp;quot;/assets/&amp;quot;
    }
  }
}

ci --&amp;amp;gt; &amp;quot;Site gerado&amp;quot; : déploiement

actor &amp;quot;Leitores&amp;quot; as users

users --&amp;amp;gt; &amp;quot;Servidor Web Estático&amp;quot; : HTTPS
@enduml&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta arquitetura permite que você comece simplesmente com dois idiomas e adicione outros sem grandes refatorações. Tudo permanece totalmente estático e performante, fiel à filosofia do JBake.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Bom desenvolvimento multilíngue! 🌍&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Instalar e verificar o Tor Browser no Ubuntu</title>
            <link >https://cheroliv.com//blog/2025/0094_tor_browser_install_post.html</link>
            <pubDate>Sun, 5 Oct 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0094_tor_browser_install_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table of Contents&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_download_do_tor_e_da_sua_assinatura&amp;quot;&amp;gt;Download do Tor e da sua assinatura&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_verificação_criptográfica_da_assinatura&amp;quot;&amp;gt;Verificação criptográfica da assinatura&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_importar_a_chave_pública_do_tor_project&amp;quot;&amp;gt;Importar a chave pública do Tor Project&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_2_verificar_a_assinatura&amp;quot;&amp;gt;2. Verificar a assinatura&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_exemplo_prático_assinatura_válida_mas_chave_não_certificada&amp;quot;&amp;gt;Exemplo prático: Assinatura válida mas chave não certificada&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_verificar_a_assinatura&amp;quot;&amp;gt;Por que verificar a assinatura?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_extração_e_lançamento_de_tor_browser&amp;quot;&amp;gt;Extração e lançamento de Tor Browser&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_integração_à_área_de_trabalho_ubuntu&amp;quot;&amp;gt;Integração à área de trabalho Ubuntu&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_explorar_recursos_educativos_com_tor&amp;quot;&amp;gt;Explorar recursos educativos com Tor&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_atualização_do_tor_browser&amp;quot;&amp;gt;Atualização do Tor Browser&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_referências&amp;quot;&amp;gt;Referências&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Num mundo onde a privacidade digital está cada vez mais ameaçada, &amp;lt;strong&amp;gt;Tor Browser&amp;lt;/strong&amp;gt; continua a ser uma ferramenta essencial para preservar o anonimato e a liberdade de informação. No entanto, é crucial nunca executar um software baixado sem ter&amp;lt;strong&amp;gt;verificado sua autenticidade&amp;lt;/strong&amp;gt;. Este artigo apresenta um método completo para:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Baixar &amp;lt;strong&amp;gt;Tor Browser&amp;lt;/strong&amp;gt; e sua assinatura`.asc`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Verificar a validade do arquivo baixado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Instalar e executar &amp;lt;strong&amp;gt;Tor Browser&amp;lt;/strong&amp;gt; no Ubuntu&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Adicioná-lo aos menus da área de trabalho para uso diário&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Explorar recursos educativos e OSINT éticos via Tor&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_download_do_tor_e_da_sua_assinatura&amp;quot;&amp;gt;Download do Tor e da sua assinatura&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Crie uma pasta dedicada para o navegador Tor:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p ~/workspace/tipiak/tor
cd ~/workspace/tipiak/tor&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em seguida, baixe o arquivo do navegador Tor e sua assinatura:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;wget https://www.torproject.org/dist/torbrowser/14.5.7/tor-browser-linux-x86_64-14.5.7.tar.xz
wget https://www.torproject.org/dist/torbrowser/14.5.7/tor-browser-linux-x86_64-14.5.7.tar.xz.asc&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_verificação_criptográfica_da_assinatura&amp;quot;&amp;gt;Verificação criptográfica da assinatura&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta etapa é&amp;lt;strong&amp;gt;indispensável&amp;lt;/strong&amp;gt;para garantir que o arquivo baixado realmente venha do &amp;lt;strong&amp;gt;Tor Project&amp;lt;/strong&amp;gt; e não foi modificado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_importar_a_chave_pública_do_tor_project&amp;quot;&amp;gt;Importar a chave pública do Tor Project&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;gpg --auto-key-locate nodefault,wkd --locate-keys torbrowser@torproject.org&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Verifique que a chave está bem importada :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;gpg --list-keys torbrowser@torproject.org&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_2_verificar_a_assinatura&amp;quot;&amp;gt;2. Verificar a assinatura&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;gpg --verify tor-browser-linux-x86_64-14.5.7.tar.xz.asc tor-browser-linux-x86_64-14.5.7.tar.xz&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se tudo estiver correto, a mensagem seguinte será exibida :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;gpg: Good signature from &amp;quot;Tor Browser Developers (signing key)&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em caso de aviso de chave não certificada, isso significa simplesmente que você ainda não assinou a chave confiável do Projeto Tor, mas a assinatura permanece válida.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_exemplo_prático_assinatura_válida_mas_chave_não_certificada&amp;quot;&amp;gt;Exemplo prático: Assinatura válida mas chave não certificada&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É comum encontrar uma mensagem como esta durante a verificação:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;gpg --verify tor-browser-linux-x86_64-14.5.7.tar.xz.asc tor-browser-linux-x86_64-14.5.7.tar.xz
gpg: Signature faite le mar. 16 sept. 2025 14:26:26 CEST
gpg:                avec la clef RSA CAAE408AEBE2288E96FC5D5E157432CF78A65729
gpg: Bonne signature de « Tor Browser Developers (signing key) &amp;amp;lt;torbrowser@torproject.org&amp;amp;gt; » [inconnu]
gpg: Attention : cette clef n&amp;#39;est pas certifiée avec une signature de confiance.
gpg:          Rien n&amp;#39;indique que la signature appartient à son propriétaire.
Empreinte de clef principale : EF6E 286D DA85 EA2A 4BA7  DE68 4E2C 6E87 9329 8290
     Empreinte de la sous-clef : CAAE 408A EBE2 288E 96FC  5D5E 1574 32CF 78A6 5729&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta mensagem indica que a assinatura é tecnicamente &amp;quot;boa&amp;quot; (o arquivo não foi alterado), mas que seu sistema GPG não pode &amp;lt;strong&amp;gt;certificar a confiança&amp;lt;/strong&amp;gt; na chave em si. Para dissipar esta dúvida, é imperativo comparar a impressão digital da chave principal (&amp;lt;code&amp;gt;EF6E 286D DA85 EA2A 4BA7 DE68 4E2C 6E87 9329 8290&amp;lt;/code&amp;gt;) com a que foi publicada no site oficial do Tor Project.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Segundo o site oficial, a pegada esperada é:`EF6E286DDA85EA2A4BA7DE684E2C6E8793298290`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Como a pegada exibida por`gpg`corresponde &amp;lt;strong&amp;gt;exatamente&amp;lt;/strong&amp;gt; àquela do site oficial, você pode ter certeza de que a chave é legítima e que o arquivo está seguro. O aviso de &amp;quot;chave não certificada&amp;quot; é então uma informação sobre sua configuração GPG local, e não sobre a validade da assinatura ou a autenticidade do arquivo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_por_que_verificar_a_assinatura&amp;quot;&amp;gt;Por que verificar a assinatura?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A verificação das assinaturas GPG é um ato fundamental de segurança digital. Ela permite:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Garantir&amp;lt;strong&amp;gt;a autenticidade&amp;lt;/strong&amp;gt;do software baixado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Impedir a instalação de um arquivo comprometido por um ator malicioso&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Participar à&amp;lt;strong&amp;gt;soberania digital&amp;lt;/strong&amp;gt;individual&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Promover uma cultura digital responsável e ética&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;usecase-tor-verification.svg&amp;quot; alt=&amp;quot;usecase tor verification&amp;quot; width=&amp;quot;408&amp;quot; height=&amp;quot;268&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_extração_e_lançamento_de_tor_browser&amp;quot;&amp;gt;Extração e lançamento de Tor Browser&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Extraia o arquivo baixado:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;tar -xvf tor-browser-linux-x86_64-14.5.7.tar.xz
cd tor-browser&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Inicie então o navegador :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./start-tor-browser.desktop&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Na primeira inicialização, uma janela de configuração será aberta para estabelecer a conexão à rede Tor.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_integração_à_área_de_trabalho_ubuntu&amp;quot;&amp;gt;Integração à área de trabalho Ubuntu&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para adicionar &amp;lt;strong&amp;gt;Tor Browser&amp;lt;/strong&amp;gt; no menu de aplicativos :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./start-tor-browser.desktop --register-app&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você pode então:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Lançar &amp;lt;strong&amp;gt;Tor Browser&amp;lt;/strong&amp;gt; a partir do menu de aplicativos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Adicioná-lo aos favoritos do dock&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Criar um atalho de teclado se desejado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;activity-tor-installation.svg&amp;quot; alt=&amp;quot;activity tor installation&amp;quot; width=&amp;quot;457&amp;quot; height=&amp;quot;470&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_explorar_recursos_educativos_com_tor&amp;quot;&amp;gt;Explorar recursos educativos com Tor&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Tor não é apenas uma ferramenta de anonimato. É um portal de acesso a recursos educacionais e de pesquisa, principalmente nas áreas de :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;OSINT (Inteligência de Fontes Abertas)&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://inteltechniques.com&amp;quot;&amp;gt;https://inteltechniques.com&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://osintframework.com&amp;quot;&amp;gt;https://osintframework.com&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Compartilhamento educativo e open source&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://archive.org&amp;quot;&amp;gt;https://archive.org&amp;lt;/a&amp;gt;(acesso via Tor para mais confidencialidade)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/&amp;quot;&amp;gt;https://github.com/&amp;lt;/a&amp;gt;(acessível via Tor)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://librarygenesis.pro&amp;quot;&amp;gt;https://librarygenesis.pro&amp;lt;/a&amp;gt;(acesso à literatura científica livre)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Transmissão de conhecimento e compreensão do ser humano na sociedade digital&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Recursos sobre a cultura digital, a liberdade de expressão e o acesso ao conhecimento&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Documentação livre sobre cibersegurança e ética das tecnologias&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_atualização_do_tor_browser&amp;quot;&amp;gt;Atualização do Tor Browser&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para garantir uma segurança ideal, é crucial manter &amp;lt;strong&amp;gt;Tor Browser&amp;lt;/strong&amp;gt; atualizado. O navegador possui um mecanismo de atualização automática que pode ser acionado via linha de comando.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A partir do diretório de instalação do Tor Browser, basta reiniciar o script de inicialização :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;cd ~/workspace/tipiak/tor/tor-browser
./start-tor-browser.desktop&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao iniciar, &amp;lt;strong&amp;gt;Tor Browser&amp;lt;/strong&amp;gt; verificará automaticamente se existe uma nova versão. Se for o caso, ele a baixará e a instalará para você.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este método simples garante que você sempre tem os últimos patches de segurança e as melhorias mais recentes, o que é essencial para uma navegação segura.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Verificar os downloads é um reflexo essencial para qualquer pessoa que deseja evoluir em um ambiente digital seguro. Instalar o Tor de forma verificada, é&amp;lt;strong&amp;gt;proteger a sua integridade digital&amp;lt;/strong&amp;gt;, &amp;lt;strong&amp;gt;preservar a confiança no software livre&amp;lt;/strong&amp;gt;, et &amp;lt;strong&amp;gt;reforçar a sua autonomia informacional&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A segurança digital é um ato de consciência, não de paranoia.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;summary-diagram.svg&amp;quot; alt=&amp;quot;summary diagram&amp;quot; width=&amp;quot;522&amp;quot; height=&amp;quot;203&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_referências&amp;quot;&amp;gt;Referências&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Site oficial do Projeto Tor :https://www.torproject.org/download/[&amp;lt;a href=&amp;quot;https://www.torproject.org/download/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://www.torproject.org/download/&amp;lt;/a&amp;gt;]&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Documentação de verificação das assinaturas :https://support.torproject.org/tbb/how-to-verify-signature/[&amp;lt;a href=&amp;quot;https://support.torproject.org/tbb/how-to-verify-signature/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://support.torproject.org/tbb/how-to-verify-signature/&amp;lt;/a&amp;gt;]&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Documentação do Ubuntu:https://ubuntu.com[&amp;lt;a href=&amp;quot;https://ubuntu.com&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://ubuntu.com&amp;lt;/a&amp;gt;]&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Arquivo educativo :https://archive.org/details/opensource[&amp;lt;a href=&amp;quot;https://archive.org/details/opensource&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://archive.org/details/opensource&amp;lt;/a&amp;gt;]&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este artigo faz parte da série &amp;lt;strong&amp;gt;Segurança e Consciência Digital&amp;lt;/strong&amp;gt;, visando promover práticas responsáveis e pedagógicas em torno das ferramentas livres&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Artigo 5: A Aventura do libs.versions.toml Compartilhado</title>
            <link >https://cheroliv.com//blog/2025/0093_gradle_plugin_toml_composite_build_post.html</link>
            <pubDate>Sat, 27 Sep 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0093_gradle_plugin_toml_composite_build_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_1_o_catálogo_de_versões_libs_versions_toml_um_lembrete&amp;quot;&amp;gt;1. O Catálogo de Versões (&amp;lt;code&amp;gt;libs.versions.toml&amp;lt;/code&amp;gt;) : Um lembrete&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_2_o_problema_um_build_composto_e_dois_mundos_separados&amp;quot;&amp;gt;2. O Problema: um Build composto e dois mundos separados&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_3_a_solução_compartilhar_a_resolução_de_dependências&amp;quot;&amp;gt;3. A Solução: Compartilhar a Resolução de Dependências&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_4_conclusão_a_potência_dos_builds_compostos_bem_gerenciados&amp;quot;&amp;gt;4. Conclusão: A Potência dos Builds Compostos Bem Gerenciados&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Público-alvo: Desenvolvedores do Gradle lidando com o gerenciamento de dependências em projetos multi-build.&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Em nossa busca para criar o plugin`site-baker`, tentamos manter uma base de código limpa e centralizada. Uma das melhores práticas no ecossistema Gradle moderno é o uso do&amp;lt;strong&amp;gt;catálogo de versões&amp;lt;/strong&amp;gt;(U+200B(U+200B`translate`)(&amp;lt;code&amp;gt;fr&amp;lt;/code&amp;gt;)&amp;lt;code&amp;gt;libs.versions.toml&amp;lt;/code&amp;gt;) para gerenciar as dependências. Mas o que acontece quando seu projeto se torna mais complexo, como um&amp;lt;strong&amp;gt;construir composto&amp;lt;/strong&amp;gt;Onde um build independente (nosso plugin) deve ser incluído em outro?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Foi ali que nossa aventura tomou um rumo inesperado. Nós queríamos que nosso plugin`site-baker`, que é ele próprio um projeto Gradle, utiliza o mesmo catálogo de versões do nosso projeto principal. Este artigo, o quinto da nossa série, conta como resolvemos esse desafio.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_1_o_catálogo_de_versões_libs_versions_toml_um_lembrete&amp;quot;&amp;gt;1. O Catálogo de Versões (&amp;lt;code&amp;gt;libs.versions.toml&amp;lt;/code&amp;gt;) : Um lembrete&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O arquivo`gradle/libs.versions.toml`é uma funcionalidade do Gradle que permite centralizar as versões e as coordenadas das dependências do seu projeto. É geralmente estruturado em quatro seções:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;[versions]&amp;lt;/code&amp;gt;: Define alias para os números de versão (ex:`kotlin = &amp;quot;1.9.20&amp;quot;`).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;[libraries]&amp;lt;/code&amp;gt;: Define alias para as dependências completas, usando as versões definidas acima (ex:`kotlin-stdlib = { module = &amp;quot;org.jetbrains.kotlin:kotlin-stdlib&amp;quot;, version.ref = &amp;quot;kotlin&amp;quot; }`).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;[bundles]&amp;lt;/code&amp;gt;: Agrupa várias bibliotecas sob um único alias (ex:`jackson = [&amp;quot;jackson-core&amp;quot;, &amp;quot;jackson-databind&amp;quot;]`)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;[plugins]&amp;lt;/code&amp;gt;: Define aliases para os plugins Gradle.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Uma vez definido, o Gradle gera automaticamente acessores tipados, o que deixa seu`build.gradle.kts`muito mais legível :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;dependencies {
    // Au lieu de : implementation(&amp;quot;org.jetbrains.kotlin:kotlin-stdlib:1.9.20&amp;quot;)
    implementation(libs.kotlin.stdlib)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;toml-flow.svg&amp;quot; alt=&amp;quot;toml flow&amp;quot; width=&amp;quot;460&amp;quot; height=&amp;quot;252&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Figure 1. Fluxo de dados do catálogo de versões&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_2_o_problema_um_build_composto_e_dois_mundos_separados&amp;quot;&amp;gt;2. O Problema: um Build composto e dois mundos separados&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A nossa estrutura de projeto é um&amp;lt;strong&amp;gt;construir composto&amp;lt;/strong&amp;gt;. O projeto principal (&amp;lt;code&amp;gt;thymeleaf.cheroliv.com&amp;lt;/code&amp;gt;) inclui o projeto do plugin (&amp;lt;code&amp;gt;site-baker&amp;lt;/code&amp;gt;) via o comando`includeBuild(&amp;quot;site-baker&amp;quot;)&amp;lt;code&amp;gt;no arquivo`settings.gradle.kts&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;composite-build.svg&amp;quot; alt=&amp;quot;composite build&amp;quot; width=&amp;quot;604&amp;quot; height=&amp;quot;246&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Figure 2. Estrutura do build composto&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O problema é que, por padrão, um build incluído é um&amp;lt;strong&amp;gt;mundo separado&amp;lt;/strong&amp;gt;. Ele tem sua própria configuração, seu próprio ciclo de vida e ele não vê o arquivo`libs.versions.toml`do build principal. Nosso`plugin/build.gradle.kts`tentava usar`libs.plugins.kotlin.jvm`, mas o objeto`libs`não estava sendo gerado a partir do arquivo TOML correto, provocando erros de build.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_3_a_solução_compartilhar_a_resolução_de_dependências&amp;quot;&amp;gt;3. A Solução: Compartilhar a Resolução de Dependências&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Após várias pesquisas, a solução se revelou ser uma funcionalidade do Gradle projetada precisamente para este caso:`dependencyResolutionManagement`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No arquivo`settings.gradle.kts` du &amp;lt;strong&amp;gt;construir incluido&amp;lt;/strong&amp;gt;(&amp;lt;code&amp;gt;site-baker/settings.gradle.kts&amp;lt;/code&amp;gt;), podemos dizer ao Gradle onde encontrar o catálogo de versões a ser usado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está a configuração que adicionamos a`site-baker/settings.gradle.kts`(No output, as the input text to translate is empty.)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// site-baker/settings.gradle.kts

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        gradlePluginPortal()
        mavenCentral()
    }
    versionCatalogs {
        create(&amp;quot;libs&amp;quot;) {
            from(files(&amp;quot;../gradle/libs.versions.toml&amp;quot;))
        }
    }
}

rootProject.name = &amp;quot;site-baker&amp;quot;
include(&amp;quot;plugin&amp;quot;)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Analisemos a parte crucial :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;versionCatalogs {
    create(&amp;quot;libs&amp;quot;) { // Crée un catalogue nommé &amp;#39;libs&amp;#39;
        from(files(&amp;quot;../gradle/libs.versions.toml&amp;quot;)) // En utilisant ce fichier
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;create(&amp;quot;libs&amp;quot;)&amp;lt;/code&amp;gt;: Declaramos um catálogo de versões que será acessível através do alias`libs`.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;from(files(&amp;quot;../gradle/libs.versions.toml&amp;quot;))&amp;lt;/code&amp;gt;: É a mágia. Dizemos ao Gradle que a fonte deste catálogo é o arquivo TOML localizado no diretório`gradle` du &amp;lt;strong&amp;gt;projeto pai&amp;lt;/strong&amp;gt;(&amp;lt;code&amp;gt;../&amp;lt;/code&amp;gt;).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com esta configuração, o build`site-baker`sabe agora que ele deve usar o catálogo de versões do projeto principal. O objeto`libs`é gerado corretamente, e as dependências são resolvidas como previsto.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_4_conclusão_a_potência_dos_builds_compostos_bem_gerenciados&amp;quot;&amp;gt;4. Conclusão: A Potência dos Builds Compostos Bem Gerenciados&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta experiência foi rica em lições. O catálogo de versões é uma ferramenta fantástica para a manutenibilidade, mas o seu comportamento em cenários de compilações compostas nem sempre é intuitivo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A chave é lembrar que um build incluído continua sendo um build independente. Para compartilhar configurações como o catálogo de versões, é necessário usar os mecanismos explícitos fornecidos pelo Gradle, como o bloco`dependencyResolutionManagement`dentro de`settings.gradle.kts`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao resolver esse problema, não apenas deixamos nosso build mais limpo, mas também reforçamos a consistência de nosso projeto, garantindo que o plugin e o projeto principal compartilhem uma única fonte de verdade para suas dependências.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No próximo artigo, continuaremos a enriquecer nosso plugin adicionando tarefas mais complexas, agora que nossa gestão de dependências é sólida e centralizada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Artigo 4 : A Armadilha do Cache de Configuração Gradle</title>
            <link >https://cheroliv.com//blog/2025/0092_gradle_configuration_cache_post.html</link>
            <pubDate>Fri, 26 Sep 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0092_gradle_configuration_cache_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_sintoma_builds_fantasma&amp;quot;&amp;gt;O Sintoma: Builds Fantasma&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_investigação_o_que_é_o_cache_de_configuração&amp;quot;&amp;gt;A Investigação: O que é o Cache de Configuração?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_causa_do_problema_um_plugin_não_conforme&amp;quot;&amp;gt;A Causa do Problema: Um Plugin Não Conforme&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_temporária_desativar_o_cache&amp;quot;&amp;gt;A Solução Temporária: Desativar o Cache&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_verdadeira_solução_tornar_o_plugin_compatível&amp;quot;&amp;gt;A Verdadeira Solução : Tornar o Plugin Compatível&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Tempo de leitura :&amp;lt;/em&amp;gt; 11 minutos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Público-alvo: desenvolvedores Gradle que já tenham encontrado comportamentos de build &amp;#39;mágicos&amp;#39; ou inesperados.&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Na nossa aventura de criação do plugin`site-baker`, seguimos uma abordagem TDD rigorosa. Cada funcionalidade era testada, validada, e avançávamos com confiança. E então, um dia, o imprevisto chegou. As builds começaram a se comportar de maneira errática. Modificações na lógica do plugin ou nos arquivos de configuração pareciam ser ignoradas, e nossos testes funcionais, antes confiáveis, falhavam sem razão aparente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este tipo de problema pode ser incrivelmente frustrante. Ele põe em causa a fiabilidade da ferramenta e a validade do nosso código. Depois de uma sessão de depuração intensa, o culpado foi identificado: o&amp;lt;strong&amp;gt;cache de configuração do Gradle&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_sintoma_builds_fantasma&amp;quot;&amp;gt;O Sintoma: Builds Fantasma&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O problema se manifestava de várias maneiras :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Eu estava modificando uma cadeia de caracteres em uma tarefa`println`, mas a antiga cadeia continuava a ser exibida em tempo de execução.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Eu estava alterando um valor no meu arquivo`managed-jbake-context.yml`, mas o plugin agia como se o arquivo não tivesse sido modificado.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Os testes funcionais, que criam projetos de teste on the fly, falhavam porque o plugin não parecia detectar os arquivos de configuração recém-criados.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Tudo estava acontecendo como se o Gradle estivesse executando uma \&amp;quot;versão fantasma\&amp;quot; do nosso build, ignorando nossas alterações mais recentes.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_investigação_o_que_é_o_cache_de_configuração&amp;quot;&amp;gt;A Investigação: O que é o Cache de Configuração?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O cache de configuração é uma funcionalidade relativamente moderna e extremamente poderosa do Gradle, ativada por padrão nas novas versões. Seu objetivo é tornar os builds mais rápidos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;O princípio é simples:&amp;lt;/div&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Na primeira execução, o Gradle executa a fase de&amp;lt;strong&amp;gt;Configuração&amp;lt;/strong&amp;gt;(leitura dos`build.gradle.kts`, criação das tarefas, resolução das dependências) e constrói um grafo de tarefas.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;No final desta fase, Gradle&amp;lt;strong&amp;gt;Serializar este grafo de tarefas&amp;lt;/strong&amp;gt;e o coloca em cache.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Nas execuções seguintes, se nada tiver mudado (scripts de compilação,&amp;lt;code&amp;gt;gradle.properties&amp;lt;/code&amp;gt;, etc.), Gradle&amp;lt;strong&amp;gt;pula completamente a fase de configuração&amp;lt;/strong&amp;gt;e reutiliza o grafo de tarefas em cache&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O ganho de tempo é espectacular nos projetos grandes. No entanto, esse desempenho tem um custo: impõe regras rígidas sobre a forma como os plugins devem ser escritos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;config-cache-lifecycle.svg&amp;quot; alt=&amp;quot;config cache lifecycle&amp;quot; width=&amp;quot;661&amp;quot; height=&amp;quot;709&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Figure 1. O ciclo de vida do cache de configuração&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_causa_do_problema_um_plugin_não_conforme&amp;quot;&amp;gt;A Causa do Problema: Um Plugin Não Conforme&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nosso plugin`site-baker`violou, sem saber, várias regras do cache de configuração. Para que um grafo de tarefas seja serializável, as tarefas não devem conter referências a objetos complexos como o objeto`Project`ou ler arquivos de forma arbitrária durante a fase de execução.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nossa principal falha era ler o conteúdo do arquivo YAML diretamente dentro da lógica de execução da tarefa, usando uma referência ao caminho armazenado na nossa extensão. Essa abordagem é incompatível com o cache porque o Gradle não consegue saber se o conteúdo do arquivo mudou se essa leitura não for modelada como uma&amp;lt;strong&amp;gt;entrada de tarefa&amp;lt;/strong&amp;gt;(Task Input)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_temporária_desativar_o_cache&amp;quot;&amp;gt;A Solução Temporária: Desativar o Cache&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para nos desbloquear e retomar um comportamento de compilação previsível, a solução mais rápida foi desativar o cache de configuração. Basta adicionar a linha seguinte ao arquivo`gradle.properties`do projeto que usa o plugin (ou, no nosso caso, o projeto de teste)&amp;lt;code&amp;gt;site-baker&amp;lt;/code&amp;gt;).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-properties hljs&amp;quot; data-lang=&amp;quot;properties&amp;quot;&amp;gt;# site-baker/gradle.properties
org.gradle.configuration-cache=false&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Instantaneamente, os builds recuperaram seu comportamento normal. Cada execução reiniciava a fase de configuração e nossas alterações foram levadas em conta.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No entanto, essa é uma solução de contorno, não uma solução duradoura. Ela sacrifica o desempenho e não resolve o problema de fundo do nosso plugin.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_verdadeira_solução_tornar_o_plugin_compatível&amp;quot;&amp;gt;A Verdadeira Solução : Tornar o Plugin Compatível&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para que um plugin seja um bom cidadão do ecossistema Gradle moderno, ele deve ser compatível com o cache de configuração. Isso implica repensar a forma como os dados fluem para nossas tarefas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A chave é usar as&amp;lt;strong&amp;gt;APIs do provedor&amp;lt;/strong&amp;gt;do Gradle. Em vez de passar valores diretos (como um`String` ou un &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt;) às nossas tarefas, devemos passar de`Property&amp;amp;lt;T&amp;amp;gt;`ou alguns`Provider&amp;amp;lt;T&amp;amp;gt;`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Aqui está o plano de refatoramento:&amp;lt;/div&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Declarar as entradas de tarefas :&amp;lt;/strong&amp;gt;A tarefa que analisa o arquivo YAML deve declarar esse arquivo como uma entrada. Usa-se para isso a anotação`@InputFile`.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Utilizar as &amp;lt;code&amp;gt;Property&amp;lt;/code&amp;gt; e &amp;lt;code&amp;gt;Provider&amp;lt;/code&amp;gt; :&amp;lt;/strong&amp;gt;O valor de`configFile`será conectada à propriedade`configPath`da nossa extensão DSL. O Gradle assim pode rastrear a origem do dado.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin] (Blank) // No plugin tasks.register&amp;lt;MyTask&amp;gt;(&amp;quot;myTask&amp;quot;) { configFile.set(extension.configPath.flatMap { project.layout.projectDirectory.file(it) }) } [empty hljs&amp;quot; data-lang=&amp;quot;kotlin] (Blank) // No plugin tasks.register&amp;lt;MyTask&amp;gt;(&amp;quot;myTask&amp;quot;) { configFile.set(extension.configPath.flatMap { project.layout.projectDirectory.file(it) }) } [empty&amp;quot;&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Artigo 3: Integração do Jackson para o Parsing YAML com TDD em um Plugin Gradle</title>
            <link >https://cheroliv.com//blog/2025/0091_jackson_yaml_tdd_post.html</link>
            <pubDate>Thu, 25 Sep 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0091_jackson_yaml_tdd_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_contexto_nosso_plugin_site_baker&amp;quot;&amp;gt;O Contexto: Nosso Plugin `site-baker&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_2_modelagem_da_configuração_em_kotlin&amp;quot;&amp;gt;2. Modelagem da Configuração em Kotlin&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_3_integração_de_jackson_no_build_gradle_kts&amp;quot;&amp;gt;3. Integração de Jackson no `build.gradle.kts&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_4_a_abordagem_tdd_testando_o_parsing_yaml&amp;quot;&amp;gt;4. A Abordagem TDD: Testando o parsing YAML&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_4_1_teste_unitário_mapear_o_yaml_para_objeto_kotlin&amp;quot;&amp;gt;4.1. Teste Unitário: Mapear o YAML para Objeto Kotlin&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_4_2_teste_funcional_validar_a_leitura_do_arquivo_de_configuração&amp;quot;&amp;gt;4.2. Teste Funcional: Validar a Leitura do Arquivo de Configuração&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_5_visualização_do_fluxo_de_parsing&amp;quot;&amp;gt;5. Visualização do Fluxo de Parsing&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Público-alvo : Desenvolvedores Gradle intermediários que desejam gerenciar configurações complexas.&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;No desenvolvimento de plugins Gradle, a gestão de configurações complexas é uma tarefa comum. Em vez de sobrecarregar o DSL com centenas de propriedades, costuma ser mais limpo e manutenível definir a configuração em um arquivo externo, como o YAML. Este artigo guia você através da integração da poderosa biblioteca Jackson para analisar arquivos YAML em objetos Kotlin, adotando uma abordagem TDD rigorosa para garantir a robustez do nosso plugin.&amp;lt;code&amp;gt;site-baker&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_contexto_nosso_plugin_site_baker&amp;quot;&amp;gt;O Contexto: Nosso Plugin `site-baker&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nosso plugin`site-baker`é projetado para automatizar a geração e a implantação de um site estático. Ele precisa ler um arquivo de configuração YAML(&amp;lt;code&amp;gt;managed-jbake-context.yml&amp;lt;/code&amp;gt;) para obter informações como os caminhos das fontes, os destinos de implantação e os identificadores Git.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A configuração YAML que queremos analisar é assim:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-yaml hljs&amp;quot; data-lang=&amp;quot;yaml&amp;quot;&amp;gt;bake:
  srcPath: &amp;quot;./site/jbake&amp;quot;
  destDirPath: &amp;quot;bake&amp;quot;
  cname: &amp;quot;cheroliv.com&amp;quot;
pushPage:
  from: &amp;quot;bake&amp;quot;
  to: &amp;quot;cvs&amp;quot;
  repo:
    name: &amp;quot;trainings&amp;quot;
    repository: &amp;quot;https://github.com/pages-content/pages-content.github.io.git&amp;quot;
    credentials:
      username: &amp;quot;USERNAME&amp;quot;
      password: &amp;quot;SECRET_TOKEN&amp;quot;
  branch: &amp;quot;main&amp;quot;
  message: &amp;quot;cheroliv.com&amp;quot;
# ... autres configurations (pushMaquette, supabase, etc.)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_2_modelagem_da_configuração_em_kotlin&amp;quot;&amp;gt;2. Modelagem da Configuração em Kotlin&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes de analisar, devemos definir a estrutura dos nossos dados em Kotlin. Jackson usará estas classes para mapear automaticamente o conteúdo YAML.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// plugin/src/main/kotlin/com/cheroliv/site/baker/data/SiteConfiguration.kt
package com.cheroliv.site.baker.data

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule

fun parseSiteConfiguration(yaml: String): SiteConfiguration {
    val mapper = ObjectMapper(YAMLFactory()).registerKotlinModule()
    return mapper.readValue(yaml)
}

data class GitPushConfiguration(
    val from: String = &amp;quot;&amp;quot;,
    val to: String = &amp;quot;&amp;quot;,
    val repo: RepositoryConfiguration = RepositoryConfiguration(),
    val branch: String = &amp;quot;&amp;quot;,
    val message: String = &amp;quot;&amp;quot;,
)

data class RepositoryConfiguration(
    val name: String = &amp;quot;&amp;quot;,
    val repository: String = &amp;quot;&amp;quot;,
    val credentials: RepositoryCredentials = RepositoryCredentials(),
) {
    companion object {
        const val ORIGIN = &amp;quot;origin&amp;quot;
        const val CNAME = &amp;quot;CNAME&amp;quot;
        const val REMOTE = &amp;quot;remote&amp;quot;
    }
}

data class RepositoryCredentials(val username: String = &amp;quot;&amp;quot;, val password: String = &amp;quot;&amp;quot;)

data class SiteConfiguration(
    val bake: BakeConfiguration = BakeConfiguration(),
    val pushPage: GitPushConfiguration = GitPushConfiguration(),
    val pushMaquette: GitPushConfiguration = GitPushConfiguration(),
    val pushSource: GitPushConfiguration? = null,
    val pushTemplate: GitPushConfiguration? = null,
    val supabase: SupabaseContactFormConfig? = null
)

data class BakeConfiguration(
    val srcPath: String = &amp;quot;&amp;quot;,
    val destDirPath: String = &amp;quot;&amp;quot;,
    val cname: String? = null,
)

// ... (autres data classes pour Supabase, si nécessaire)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A função`parseSiteConfiguration`é o nosso ponto de entrada para a desserialização. Ela utiliza`ObjectMapper`de Jackson, configurado com`YAMLFactory`para o formato YAML e`registerKotlinModule()`para o suporte às especificidades do Kotlin (como os valores padrão das propriedades).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_3_integração_de_jackson_no_build_gradle_kts&amp;quot;&amp;gt;3. Integração de Jackson no `build.gradle.kts&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para usar o Jackson, devemos adicionar as dependências necessárias no`build.gradle.kts`do nosso módulo`plugin` :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// plugin/build.gradle.kts
dependencies {
    // Jackson for YAML parsing
    implementation(&amp;quot;com.fasterxml.jackson.module:jackson-module-kotlin:2.18.3&amp;quot;)
    implementation(&amp;quot;com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.3&amp;quot;)

    // ... autres dépendances
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Utilizamos`jackson-module-kotlin`para o suporte de Kotlin e`jackson-dataformat-yaml`para a gestão do formato YAML.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_4_a_abordagem_tdd_testando_o_parsing_yaml&amp;quot;&amp;gt;4. A Abordagem TDD: Testando o parsing YAML&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Agora, vamos aplicar o TDD para validar nossa lógica de análise.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_4_1_teste_unitário_mapear_o_yaml_para_objeto_kotlin&amp;quot;&amp;gt;4.1. Teste Unitário: Mapear o YAML para Objeto Kotlin&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Começamos com um teste unitário em`SiteBakerPluginTest.kt`para verificar que a função`parseSiteConfiguration`pode transformar corretamente uma string YAML em um objeto`SiteConfiguration`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// plugin/src/test/kotlin/com/cheroliv/site/baker/SiteBakerPluginTest.kt
@Test
fun `can map configuration text to SiteConfiguration object`() {
    val yamlString = &amp;quot;../../managed-jbake-context.yml&amp;quot;
        .run(::File)
        .readText()
        .trimIndent()

    val config: SiteConfiguration = parseSiteConfiguration(yamlString)

    assertEquals(&amp;quot;cheroliv.com&amp;quot;, config.bake.cname)
    assertEquals(&amp;quot;main&amp;quot;, config.pushPage.branch)
    assertEquals(&amp;quot;https://github.com/pages-content/pages-content.github.io.git&amp;quot;, config.pushPage.repo.repository)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este teste lê o conteúdo do arquivo`managed-jbake-context.yml`(que deve existir para que o teste seja válido) e faz asserções sobre o objeto`SiteConfiguration`resultante. Ele valida que os valores-chave do YAML estão corretamente mapeados.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_4_2_teste_funcional_validar_a_leitura_do_arquivo_de_configuração&amp;quot;&amp;gt;4.2. Teste Funcional: Validar a Leitura do Arquivo de Configuração&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para garantir que o plugin possa ler o arquivo de configuração via seu DSL e que a análise funcione em um ambiente Gradle real, adicionamos um teste funcional em`SiteBakerPluginFunctionalTest.kt`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este teste é crucial porque simula a execução do plugin em um projeto real. Deve ser&amp;lt;strong&amp;gt;hermético&amp;lt;/strong&amp;gt;, isto é, ele deve criar o arquivo por conta própria`managed-jbake-context.yml`com um conteúdo controlado, para garantir a reprodutibilidade e o isolamento do teste&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// plugin/src/functionalTest/kotlin/com/cheroliv/site/baker/SiteBakerPluginFunctionalTest.kt
@Test
fun `config file contains good data`(){
    val configContent = configFile.readText(UTF_8)
    // Vérifications comme dans vos commentaires
    assertTrue(configContent.contains(&amp;quot;bake&amp;quot;))
    assertTrue(configContent.contains(&amp;quot;pushPage&amp;quot;))
    assertTrue(configContent.contains(&amp;quot;repository&amp;quot;))
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este teste funcional verifica que o arquivo de configuração copiado para o diretório temporário do teste contém os dados esperados. Embora este teste não faça o parsing diretamente do YAML em um objeto Kotlin, ele valida a presença do arquivo e do seu conteúdo, o que é um passo prévio essencial para o parsing pelo plugin.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_5_visualização_do_fluxo_de_parsing&amp;quot;&amp;gt;5. Visualização do Fluxo de Parsing&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O diagrama a seguir ilustra o fluxo de dados e as interações durante o parsing da configuração YAML :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-297b3d98d9a16575dcdb6d07a1077b4f.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;1534&amp;quot; height=&amp;quot;472&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao seguir uma abordagem TDD, integramos com sucesso a biblioteca Jackson para analisar arquivos de configuração YAML em objetos Kotlin dentro do nosso plugin Gradle. Este método nos permitiu:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Modelar claramente&amp;lt;/strong&amp;gt;nossa configuração com classes de dados Kotlin.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Validar a lógica de análise&amp;lt;/strong&amp;gt;com testes unitários direcionados.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Garantir a integração&amp;lt;/strong&amp;gt;num ambiente Gradle real graças a testes funcionais herméticos.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta base sólida nos oferece uma grande confiança para estender nosso plugin com funcionalidades que se baseiam nessa configuração, ao mesmo tempo em que garante a manutenibilidade e a robustez do código. O parsing YAML agora é um recurso confiável e testado do nosso plugin`site-baker`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Artigo 2 : Desenvolver um Plugin Gradle com uma Abordagem TDD</title>
            <link >https://cheroliv.com//blog/2025/0090_TDD_gradle_plugin_post.html</link>
            <pubDate>Wed, 24 Sep 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0090_TDD_gradle_plugin_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table of Contents&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_inicialização_do_projeto&amp;quot;&amp;gt;A Inicialização do Projeto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_2_nosso_primeiro_ciclo_tdd_registrar_uma_tarefa&amp;quot;&amp;gt;2. Nosso Primeiro Ciclo TDD : Registrar uma Tarefa&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_2_1_o_teste_primeiro_o_teste_que_falha&amp;quot;&amp;gt;2.1. O teste primeiro (O teste que falha)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_2_2_o_código_depois_fazer_passar_o_teste&amp;quot;&amp;gt;2.2. O Código Depois (Fazer Passar o Teste)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_3_segundo_ciclo_tdd_adicionar_uma_extensão_dsl&amp;quot;&amp;gt;3. Segundo Ciclo TDD : Adicionar uma Extensão DSL&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_3_1_o_teste_primeiro&amp;quot;&amp;gt;3.1. O Teste Primeiro&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_3_2_o_código_depois&amp;quot;&amp;gt;3.2. O Código Depois&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_4_o_teste_funcional_validar_a_integração&amp;quot;&amp;gt;4. O Teste Funcional: Validar a Integração&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_4_1_o_teste_de_integração_criar_um_ambiente_controlado&amp;quot;&amp;gt;4.1. O Teste de Integração : Criar um Ambiente Controlado&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_4_2_finalizar_a_lógica&amp;quot;&amp;gt;4.2. Finalizar a Lógica&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Neste artigo, vamos explorar como montar uma base de desenvolvimento sólida para um plugin Gradle usando uma abordagem de Desenvolvimento Orientado por Testes (TDD - Test‑Driven Development). Este método garante que nosso código seja robusto, manutenível e atenda exatamente aos requisitos desde o início.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_inicialização_do_projeto&amp;quot;&amp;gt;A Inicialização do Projeto&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Gradle facilita muito a criação de um novo plugin graças ao comando`gradle init`. Ao escolher criar um &amp;#39;Gradle Plugin&amp;#39; com Kotlin, obtemos uma estrutura de projeto pronta para uso, incluindo dois tipos de testes cruciais :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Testes Unitários :&amp;lt;/strong&amp;gt;Localizados em`src/test`, eles permitem validar componentes isolados do nosso plugin, como a lógica interna de uma tarefa ou a configuração de uma extensão. Eles são rápidos e não precisam de uma execução completa do Gradle.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Testes Funcionais :&amp;lt;/strong&amp;gt;Situados em`src/functionalTest`, eles usam`GradleRunner`para executar uma compilação Gradle completa em um projeto de teste temporário. Isso nos permite verificar o comportamento real do plugin em um ambiente controlado.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_2_nosso_primeiro_ciclo_tdd_registrar_uma_tarefa&amp;quot;&amp;gt;2. Nosso Primeiro Ciclo TDD : Registrar uma Tarefa&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nossa primeira exigência é simples: o plugin deve registrar uma tarefa nomeada`printSiteConfig`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_2_1_o_teste_primeiro_o_teste_que_falha&amp;quot;&amp;gt;2.1. O teste primeiro (O teste que falha)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Conforme o TDD, escrevemos primeiro um teste que verifica a existência dessa tarefa. Dentro`SiteBakerPluginTest.kt`(nosso arquivo de teste unitário), adicionamos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test
fun `plugin registers task`() {
    // Créer un projet de test en mémoire
    val project = ProjectBuilder.builder().build()
    project.plugins.apply(&amp;quot;com.cheroliv.site-baker&amp;quot;)

    // Vérifier que la tâche a bien été enregistrée
    assertNotNull(project.tasks.findByName(&amp;quot;printSiteConfig&amp;quot;))
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este teste falha porque ainda não escrevemos nenhum código em nosso plugin.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_2_2_o_código_depois_fazer_passar_o_teste&amp;quot;&amp;gt;2.2. O Código Depois (Fazer Passar o Teste)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Agora, estamos escrevendo o mínimo de código necessário em`SiteBakerPlugin.kt`para que o teste passe :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;class SiteBakerPlugin: Plugin&amp;amp;lt;Project&amp;amp;gt; {
    override fun apply(project: Project) {
        // Enregistrer une tâche simple
        project.tasks.register(&amp;quot;printSiteConfig&amp;quot;) { task -&amp;amp;gt;
            // ... la logique de la tâche viendra plus tard
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nós reiniciamos os testes, e eles passam. Nossa primeira funcionalidade está validada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_3_segundo_ciclo_tdd_adicionar_uma_extensão_dsl&amp;quot;&amp;gt;3. Segundo Ciclo TDD : Adicionar uma Extensão DSL&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O requisito seguinte é permitir que os usuários configurem nosso plugin por meio de um bloco DSL em seu`build.gradle.kts`. Nós queremos um bloco`site { &amp;amp;#8230;&amp;amp;#8203; }`onde se pode especificar um caminho de configuração.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_3_1_o_teste_primeiro&amp;quot;&amp;gt;3.1. O Teste Primeiro&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Adicionamos um teste para verificar que a extensão`site`é bem registrada e pode-se atribuir um valor a ela.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test
fun `plugin registers extension`() {
    val project = ProjectBuilder.builder().build()
    project.plugins.apply(&amp;quot;com.cheroliv.site-baker&amp;quot;)

    // Récupérer l&amp;#39;extension et lui affecter une valeur
    project.extensions
        .findByType(SiteExtension::class.java)!!
        .configPath
        .set(&amp;quot;config.yml&amp;quot;)

    // Vérifier que la valeur a bien été prise en compte
    assertEquals(
        &amp;quot;config.yml&amp;quot;,
        project.extensions.findByType(SiteExtension::class.java)?.configPath?.get()
    )
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este teste falha porque nem a classe`SiteExtension`nem o registro da extensão existem&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_3_2_o_código_depois&amp;quot;&amp;gt;3.2. O Código Depois&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nós criamos a classe`SiteBakerExtension.kt`e atualizamos`SiteBakerPlugin.kt`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// SiteBakerExtension.kt
open class SiteExtension @Inject constructor(objects: ObjectFactory) {
    val configPath: Property&amp;amp;lt;String&amp;amp;gt; = objects.property(String::class.java)
}

// SiteBakerPlugin.kt
class SiteBakerPlugin: Plugin&amp;amp;lt;Project&amp;amp;gt; {
    override fun apply(project: Project) {
        // Enregistrer l&amp;#39;extension
        val extension = project.extensions.create(&amp;quot;site&amp;quot;, SiteExtension::class.java)

        project.tasks.register(&amp;quot;printSiteConfig&amp;quot;) { task -&amp;amp;gt;
            task.doLast {
                // On utilisera l&amp;#39;extension plus tard
            }
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os testes passam novamente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_4_o_teste_funcional_validar_a_integração&amp;quot;&amp;gt;4. O Teste Funcional: Validar a Integração&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Agora que as unidades estão testadas, precisamos garantir que tudo funcione junto em uma build real. Essa é a função do teste funcional em`SiteBakerPluginFunctionalTest.kt`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_4_1_o_teste_de_integração_criar_um_ambiente_controlado&amp;quot;&amp;gt;4.1. O Teste de Integração : Criar um Ambiente Controlado&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este teste vai simular um projeto real usando nosso plugin. Para que seja confiável, ele deve ser&amp;lt;strong&amp;gt;hermético&amp;lt;/strong&amp;gt;, ou seja, ele não deve depender de arquivos existentes no sistema. Ele deve criar por si mesmo todas as condições necessárias à sua execução.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O teste vai portanto :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Criar um`build.gradle.kts`de teste que utiliza nosso plugin e seu DSL.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Criar o arquivo de configuração&amp;lt;/strong&amp;gt;(&amp;lt;code&amp;gt;managed-jbake-context.yml&amp;lt;/code&amp;gt;) que o plugin deve ler. É uma etapa crucial para a robustez do teste.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Executar a tarefa`printSiteConfig`via`GradleRunner`.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Verificar que a saída da build está correta.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao copiar ou criar este arquivo de configuração em cada execução, garantimos que o teste esteja&amp;lt;strong&amp;gt;reprodutível&amp;lt;/strong&amp;gt;e não depende de um estado externo. Isso protege nossos testes contra regressões que poderiam estar relacionadas à leitura do arquivo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test fun `can run task with DSL`() {
    // 1. Créer un build.gradle.kts de test
    buildFile.writeText(&amp;quot;&amp;quot;&amp;quot;
        plugins { id(&amp;quot;com.cheroliv.site-baker&amp;quot;) }
        site { configPath = &amp;quot;managed-jbake-context.yml&amp;quot; }
    &amp;quot;&amp;quot;&amp;quot;.trimIndent())

    // 2. Créer le fichier de configuration pour un test contrôlé
    val configFile = File(projectDir, &amp;quot;managed-jbake-context.yml&amp;quot;)
    configFile.writeText(&amp;quot;site: { title: &amp;#39;Mon Site de Test&amp;#39; }&amp;quot;) // Contenu YAML simple

    // 3. Exécuter la build
    val runner = GradleRunner.create()
    runner.withPluginClasspath()
    runner.withArguments(&amp;quot;printSiteConfig&amp;quot;)
    runner.withProjectDir(projectDir) // Spécifier le répertoire du projet de test
    val result = runner.build()

    // 4. Vérifier la sortie
    assertTrue(result.output.contains(&amp;quot;Site config path: managed-jbake-context.yml&amp;quot;))
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este teste falhará enquanto a tarefa`printSiteConfig`Não usará realmente o valor da extensão.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_4_2_finalizar_a_lógica&amp;quot;&amp;gt;4.2. Finalizar a Lógica&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Estamos atualizando a tarefa em`SiteBakerPlugin.kt`para que ela exiba o valor configurado:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;project.tasks.register(&amp;quot;printSiteConfig&amp;quot;) { task -&amp;amp;gt;
    task.doLast {
        println(&amp;quot;Site config path: ${extension.configPath.get()}&amp;quot;)
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Todos os testes, unitários e funcionais, agora passam.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Seguindo uma abordagem TDD, construímos um plugin de forma incremental e segura. Cada pequena funcionalidade é imediatamente validada por um teste, desde testes unitários rápidos até testes funcionais que validam a integração completa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ao cuidar de tornar nossos testes funcionais&amp;lt;strong&amp;gt;herméticos&amp;lt;/strong&amp;gt;— notadamente ao criar programaticamente os arquivos de configuração necessários — construímos uma rede de segurança extremamente robusta. Essa rigidez nos protege eficazmente contra as regressões e nos dá muita confiança para adicionar funcionalidades mais complexas posteriormente, como a análise do arquivo de configuração YAML.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta base de testes sólidos é o ativo mais valioso para a manutenção e evolução futura do plugin.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Artigo 1: Criar um Plugin Gradle do A ao Z com o comando gradle init</title>
            <link >https://cheroliv.com//blog/2025/0089_creation_projet_plugin_gradle_post.html</link>
            <pubDate>Tue, 23 Sep 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0089_creation_projet_plugin_gradle_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_1_iniciar_o_assistente_do_projeto&amp;quot;&amp;gt;Etapa 1: Iniciar o assistente do projeto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_2_compreender_a_estrutura_gerada&amp;quot;&amp;gt;Passo 2: Compreender a estrutura gerada&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquivos_e_pastas_chave&amp;quot;&amp;gt;Arquivos e pastas chave&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Público-alvo : Desenvolvedor iniciante a intermediário com Gradle.&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Iniciar um novo projeto de plugin Gradle pode parecer intimidante. Felizmente, o Gradle fornece um assistente interativo poderoso, a tarefa`init`, que gera uma estrutura de projeto completa, limpa e pronta para uso. Este guia mostrará como usar esta ferramenta para criar uma base de plugin saudável, explicará a estrutura e mostrará como executar as primeiras tarefas de build.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_etapa_1_iniciar_o_assistente_do_projeto&amp;quot;&amp;gt;Etapa 1: Iniciar o assistente do projeto&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O comando`gradle init`é o ponto de partida. Ele lança um assistente de linha de comando que faz uma série de perguntas para configurar o projeto.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Abra o seu terminal em uma pasta vazia e execute:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;gradle init&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O assistente o guiará. Aqui estão as escolhas a fazer para um plugin Gradle escrito em Kotlin:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Selecione o tipo de projeto a gerar:&amp;lt;/strong&amp;gt;Escolha`Gradle plugin`.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Selecionar linguagem de implementação:&amp;lt;/strong&amp;gt;Escolha`Kotlin`.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Selecione o DSL do script de compilação:&amp;lt;/strong&amp;gt;Escolha`Kotlin`.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Nome do projeto:&amp;lt;/strong&amp;gt;Insira um nome para o seu projeto (ex:`site-baker`).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;ID do Plugin&amp;lt;/strong&amp;gt;Dê um identificador único ao seu plugin (ex:`com.cheroliv.site-baker`).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Plugin class:&amp;lt;/strong&amp;gt;Especifique o nome da classe da implementação (ex:`com.cheroliv.SiteBakerPlugin`)&amp;#39;.&amp;#39;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Uma vez concluído, o Gradle gera uma árvore de arquivos completa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;init-gradle.svg&amp;quot; alt=&amp;quot;init gradle&amp;quot; width=&amp;quot;193&amp;quot; height=&amp;quot;406&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Figure 1. Diagrama do processo de inicialização&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_passo_2_compreender_a_estrutura_gerada&amp;quot;&amp;gt;Passo 2: Compreender a estrutura gerada&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O assistente cria uma estrutura de projeto multi-módulos, separando o projeto raiz do código-fonte do próprio plugin.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;.
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── plugin/
│   ├── build.gradle.kts
│   └── src/
│       ├── main/
│       │   └── kotlin/
│       │       └── com/cheroliv/SiteBakerPlugin.kt
│       ├── test/
│       │   └── kotlin/
│       │       └── com/cheroliv/SiteBakerPluginTest.kt
│       └── functionalTest/
│           └── kotlin/
│               └── com/cheroliv/SiteBakerPluginFunctionalTest.kt
└── settings.gradle.kts&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_arquivos_e_pastas_chave&amp;quot;&amp;gt;Arquivos e pastas chave&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;settings.gradle.kts`Este ficheiro na raiz define os módulos incluídos na compilação. Aqui, ele inclui o subprojeto`plugin&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;[source,kotlin] (No text provided to translate, so output is empty.) rootProject.name = &amp;quot;site-baker include(&amp;quot;plugin&amp;quot;) ----&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Artigo 0: Configuração do Ambiente para Criar Plugins Gradle</title>
            <link >https://cheroliv.com//blog/2025/0088_environnement_plugin_gradle_post.html</link>
            <pubDate>Mon, 22 Sep 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0088_environnement_plugin_gradle_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_jdk_o_motor_do_gradle&amp;quot;&amp;gt;O JDK: O motor do Gradle&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_qual_versão_escolher&amp;quot;&amp;gt;Qual versão escolher?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalação_via_sdkman_recomendado&amp;quot;&amp;gt;Instalação via SDKMAN! (Recomendado)&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalar_sdkman&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Instalar SDKMAN!&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalar_uma_versão_do_jdk&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Instalar uma versão do JDK&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_verificar_a_instalação&amp;quot;&amp;gt;Verificar a instalação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_2_o_ide_intellij_idea&amp;quot;&amp;gt;2. O IDE: IntelliJ IDEA&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_3_gradle_a_ferramenta_de_build&amp;quot;&amp;gt;3. Gradle: A ferramenta de build&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalação_via_sdkman&amp;quot;&amp;gt;Instalação via SDKMAN!&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalar_gradle&amp;quot;&amp;gt;Instalar Gradle&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_verificar_a_instalação_2&amp;quot;&amp;gt;Verificar a instalação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_4_jbake_o_gerador_de_site_estático&amp;quot;&amp;gt;4. JBake: O gerador de site estático&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalação_via_sdkman_2&amp;quot;&amp;gt;Instalação via SDKMAN!&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalar_jbake&amp;quot;&amp;gt;Instalar JBake&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_verificar_a_instalação_3&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Verificar a instalação&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_5_o_gradle_wrapper_a_prática_absoluta&amp;quot;&amp;gt;5. O Gradle Wrapper: A prática absoluta&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_6_docker_e_portainer_para_builds_reprodutíveis_opcional&amp;quot;&amp;gt;6. Docker e Portainer: Para Builds Reprodutíveis (Opcional)&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalação_do_docker_engine&amp;quot;&amp;gt;Instalação do Docker Engine&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalar_docker_via_o_script_oficial&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Instalar Docker via o script oficial :&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_gerir_docker_sem_sudo_recomendado&amp;quot;&amp;gt;Gerir Docker sem &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (recomendado):&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_gerenciar_docker_com_portainer_gui&amp;quot;&amp;gt;Gerenciar Docker com Portainer (GUI)&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_criar_um_volume_para_os_dados_do_portainer&amp;quot;&amp;gt;Criar um volume para os dados do Portainer :&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_iniciar_o_contêiner_portainer&amp;quot;&amp;gt;Iniciar o contêiner Portainer :&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Público-alvo: Desenvolvedores que desejam iniciar a criação de plugins Gradle.&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Para desenvolver plugins Gradle de forma eficaz, um ambiente de desenvolvimento bem configurado é indispensável. Um bom setup vai economizar tempo, reduzirá as frustrações e garantirá a consistência das suas builds.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este guia concentra‑se em quatro pilares essenciais:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Le &amp;lt;strong&amp;gt;JDK&amp;lt;/strong&amp;gt;(Java Development Kit), pois Gradle e JBake executam-se na JVM.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Un &amp;lt;strong&amp;gt;IDE&amp;lt;/strong&amp;gt;moderno (IntelliJ IDEA) com bom suporte para Kotlin e Gradle.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Gradle&amp;lt;/strong&amp;gt;ele mesmo, para inicializar os projetos.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;JBake&amp;lt;/strong&amp;gt;, o motor do nosso site estático.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;images/gradle-env.svg&amp;quot; alt=&amp;quot;gradle env&amp;quot; width=&amp;quot;1615&amp;quot; height=&amp;quot;5696&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Figure 1. Componentes do ambiente de desenvolvimento&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_jdk_o_motor_do_gradle&amp;quot;&amp;gt;O JDK: O motor do Gradle&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Gradle é um aplicativo que é executado na Máquina Virtual Java (JVM). Portanto, a instalação de um JDK é um requisito absoluto.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_qual_versão_escolher&amp;quot;&amp;gt;Qual versão escolher?&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É recomendado usar uma versão LTS (Long-Term Support) do Java. Atualmente,&amp;lt;strong&amp;gt;JDK 17&amp;lt;/strong&amp;gt; ou &amp;lt;strong&amp;gt;JDK 21&amp;lt;/strong&amp;gt;são excelentes escolhas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_instalação_via_sdkman_recomendado&amp;quot;&amp;gt;Instalação via SDKMAN! (Recomendado)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;SDKMAN! é uma ferramenta excelente para gerenciar as versões de muitos SDKs, incluindo Java e Gradle.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_instalar_sdkman&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Instalar SDKMAN!&amp;lt;/strong&amp;gt;&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;curl -s &amp;quot;https://get.sdkman.io&amp;quot; | bash source &amp;quot;$HOME/.sdkman/bin/sdkman-init.sh&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_instalar_uma_versão_do_jdk&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Instalar uma versão do JDK&amp;lt;/strong&amp;gt;&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Pour lister les versions de Java disponibles sdk list java

# Pour installer Java 17 (par exemple, la distribution Temurin)
sdk install java 17.0.8-tem&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_verificar_a_instalação&amp;quot;&amp;gt;Verificar a instalação&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;java -version&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você deve ver a versão que você acabou de instalar ser exibida.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_2_o_ide_intellij_idea&amp;quot;&amp;gt;2. O IDE: IntelliJ IDEA&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para o desenvolvimento de plugins Gradle com o DSL Kotlin,&amp;lt;strong&amp;gt;IntelliJ IDEA&amp;lt;/strong&amp;gt;é a ferramenta de escolha. A sua integração com Gradle e Kotlin é incomparável.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Edição Community :&amp;lt;/strong&amp;gt;gratuita e amplamente suficiente para começar.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Edição Definitiva :&amp;lt;/strong&amp;gt;Paga, ela oferece funcionalidades avançadas para desenvolvimento web e banco de dados.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Baixe a versão que lhe convém do site da JetBrains: &amp;lt;a href=&amp;quot;https://www.jetbrains.com/idea/download/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://www.jetbrains.com/idea/download/&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A IDE detectará automaticamente seus arquivos`build.gradle.kts` et &amp;lt;code&amp;gt;settings.gradle.kts&amp;lt;/code&amp;gt;, oferecendo autocompletão poderosa, navegação no código e ferramentas de depuração integradas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_3_gradle_a_ferramenta_de_build&amp;quot;&amp;gt;3. Gradle: A ferramenta de build&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mesmo que cada projeto utilizará sua própria versão do Gradle via o Gradle Wrapper, é útil ter uma instalação global do Gradle para tarefas como a inicialização de novos projetos (&amp;lt;code&amp;gt;gradle init&amp;lt;/code&amp;gt;).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_instalação_via_sdkman&amp;quot;&amp;gt;Instalação via SDKMAN!&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Tal como para o JDK, SDKMAN! simplifica a instalação do Gradle.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_instalar_gradle&amp;quot;&amp;gt;Instalar Gradle&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sdk install gradle&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_verificar_a_instalação_2&amp;quot;&amp;gt;Verificar a instalação&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;gradle -v&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este comando exibirá as versões do Gradle, Kotlin, Groovy e do JDK usadas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_4_jbake_o_gerador_de_site_estático&amp;quot;&amp;gt;4. JBake: O gerador de site estático&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;JBake é a ferramenta que vamos usar para transformar os nossos ficheiros fontes (como este) num site web estático. Ter uma instalação local é prático para visualizar as mudanças.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_instalação_via_sdkman_2&amp;quot;&amp;gt;Instalação via SDKMAN!&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Assim como as outras ferramentas, SDKMAN! é o método mais simples.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_instalar_jbake&amp;quot;&amp;gt;Instalar JBake&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sdk install jbake&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_verificar_a_instalação_3&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Verificar a instalação&amp;lt;/strong&amp;gt;&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;jbake -v&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este comando exibirá a versão do JBake e as informações do ambiente Java que ele utiliza.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_5_o_gradle_wrapper_a_prática_absoluta&amp;quot;&amp;gt;5. O Gradle Wrapper: A prática absoluta&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Depois que seu projeto for criado (o que veremos no próximo artigo), você não deve mais usar o comando global.&amp;lt;code&amp;gt;gradle&amp;lt;/code&amp;gt;. Em vez disso, você usará o&amp;lt;strong&amp;gt;Wrapper do Gradle&amp;lt;/strong&amp;gt;(&amp;lt;code&amp;gt;./gradlew&amp;lt;/code&amp;gt;), um script incluido no seu projeto&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Por que?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Reprodutibilidade :&amp;lt;/strong&amp;gt;O Wrapper garante que cada pessoa que trabalha no projeto (incluindo o seu servidor de CI/CD) use exatamente a&amp;lt;strong&amp;gt;mesma versão do Gradle&amp;lt;/strong&amp;gt;, eliminando assim os erros de build devido a ambientes diferentes&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Simplicidade:&amp;lt;/strong&amp;gt;Não é necessário instalar o Gradle manualmente para contribuir a um projeto.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Basta clonar o repositório e executar`./gradlew`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um comando típico será assim:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Ne pas faire : gradle build
# À faire :
./gradlew build&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_6_docker_e_portainer_para_builds_reprodutíveis_opcional&amp;quot;&amp;gt;6. Docker e Portainer: Para Builds Reprodutíveis (Opcional)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Embora não sejam estritamente necessários para escrever um plugin, Docker e Portainer são ferramentas DevOps modernas que facilitam muito a criação de builds reproduzíveis e o gerenciamento do seu ambiente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_instalação_do_docker_engine&amp;quot;&amp;gt;Instalação do Docker Engine&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O Docker permite que você empacote sua aplicação e suas dependências em um contêiner.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_instalar_docker_via_o_script_oficial&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Instalar Docker via o script oficial :&amp;lt;/strong&amp;gt;&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_gerir_docker_sem_sudo_recomendado&amp;quot;&amp;gt;Gerir Docker sem &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (recomendado):&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Adicione seu usuário ao grupo`docker`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sudo usod -aG docker $USER&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;Important&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;

&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;docker run hello-world&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_gerenciar_docker_com_portainer_gui&amp;quot;&amp;gt;Gerenciar Docker com Portainer (GUI)&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Portainer é uma interface web leve para gerenciar seus contêineres Docker.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_criar_um_volume_para_os_dados_do_portainer&amp;quot;&amp;gt;Criar um volume para os dados do Portainer :&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;docker volume create portainer_data&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_iniciar_o_contêiner_portainer&amp;quot;&amp;gt;Iniciar o contêiner Portainer :&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você pode agora acessar a interface do Portainer em`https://localhost:9443`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Seu ambiente está agora pronto! Você dispõe de um JDK, do IDE mais adequado, do Gradle e do JBake. Com a adição do Docker, você também está pronto para criar builds contêinerizados e reproduzíveis. Esta base sólida permitirá que você se concentre no essencial: o desenvolvimento de plugins poderosos e úteis.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No próximo artigo, utilizaremos o comando`gradle init`para gerar a estrutura do nosso primeiro projeto de plugin.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>CI/CD Python – Parte 3 : Ir além com Poetry, Docker e a publicação avançada</title>
            <link >https://cheroliv.com//blog/2025/0087_pypi_cicd_part_3_post.html</link>
            <pubDate>Sat, 19 Jul 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0087_pypi_cicd_part_3_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table of Contents&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_integração_com_poetry&amp;quot;&amp;gt;Integração com Poetry&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalação_do_poetry&amp;quot;&amp;gt;Instalação do Poetry&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_inicialização_do_projeto&amp;quot;&amp;gt;Inicialização do projeto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_adição_e_instalação_de_dependências&amp;quot;&amp;gt;Adição e instalação de dependências&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_publicação_com_poetry&amp;quot;&amp;gt;Publicação com Poetry&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_builds_reprodutíveis_com_docker&amp;quot;&amp;gt;Builds reprodutíveis com Docker&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_exemplo_de_dockerfile&amp;quot;&amp;gt;Exemplo de Dockerfile&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_integração_no_github_actions&amp;quot;&amp;gt;Integração no GitHub Actions&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_publicação_condicional&amp;quot;&amp;gt;Publicação condicional&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_automatização_das_dependências_com_renovate_e_dependabot&amp;quot;&amp;gt;Automatização das dependências com Renovate e Dependabot&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_dependabot&amp;quot;&amp;gt;Dependabot&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_renovar&amp;quot;&amp;gt;renovar&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagramas_plantuml&amp;quot;&amp;gt;Diagramas PlantUML&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_caso_de_uso_cicd_avançado&amp;quot;&amp;gt;Caso de Uso – CI/CD avançado&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_sequência_publicação_condicional&amp;quot;&amp;gt;Sequência – Publicação condicional&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estados_pipeline_cicd&amp;quot;&amp;gt;Estados – Pipeline CI/CD&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_implantação_arquitetura_cicd&amp;quot;&amp;gt;Implantação – Arquitetura CI/CD&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nas duas primeiras partes desta série, nós temos : - Estabeleci um pipeline CI/CD funcional para uma aplicação Python com GitHub Actions e PyPI. - Industrializamos este pipeline com testes multi-versão, uma publicação progressiva via Test PyPI e ferramentas de qualidade e segurança.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nesta terceira parte, vamos ir&amp;lt;strong&amp;gt;além da simples publicação no PyPI&amp;lt;/strong&amp;gt;para construir uma cadeia de CI/CD completa, robusta e profissional com : - &amp;lt;strong&amp;gt;Poesia&amp;lt;/strong&amp;gt;para uma embalagem moderna e uma gestão otimizada das dependências. - &amp;lt;strong&amp;gt;Docker&amp;lt;/strong&amp;gt;para criar builds reproduzíveis e multi-plataformas - &amp;lt;strong&amp;gt;Publicação condicional&amp;lt;/strong&amp;gt;para gerenciar cenários como os release candidates. - &amp;lt;strong&amp;gt;Ferramentas de automação&amp;lt;/strong&amp;gt;(Renovate, Dependabot) para manter o pipeline atualizado sem esforço.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_integração_com_poetry&amp;quot;&amp;gt;Integração com Poetry&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Poetry substitui as antigas ferramentas de empacotamento (setup.py, requirements.txt) ao centralizar a gestão das dependências e da construção em`pyproject.toml`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_instalação_do_poetry&amp;quot;&amp;gt;Instalação do Poetry&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;shell&amp;quot;&amp;gt;# Installer Poetry
curl -sSL https://install.python-poetry.org | python3 -
# Vérifier la version
poetry --version&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_inicialização_do_projeto&amp;quot;&amp;gt;Inicialização do projeto&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;shell&amp;quot;&amp;gt;# Initialiser un nouveau projet avec Poetry
poetry init
# Suivre l&amp;#39;assistant pour renseigner : nom, version, description, licence, dépendances.&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Isso gera um arquivo`pyproject.toml` :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;toml&amp;quot;&amp;gt;[tool.poetry]
name = &amp;quot;playlist-downloader&amp;quot;
version = &amp;quot;0.1.0&amp;quot;
description = &amp;quot;CLI tool for managing YouTube playlists&amp;quot;
authors = [&amp;quot;Christophe Hérolivier &amp;amp;lt;cheroliv@example.com&amp;amp;gt;&amp;quot;]

[tool.poetry.dependencies]
python = &amp;quot;&amp;amp;gt;=3.8&amp;quot;
typer = &amp;quot;^0.9.0&amp;quot;
yt-dlp = &amp;quot;^2023.7.6&amp;quot;
google-api-python-client = &amp;quot;^2.0.0&amp;quot;
google-auth-oauthlib = &amp;quot;^1.0.0&amp;quot;

[tool.poetry.group.dev.dependencies]
pytest = &amp;quot;^7.0&amp;quot;
mypy = &amp;quot;^1.0&amp;quot;
bandit = &amp;quot;^1.7&amp;quot;
safety = &amp;quot;^2.3&amp;quot;
black = &amp;quot;^23.0&amp;quot;
ruff = &amp;quot;^0.1&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_adição_e_instalação_de_dependências&amp;quot;&amp;gt;Adição e instalação de dependências&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;shell&amp;quot;&amp;gt;poetry add typer yt-dlp google-api-python-client google-auth-oauthlib
poetry add --group dev pytest mypy black bandit safety ruff&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_publicação_com_poetry&amp;quot;&amp;gt;Publicação com Poetry&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Poetry lida nativamente com a publicação :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;shell&amp;quot;&amp;gt;# Publication sur Test PyPI
poetry publish --build --repository test-pypi

# Publication sur PyPI
poetry publish --build&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este comando usa automaticamente as informações presentes em`pyproject.toml`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_builds_reprodutíveis_com_docker&amp;quot;&amp;gt;Builds reprodutíveis com Docker&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para garantir execuções idênticas em desenvolvimento, CI/CD e produção, o Docker integra-se perfeitamente com a Poetry.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_exemplo_de_dockerfile&amp;quot;&amp;gt;Exemplo de Dockerfile&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;dockerfile&amp;quot;&amp;gt;FROM python:3.11-slim

WORKDIR /app
COPY pyproject.toml poetry.lock ./
RUN pip install poetry
RUN poetry install --no-root --only main

COPY . .

CMD [&amp;quot;poetry&amp;quot;, &amp;quot;run&amp;quot;, &amp;quot;python&amp;quot;, &amp;quot;cli.py&amp;quot;]&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Isso garante: - Um ambiente Python congelado. - Dependências bloqueadas via`poetry.lock`. - Uma imagem executável em qualquer sistema que suporte o Docker.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_integração_no_github_actions&amp;quot;&amp;gt;Integração no GitHub Actions&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;yaml&amp;quot;&amp;gt;name: Docker Build

on:
  push:
    branches: [main]

jobs:
  build-docker:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build Docker image
        run: docker build -t ghcr.io/${{ github.repository }}:latest .
      - name: Push Docker image
        run: docker push ghcr.io/${{ github.repository }}:latest&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_publicação_condicional&amp;quot;&amp;gt;Publicação condicional&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em um pipeline profissional, é preciso poder publicar apenas em alguns casos: - Release candidates para o Test PyPI. - Versões estáveis para o PyPI. - Dispara construções do Docker apenas para`main`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;yaml&amp;quot;&amp;gt;- name: Publish to Test PyPI
  if: contains(github.ref, &amp;#39;-rc&amp;#39;)
  run: poetry publish --build --repository test-pypi

- name: Publish to PyPI
  if: startsWith(github.ref, &amp;#39;refs/tags/v&amp;#39;)
  run: poetry publish --build&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta abordagem evita publicações acidentais.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_automatização_das_dependências_com_renovate_e_dependabot&amp;quot;&amp;gt;Automatização das dependências com Renovate e Dependabot&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para evitar que suas dependências se tornem obsoletas, integre ferramentas de atualização automática.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_dependabot&amp;quot;&amp;gt;Dependabot&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;yaml&amp;quot;&amp;gt;version: 2
updates:
  - package-ecosystem: &amp;quot;pip&amp;quot;
    directory: &amp;quot;/&amp;quot;
    schedule:
      interval: &amp;quot;weekly&amp;quot;
  - package-ecosystem: &amp;quot;github-actions&amp;quot;
    directory: &amp;quot;/&amp;quot;
    schedule:
      interval: &amp;quot;weekly&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Dependabot abre PR todas as semanas para atualizar as dependências do Python e do GitHub Actions.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_renovar&amp;quot;&amp;gt;renovar&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;json&amp;quot;&amp;gt;{
  &amp;quot;extends&amp;quot;: [&amp;quot;config:base&amp;quot;],
  &amp;quot;packageRules&amp;quot;: [
    {
      &amp;quot;matchManagers&amp;quot;: [&amp;quot;pip&amp;quot;],
      &amp;quot;groupName&amp;quot;: &amp;quot;python-dependencies&amp;quot;,
      &amp;quot;schedule&amp;quot;: [&amp;quot;before 6am on monday&amp;quot;]
    }
  ]
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Renovate permite um controle mais fino: agrupamento de dependências, planejamento e regras avançadas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_diagramas_plantuml&amp;quot;&amp;gt;Diagramas PlantUML&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_caso_de_uso_cicd_avançado&amp;quot;&amp;gt;Caso de Uso – CI/CD avançado&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;images/usecase-cicd.svg&amp;quot; alt=&amp;quot;usecase cicd&amp;quot; width=&amp;quot;819&amp;quot; height=&amp;quot;317&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_sequência_publicação_condicional&amp;quot;&amp;gt;Sequência – Publicação condicional&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;images/sequence-cicd.svg&amp;quot; alt=&amp;quot;sequence cicd&amp;quot; width=&amp;quot;275&amp;quot; height=&amp;quot;571&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estados_pipeline_cicd&amp;quot;&amp;gt;Estados – Pipeline CI/CD&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;images/states-cicd.svg&amp;quot; alt=&amp;quot;states cicd&amp;quot; width=&amp;quot;284&amp;quot; height=&amp;quot;608&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_implantação_arquitetura_cicd&amp;quot;&amp;gt;Implantação – Arquitetura CI/CD&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;images/deployment-cicd.svg&amp;quot; alt=&amp;quot;deployment cicd&amp;quot; width=&amp;quot;1587&amp;quot; height=&amp;quot;5604&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nesta terceira parte, vimos como : - Modernizar o empacotamento Python com Poetry. - Garantir builds reproduzíveis via Docker. Implementar uma publicação condicional. - Automatizar a atualização das dependências.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você agora tem um pipeline CI/CD&amp;lt;strong&amp;gt;completo, industrializado e seguro&amp;lt;/strong&amp;gt;, pronto para evoluir com seus projetos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Parte 2: Industrializar e proteger um pipeline avançado de CI/CD em Python</title>
            <link >https://cheroliv.com//blog/2025/0086_pypi_cicd_part_2_post.html</link>
            <pubDate>Fri, 18 Jul 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0086_pypi_cicd_part_2_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_do_pipeline_cicd&amp;quot;&amp;gt;Arquitetura do Pipeline CI/CD&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estrutura_do_projeto&amp;quot;&amp;gt;Estrutura do Projeto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configuração_do_pacote_com_pyproject_toml&amp;quot;&amp;gt;Configuração do Pacote com pyproject.toml&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_workflow_de_cicd_testes_e_qualidade&amp;quot;&amp;gt;Workflow de CI/CD - Testes e Qualidade&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_workflow_de_lançamento_e_implantação&amp;quot;&amp;gt;Workflow de Lançamento e Implantação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagramas_de_arquitetura&amp;quot;&amp;gt;Diagramas de Arquitetura&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_sequência_processo_de_lançamento&amp;quot;&amp;gt;Diagrama de Sequência - Processo de Lançamento&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_estados_ciclo_de_vida_do_pacote&amp;quot;&amp;gt;Diagrama de estados - ciclo de vida do pacote&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_implantação_infraestrutura_cicd&amp;quot;&amp;gt;Diagrama de Implantação - Infraestrutura CI/CD&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_objetos_e_modelos_do_pipeline&amp;quot;&amp;gt;Objetos e Modelos do Pipeline&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_classes_modelos_cicd&amp;quot;&amp;gt;Diagrama de Classes - Modelos CI/CD&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configuração_dos_segredos&amp;quot;&amp;gt;Configuração dos Segredos&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_segredos_github_actions&amp;quot;&amp;gt;Segredos GitHub Actions&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_geração_de_tokens_pypi&amp;quot;&amp;gt;Geração de Tokens PyPI&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_scripts_de_desenvolvimento_local&amp;quot;&amp;gt;Scripts de Desenvolvimento Local&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_makefile&amp;quot;&amp;gt;Makefile&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_script_de_versão&amp;quot;&amp;gt;Script de Versão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_boas_práticas_e_recomendações&amp;quot;&amp;gt;Boas práticas e recomendações&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_versionamento_semântico&amp;quot;&amp;gt;Versionamento Semântico&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estratégia_de_ramificação&amp;quot;&amp;gt;Estratégia de ramificação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_testes_e_cobertura&amp;quot;&amp;gt;Testes e Cobertura&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_segurança&amp;quot;&amp;gt;Segurança&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_monitoramento_e_observabilidade&amp;quot;&amp;gt;Monitoramento e Observabilidade&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_métricas_do_pipeline&amp;quot;&amp;gt;Métricas do Pipeline&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_notificações&amp;quot;&amp;gt;Notificações&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_recursos_complementares&amp;quot;&amp;gt;Recursos Complementares&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Objetivo : Transformar um pipeline simples em um pipeline industrial robusto, respeitando os padrões modernos de empacotamento e engenharia de software.&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Na primeira parte desta série, construímos um pipeline CI/CD simples mas eficaz para automatizar os testes e a publicação de um pacote Python no PyPI.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Agora é hora de profissionalizar este pipeline. Nesta segunda parte, vamos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Migrar para o padrão moderno pyproject.toml,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Adicionar ferramentas de qualidade e segurança (Black, Mypy, Bandit, Safety),&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Implementar testes multi-versão,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Integrar um lançamento gradual via Test PyPI,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Automatizar o versionamento e melhorar o monitoramento do pipeline.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Prepare-se para passar de um pipeline funcional para uma infraestrutura CI/CD profissional.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Implantar uma aplicação Python no PyPI requer um pipeline CI/CD robusto que automatiza os testes, a construção e a publicação dos pacotes. Este artigo detalha a implementação de um pipeline completo usando GitHub Actions para uma aplicação CLI Python, baseando-se nas boas práticas do ecossistema Python moderno.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_arquitetura_do_pipeline_cicd&amp;quot;&amp;gt;Arquitetura do Pipeline CI/CD&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O pipeline que vamos construir segue uma abordagem em várias etapas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;images/diag-plantuml-md5-bb11d40b5f9697204efc20606623b3ef.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;1065&amp;quot; height=&amp;quot;604&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_estrutura_do_projeto&amp;quot;&amp;gt;Estrutura do Projeto&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Uma aplicação CLI Python pronta para distribuição deve seguir uma estrutura padronizada :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;text&amp;quot;&amp;gt;playlist-downloader/
├── .github/
│   └── workflows/
│       ├── ci.yml
│       ├── release.yml
│       └── security.yml
├── src/
│   └── playlist_downloader/
│       ├── __init__.py
│       ├── cli.py
│       ├── core/
│       └── adapters/
├── tests/
│   ├── unit/
│   ├── integration/
│   └── conftest.py
├── docs/
├── pyproject.toml
├── requirements.txt
├── requirements-dev.txt
├── MANIFEST.in
├── README.md
├── LICENSE
└── CHANGELOG.md&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_configuração_do_pacote_com_pyproject_toml&amp;quot;&amp;gt;Configuração do Pacote com pyproject.toml&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O arquivo`pyproject.toml`é o padrão moderno para configurar pacotes Python :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;toml&amp;quot;&amp;gt;[build-system]
requires = [&amp;quot;setuptools&amp;amp;gt;=45&amp;quot;, &amp;quot;wheel&amp;quot;, &amp;quot;setuptools_scm&amp;amp;gt;=6.2&amp;quot;]
build-backend = &amp;quot;setuptools.build_meta&amp;quot;

[project]
name = &amp;quot;playlist-downloader&amp;quot;
authors = [
    {name = &amp;quot;Christophe Hérolivier&amp;quot;, email = &amp;quot;cheroliv@example.com&amp;quot;},
]
description = &amp;quot;CLI tool for YouTube playlist management&amp;quot;
readme = &amp;quot;README.md&amp;quot;
requires-python = &amp;quot;&amp;amp;gt;=3.8&amp;quot;
keywords = [&amp;quot;youtube&amp;quot;, &amp;quot;playlist&amp;quot;, &amp;quot;cli&amp;quot;, &amp;quot;downloader&amp;quot;]
license = {text = &amp;quot;MIT&amp;quot;}
classifiers = [
    &amp;quot;Development Status :: 4 - Beta&amp;quot;,
    &amp;quot;Environment :: Console&amp;quot;,
    &amp;quot;Intended Audience :: End Users/Desktop&amp;quot;,
    &amp;quot;License :: OSI Approved :: MIT License&amp;quot;,
    &amp;quot;Operating System :: OS Independent&amp;quot;,
    &amp;quot;Programming Language :: Python :: 3&amp;quot;,
    &amp;quot;Programming Language :: Python :: 3.8&amp;quot;,
    &amp;quot;Programming Language :: Python :: 3.9&amp;quot;,
    &amp;quot;Programming Language :: Python :: 3.10&amp;quot;,
    &amp;quot;Programming Language :: Python :: 3.11&amp;quot;,
    &amp;quot;Topic :: Multimedia :: Sound/Audio&amp;quot;,
    &amp;quot;Topic :: Utilities&amp;quot;,
]
dependencies = [
    &amp;quot;typer&amp;amp;gt;=0.9.0&amp;quot;,
    &amp;quot;yt-dlp&amp;amp;gt;=2023.7.6&amp;quot;,
    &amp;quot;google-api-python-client&amp;amp;gt;=2.0.0&amp;quot;,
    &amp;quot;google-auth-oauthlib&amp;amp;gt;=1.0.0&amp;quot;,
    &amp;quot;pyyaml&amp;amp;gt;=6.0&amp;quot;,
    &amp;quot;rich&amp;amp;gt;=13.0.0&amp;quot;,
]
dynamic = [&amp;quot;version&amp;quot;]

[project.optional-dependencies]
dev = [
    &amp;quot;pytest&amp;amp;gt;=7.0.0&amp;quot;,
    &amp;quot;pytest-cov&amp;amp;gt;=4.0.0&amp;quot;,
    &amp;quot;pytest-mock&amp;amp;gt;=3.10.0&amp;quot;,
    &amp;quot;black&amp;amp;gt;=23.0.0&amp;quot;,
    &amp;quot;flake8&amp;amp;gt;=6.0.0&amp;quot;,
    &amp;quot;mypy&amp;amp;gt;=1.0.0&amp;quot;,
    &amp;quot;pre-commit&amp;amp;gt;=3.0.0&amp;quot;,
    &amp;quot;tox&amp;amp;gt;=4.0.0&amp;quot;,
]
test = [
    &amp;quot;pytest&amp;amp;gt;=7.0.0&amp;quot;,
    &amp;quot;pytest-cov&amp;amp;gt;=4.0.0&amp;quot;,
    &amp;quot;pytest-mock&amp;amp;gt;=3.10.0&amp;quot;,
]

[project.urls]
Homepage = &amp;quot;https://github.com/cheroliv/playlist-downloader&amp;quot;
Documentation = &amp;quot;https://github.com/cheroliv/playlist-downloader#readme&amp;quot;
Repository = &amp;quot;https://github.com/cheroliv/playlist-downloader.git&amp;quot;
&amp;quot;Bug Tracker&amp;quot; = &amp;quot;https://github.com/cheroliv/playlist-downloader/issues&amp;quot;

[project.scripts]
playlist-downloader = &amp;quot;playlist_downloader.cli:main&amp;quot;

[tool.setuptools_scm]
write_to = &amp;quot;src/playlist_downloader/_version.py&amp;quot;

[tool.setuptools.packages.find]
where = [&amp;quot;src&amp;quot;]

[tool.pytest.ini_options]
testpaths = [&amp;quot;tests&amp;quot;]
python_files = [&amp;quot;test_*.py&amp;quot;]
python_classes = [&amp;quot;Test*&amp;quot;]
python_functions = [&amp;quot;test_*&amp;quot;]
addopts = [
    &amp;quot;--cov=src/playlist_downloader&amp;quot;,
    &amp;quot;--cov-report=html&amp;quot;,
    &amp;quot;--cov-report=term-missing&amp;quot;,
    &amp;quot;--cov-fail-under=85&amp;quot;,
]

[tool.black]
line-length = 88
target-version = [&amp;#39;py38&amp;#39;]
include = &amp;#39;\.pyi?$&amp;#39;
extend-exclude = &amp;#39;&amp;#39;&amp;#39;
/(
  \.eggs
  | \.git
  | \.hg
  | \.mypy_cache
  | \.tox
  | \.venv
  | _build
  | buck-out
  | build
  | dist
)/
&amp;#39;&amp;#39;&amp;#39;

[tool.mypy]
python_version = &amp;quot;3.8&amp;quot;
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
warn_unreachable = true
strict_equality = true

[[tool.mypy.overrides]]
module = [
    &amp;quot;yt_dlp.*&amp;quot;,
    &amp;quot;googleapiclient.*&amp;quot;,
    &amp;quot;google_auth_oauthlib.*&amp;quot;,
]
ignore_missing_imports = true&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_workflow_de_cicd_testes_e_qualidade&amp;quot;&amp;gt;Workflow de CI/CD - Testes e Qualidade&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O fluxo de trabalho principal`ci.yml`) executa os testes em várias versões do Python :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;yaml&amp;quot;&amp;gt;name: CI

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [&amp;quot;3.8&amp;quot;, &amp;quot;3.9&amp;quot;, &amp;quot;3.10&amp;quot;, &amp;quot;3.11&amp;quot;]

    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}

    - name: Cache dependencies
      uses: actions/cache@v3
      with:
        path: |
          ~/.cache/pip
          ~/.cache/pre-commit
        key: ${{ runner.os }}-pip-${{ hashFiles(&amp;#39;**/requirements*.txt&amp;#39;) }}
        restore-keys: |
          ${{ runner.os }}-pip-

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -e &amp;quot;.[dev]&amp;quot;

    - name: Lint with flake8
      run: |
        flake8 src tests --count --select=E9,F63,F7,F82 --show-source --statistics
        flake8 src tests --count --exit-zero --max-complexity=10 --max-line-length=88 --statistics

    - name: Check code formatting with Black
      run: black --check src tests

    - name: Type checking with mypy
      run: mypy src

    - name: Run tests with pytest
      run: |
        pytest tests/ -v --cov=src/playlist_downloader \
          --cov-report=xml --cov-report=term-missing

    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3
      if: matrix.python-version == &amp;#39;3.11&amp;#39;
      with:
        file: ./coverage.xml
        flags: unittests
        name: codecov-umbrella

  security:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: &amp;quot;3.11&amp;quot;

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install bandit[toml] safety

    - name: Run security checks with bandit
      run: bandit -r src/ -f json -o bandit-report.json

    - name: Check dependencies with safety
      run: safety check --json --output safety-report.json

    - name: Upload security reports
      uses: actions/upload-artifact@v3
      if: always()
      with:
        name: security-reports
        path: |
          bandit-report.json
          safety-report.json

  build:
    needs: [test, security]
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: &amp;quot;3.11&amp;quot;

    - name: Install build dependencies
      run: |
        python -m pip install --upgrade pip
        pip install build twine

    - name: Build package
      run: python -m build

    - name: Check package with twine
      run: twine check dist/*

    - name: Upload build artifacts
      uses: actions/upload-artifact@v3
      with:
        name: dist
        path: dist/&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_workflow_de_lançamento_e_implantação&amp;quot;&amp;gt;Workflow de Lançamento e Implantação&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O workflow de release (&amp;lt;code&amp;gt;release.yml&amp;lt;/code&amp;gt;) gerencia a publicação automática no PyPI :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;yaml&amp;quot;&amp;gt;name: Release

on:
  push:
    tags:
      - &amp;#39;v*.*.*&amp;#39;
  workflow_dispatch:
    inputs:
      environment:
        description: &amp;#39;Deployment environment&amp;#39;
        required: true
        default: &amp;#39;test&amp;#39;
        type: choice
        options:
        - test
        - production

env:
  PYTHON_VERSION: &amp;quot;3.11&amp;quot;

jobs:
  release:
    runs-on: ubuntu-latest
    environment:
      name: ${{ github.event.inputs.environment || (startsWith(github.ref, &amp;#39;refs/tags/&amp;#39;) &amp;amp;amp;&amp;amp;amp; &amp;#39;production&amp;#39; || &amp;#39;test&amp;#39;) }}

    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: ${{ env.PYTHON_VERSION }}

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install build twine

    - name: Build package
      run: python -m build

    - name: Check package
      run: twine check dist/*

    - name: Publish to Test PyPI
      if: github.event.inputs.environment == &amp;#39;test&amp;#39; || (startsWith(github.ref, &amp;#39;refs/tags/&amp;#39;) &amp;amp;amp;&amp;amp;amp; contains(github.ref, &amp;#39;rc&amp;#39;))
      env:
        TWINE_USERNAME: __token__
        TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
      run: |
        twine upload --repository testpypi dist/*

    - name: Publish to PyPI
      if: github.event.inputs.environment == &amp;#39;production&amp;#39; || (startsWith(github.ref, &amp;#39;refs/tags/&amp;#39;) &amp;amp;amp;&amp;amp;amp; !contains(github.ref, &amp;#39;rc&amp;#39;))
      env:
        TWINE_USERNAME: __token__
        TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
      run: |
        twine upload dist/*

    - name: Create GitHub Release
      if: startsWith(github.ref, &amp;#39;refs/tags/&amp;#39;)
      uses: actions/create-release@v1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        tag_name: ${{ github.ref }}
        release_name: Release ${{ github.ref }}
        draft: false
        prerelease: ${{ contains(github.ref, &amp;#39;rc&amp;#39;) }}

  post-release:
    needs: release
    runs-on: ubuntu-latest
    if: startsWith(github.ref, &amp;#39;refs/tags/&amp;#39;)

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: ${{ env.PYTHON_VERSION }}

    - name: Test installation from PyPI
      run: |
        sleep 60  # Attendre la propagation sur PyPI
        pip install playlist-downloader
        playlist-downloader --version

    - name: Update documentation
      run: |
        # Script pour mettre à jour la documentation
        echo &amp;quot;Documentation updated for version ${GITHUB_REF#refs/tags/}&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_diagramas_de_arquitetura&amp;quot;&amp;gt;Diagramas de Arquitetura&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_de_sequência_processo_de_lançamento&amp;quot;&amp;gt;Diagrama de Sequência - Processo de Lançamento&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;images/diag-plantuml-md5-f01a09414cc7d19d59813f2a5bed8b09.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;968&amp;quot; height=&amp;quot;832&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_de_estados_ciclo_de_vida_do_pacote&amp;quot;&amp;gt;Diagrama de estados - ciclo de vida do pacote&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;images/diag-plantuml-md5-d2c05f6df902032b6369e62a5f189875.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;525&amp;quot; height=&amp;quot;740&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_de_implantação_infraestrutura_cicd&amp;quot;&amp;gt;Diagrama de Implantação - Infraestrutura CI/CD&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;images/diag-plantuml-md5-a9f28ad3f4c86f1d4598d2a2170bbd87.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;734&amp;quot; height=&amp;quot;604&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_objetos_e_modelos_do_pipeline&amp;quot;&amp;gt;Objetos e Modelos do Pipeline&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_diagrama_de_classes_modelos_cicd&amp;quot;&amp;gt;Diagrama de Classes - Modelos CI/CD&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;images/diag-plantuml-md5-afca7b13fea2484559ca99543b4a540a.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;814&amp;quot; height=&amp;quot;635&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_configuração_dos_segredos&amp;quot;&amp;gt;Configuração dos Segredos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para que o pipeline funcione, você deve configurar os seguintes segredos no GitHub&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_segredos_github_actions&amp;quot;&amp;gt;Segredos GitHub Actions&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Dans Settings &amp;amp;gt; Secrets and variables &amp;amp;gt; Actions

# Token PyPI pour la production
PYPI_API_TOKEN=pypi-...

# Token Test PyPI pour les pré-releases
TEST_PYPI_API_TOKEN=pypi-...

# Token GitHub pour créer les releases
GITHUB_TOKEN=(automatiquement fourni)

# Token Codecov (optionnel)
CODECOV_TOKEN=...&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_geração_de_tokens_pypi&amp;quot;&amp;gt;Geração de Tokens PyPI&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;bash&amp;quot;&amp;gt;# 1. Créer un compte sur PyPI et Test PyPI
# 2. Aller dans Account Settings &amp;amp;gt; API tokens
# 3. Créer un token avec scope &amp;quot;Entire account&amp;quot; ou spécifique au projet
# 4. Format du token : pypi-AgEIcHlwaS5vcmc...&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_scripts_de_desenvolvimento_local&amp;quot;&amp;gt;Scripts de Desenvolvimento Local&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para facilitar o desenvolvimento, crie scripts utilitários:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_makefile&amp;quot;&amp;gt;Makefile&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;makefile&amp;quot;&amp;gt;.PHONY: install test lint format security build clean release-test release-prod

install:
	pip install -e &amp;quot;.[dev]&amp;quot;

test:
	pytest tests/ -v --cov=src/playlist_downloader

lint:
	flake8 src tests
	mypy src

format:
	black src tests

security:
	bandit -r src/
	safety check

build:
	python -m build
	twine check dist/*

clean:
	rm -rf build/ dist/ *.egg-info/
	find . -type d -name __pycache__ -delete
	find . -name &amp;quot;*.pyc&amp;quot; -delete

release-test: clean build
	twine upload --repository testpypi dist/*

release-prod: clean build
	twine upload dist/*

pre-commit: format lint test security
	@echo &amp;quot;✅ Prêt pour commit&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_script_de_versão&amp;quot;&amp;gt;Script de Versão&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;python&amp;quot;&amp;gt;#!/usr/bin/env python3
&amp;quot;&amp;quot;&amp;quot;Script pour gérer les versions du projet.&amp;quot;&amp;quot;&amp;quot;

import sys
import subprocess
from pathlib import Path

def get_current_version():
    &amp;quot;&amp;quot;&amp;quot;Récupère la version actuelle depuis git.&amp;quot;&amp;quot;&amp;quot;
    try:
        result = subprocess.run(
            [&amp;quot;git&amp;quot;, &amp;quot;describe&amp;quot;, &amp;quot;--tags&amp;quot;, &amp;quot;--abbrev=0&amp;quot;],
            capture_output=True,
            text=True,
            check=True
        )
        return result.stdout.strip()
    except subprocess.CalledProcessError:
        return &amp;quot;0.0.0&amp;quot;

def create_version_tag(version, message=None):
    &amp;quot;&amp;quot;&amp;quot;Crée un tag de version.&amp;quot;&amp;quot;&amp;quot;
    if not version.startswith(&amp;#39;v&amp;#39;):
        version = f&amp;#39;v{version}&amp;#39;

    tag_message = message or f&amp;quot;Release {version}&amp;quot;

    subprocess.run([&amp;quot;git&amp;quot;, &amp;quot;tag&amp;quot;, &amp;quot;-a&amp;quot;, version, &amp;quot;-m&amp;quot;, tag_message], check=True)
    print(f&amp;quot;✅ Tag {version} créé&amp;quot;)

    # Push le tag
    subprocess.run([&amp;quot;git&amp;quot;, &amp;quot;push&amp;quot;, &amp;quot;origin&amp;quot;, version], check=True)
    print(f&amp;quot;✅ Tag {version} poussé vers origin&amp;quot;)

if __name__ == &amp;quot;__main__&amp;quot;:
    if len(sys.argv) &amp;amp;lt; 2:
        current = get_current_version()
        print(f&amp;quot;Version actuelle: {current}&amp;quot;)
        print(&amp;quot;Usage: python version.py &amp;amp;lt;new_version&amp;amp;gt; [message]&amp;quot;)
        sys.exit(1)

    new_version = sys.argv[1]
    message = sys.argv[2] if len(sys.argv) &amp;amp;gt; 2 else None

    create_version_tag(new_version, message)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_boas_práticas_e_recomendações&amp;quot;&amp;gt;Boas práticas e recomendações&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_versionamento_semântico&amp;quot;&amp;gt;Versionamento Semântico&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Utilize o versionamento semântico (SemVer):&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;MAJOR.MINOR.PATCH&amp;lt;/code&amp;gt; (ex: 1.2.3)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;MAJOR&amp;lt;/code&amp;gt;: alterações incompatíveis&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;`MINOR`novas funcionalidades compatíveis&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;PATCH&amp;lt;/code&amp;gt;: correções de bugs compatíveis&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estratégia_de_ramificação&amp;quot;&amp;gt;Estratégia de ramificação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;text&amp;quot;&amp;gt;main          ──●──●──●──●──●────●── (releases stables)
               /       /          /
develop    ──●──●──●──●──●──●──●──●── (développement)
            /     /        /
feature/xxx  ●──●──●──●──●──/ (fonctionnalités)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_testes_e_cobertura&amp;quot;&amp;gt;Testes e Cobertura&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Cobertura mínima de código: 85%&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Testes unitários para a lógica de negócio&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Testes de integração para os adaptadores&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Testes de ponta a ponta para as CLI&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_segurança&amp;quot;&amp;gt;Segurança&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Escaneamento automático de dependências (Safety)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Análise estática do código (Bandit)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Segredos nunca no código&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Utilização de tokens PyPI específicos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_monitoramento_e_observabilidade&amp;quot;&amp;gt;Monitoramento e Observabilidade&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_métricas_do_pipeline&amp;quot;&amp;gt;Métricas do Pipeline&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;yaml&amp;quot;&amp;gt;# .github/workflows/metrics.yml
name: Pipeline Metrics

on:
  workflow_run:
    workflows: [&amp;quot;CI&amp;quot;, &amp;quot;Release&amp;quot;]
    types: [completed]

jobs:
  metrics:
    runs-on: ubuntu-latest
    steps:
    - name: Collect metrics
      run: |
        echo &amp;quot;Pipeline: ${{ github.event.workflow_run.name }}&amp;quot;
        echo &amp;quot;Status: ${{ github.event.workflow_run.conclusion }}&amp;quot;
        echo &amp;quot;Duration: ${{ github.event.workflow_run.updated_at - github.event.workflow_run.created_at }}&amp;quot;
        # Envoyer vers système de monitoring&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_notificações&amp;quot;&amp;gt;Notificações&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;yaml&amp;quot;&amp;gt;# Ajout dans les workflows pour notifications
- name: Notify on failure
  if: failure()
  uses: 8398a7/action-slack@v3
  with:
    status: failure
    text: &amp;quot;❌ Pipeline failed for ${{ github.repository }}&amp;quot;
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este pipeline CI/CD completo para Python oferece :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Automação completa&amp;lt;/strong&amp;gt;: da validação do código à publicação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Segurança&amp;lt;/strong&amp;gt;: digitalizações automáticas e gestão segura dos segredos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Qualidade&amp;lt;/strong&amp;gt;testes multi-versões, linting e cobertura de código&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fiabilidade&amp;lt;/strong&amp;gt;implementação gradual via Test PyPI&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Traçabilidade&amp;lt;/strong&amp;gt;: artefatos, relatórios e lançamentos GitHub&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A adoção dessas práticas garante um processo de entrega robusto e profissional para suas aplicações Python CLI, facilitando a manutenção e a evolução de seus projetos a longo prazo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_recursos_complementares&amp;quot;&amp;gt;Recursos Complementares&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://packaging.python.org/&amp;quot;&amp;gt;Python Packaging User Guide&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.github.com/en/actions&amp;quot;&amp;gt;Documentação do GitHub Actions&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://pypi.org/help/&amp;quot;&amp;gt;Documentação de Ajuda do PyPI&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://semver.org/&amp;quot;&amp;gt;Especificação de Versionamento Semântico&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://keepachangelog.com/&amp;quot;&amp;gt;Mantenha um changelog&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Parte 1: Configurar um pipeline CI/CD simples para Python e PyPI</title>
            <link >https://cheroliv.com//blog/2025/0085_pypi_cicd_part_1_post.html</link>
            <pubDate>Thu, 17 Jul 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0085_pypi_cicd_part_1_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_do_pipeline&amp;quot;&amp;gt;Arquitetura do Pipeline&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configuração_do_pipeline_de_integração_contínua_ci&amp;quot;&amp;gt;Configuração do Pipeline de Integração Contínua (CI)&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estrutura_do_workflow_ci&amp;quot;&amp;gt;Estrutura do workflow CI&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_análise_das_etapas_ci&amp;quot;&amp;gt;Análise das Etapas CI&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_gatilhos_on&amp;quot;&amp;gt;Gatilhos (&amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt;)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Objetivo : Acompanhar o leitor na implementação de um pipeline CI/CD minimalista, mas funcional, para uma aplicação Python, com implantação automática no PyPI.&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;A automação dos processos de desenvolvimento tornou-se indispensável nos projetos modernos. Um pipeline CI/CD bem projetado permite não apenas detectar as regressões cedo no ciclo de desenvolvimento, mas também automatizar totalmente o processo de implantação.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Neste artigo, vamos explorar como configurar um pipeline completo com GitHub Actions para uma aplicação Python, da integração contínua (CI) à implantação contínua (CD) no PyPI.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_arquitetura_do_pipeline&amp;quot;&amp;gt;Arquitetura do Pipeline&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nosso pipeline consiste em dois fluxos de trabalho distintos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Pipeline CI&amp;lt;/strong&amp;gt;: executado em cada push e pull request&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Pipeline CD&amp;lt;/strong&amp;gt;Acionado apenas nas releases do GitHub&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;images/ci-cd-overview.svg&amp;quot; alt=&amp;quot;ci cd overview&amp;quot; width=&amp;quot;1601&amp;quot; height=&amp;quot;5132&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_configuração_do_pipeline_de_integração_contínua_ci&amp;quot;&amp;gt;Configuração do Pipeline de Integração Contínua (CI)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estrutura_do_workflow_ci&amp;quot;&amp;gt;Estrutura do workflow CI&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O fluxo de trabalho CI é projetado para validar cada contribuição ao código. Eis a sua configuração completa :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;yaml&amp;quot;&amp;gt;name: CI/CD Pipeline

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: &amp;#39;3.x&amp;#39;

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install ruff pytest pytest-mock

    - name: Run Linting (Ruff)
      run: ruff check .

    - name: Run Tests (Pytest)
      run: pytest&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_análise_das_etapas_ci&amp;quot;&amp;gt;Análise das Etapas CI&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_gatilhos_on&amp;quot;&amp;gt;Gatilhos (&amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt;)&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;pygments highlight&amp;quot;&amp;gt;&amp;lt;code data-lang=&amp;quot;yaml&amp;quot;&amp;gt;----
----
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
----

O pipeline é disparado em: - Cada push na branch`main` - Cada pull request para`main`

Esta abordagem garante que o código principal permaneça estável e que toda contribuição seja validada antes da integração.

==== 2. Ambiente de Execução

----

runs-on: ubuntu-latest

Ubuntu Latest oferece um bom equilíbrio entre desempenho, custo e compatibilidade para a maioria dos projetos Python.

==== 3. Checkout do Código

```yaml
----
----
- name: Checkout code
  uses: actions/checkout@v4
----

A ação`checkout@v4`Recupera o código-fonte do repositório. A versão v4 traz melhorias de desempenho e de segurança.

==== 4. Configuração Python

----

- name: Set up Python uses: actions/setup-python@v5 with: python-version: &amp;#39;3.x&amp;#39;

O uso de`&amp;#39;3.x&amp;#39;`permite usar automaticamente a última versão estável do Python 3, simplificando a manutenção.

==== 5. Instalação das Dependências

```yaml
----
----
- name: Install dependencies
  run: |
    python -m pip install --upgrade pip
    pip install -r requirements.txt
    pip install ruff pytest pytest-mock
----

Esta etapa: - Atualiza o pip para a última versão - Instala as dependências do projeto - Adiciona as ferramentas de desenvolvimento (linting e testes)

==== 6. Linting com Ruff

----

- name: Run Linting (Ruff) run: ruff check .

**Ruff**é um linter Python ultra-rápido escrito em Rust. Ele combina as funcionalidades de várias ferramentas (Flake8, Black, isort) em uma única ferramenta performante.

==== 7. Execução dos Testes

```yaml
----
----
- name: Run Tests (Pytest)
  run: pytest
----

O Pytest executa todo o conjunto de testes, garantindo que as alterações não introduzam regressões.

== Configuração do Pipeline de Implantação (CD)

=== Estrutura do Workflow CD

O workflow CD é acionado apenas durante os releases do GitHub e automatiza a publicação no PyPI:

[source,yaml]
----
name: Publish to PyPI

on:
  release:
    types:
      - published

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: &amp;#39;3.x&amp;#39;

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install setuptools wheel twine

    - name: Build and publish
      env:
        TWINE_USERNAME: __token__
        TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
      run: |
        python setup.py sdist bdist_wheel
        twine upload dist/*
----

=== Análise das Etapas CD

==== 1. Gatilho Release

----

on: release: types: - published

O pipeline CD é acionado apenas quando uma release do GitHub é publicada. Essa abordagem garante um controle preciso das implantações.

==== 2. Instalação das Ferramentas de Build

```yaml
----
----
pip install setuptools wheel twine
----

- **setuptools**: Ferramentas de empacotamento Python - **roda**: Formato de distribuição Python moderno - **barbante**Ferramenta segura para fazer upload para o PyPI

==== 3. Configuração da Autenticação

----

env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}

A autenticação utiliza um token de API PyPI armazenado como segredo GitHub, mais seguro que as credenciais tradicionais.

==== 4. Build e Publicação

```yaml
----
----
run: |
  python setup.py sdist bdist_wheel
  twine upload dist/*
----

- `sdist`: Crie uma distribuição fonte - `bdist_wheel`Cria uma wheel (distribuição binária) - `twine upload`Publica as distribuições no PyPI

== Configuração do Pacote Python

=== Estrutura do setup.py

Para que o pipeline funcione, seu projeto deve incluir um arquivo`setup.py`:

[source,python]
----
from setuptools import setup, find_packages

with open(&amp;quot;README.adoc&amp;quot;, &amp;quot;r&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as fh:
    long_description = fh.read()

setup(
    name=&amp;quot;playlist-downloader&amp;quot;,
    version=&amp;quot;1.0.0&amp;quot;,
    author=&amp;quot;Votre Nom&amp;quot;,
    author_email=&amp;quot;votre.email@example.com&amp;quot;,
    description=&amp;quot;CLI tool for managing YouTube playlists&amp;quot;,
    long_description=long_description,
    long_description_content_type=&amp;quot;text/plain&amp;quot;,
    url=&amp;quot;https://github.com/cheroliv/playlist-downloader&amp;quot;,
    packages=find_packages(),
    classifiers=[
        &amp;quot;Development Status :: 4 - Beta&amp;quot;,
        &amp;quot;Intended Audience :: Developers&amp;quot;,
        &amp;quot;License :: OSI Approved :: MIT License&amp;quot;,
        &amp;quot;Operating System :: OS Independent&amp;quot;,
        &amp;quot;Programming Language :: Python :: 3&amp;quot;,
        &amp;quot;Programming Language :: Python :: 3.8&amp;quot;,
        &amp;quot;Programming Language :: Python :: 3.9&amp;quot;,
        &amp;quot;Programming Language :: Python :: 3.10&amp;quot;,
        &amp;quot;Programming Language :: Python :: 3.11&amp;quot;,
    ],
    python_requires=&amp;quot;&amp;amp;gt;=3.8&amp;quot;,
    install_requires=[
        &amp;quot;typer&amp;amp;gt;=0.9.0&amp;quot;,
        &amp;quot;yt-dlp&amp;amp;gt;=2023.1.6&amp;quot;,
        &amp;quot;google-api-python-client&amp;amp;gt;=2.70.0&amp;quot;,
        &amp;quot;google-auth-oauthlib&amp;amp;gt;=0.7.1&amp;quot;,
        &amp;quot;pymonad&amp;amp;gt;=2.4.0&amp;quot;,
        &amp;quot;pyyaml&amp;amp;gt;=6.0&amp;quot;,
    ],
    entry_points={
        &amp;quot;console_scripts&amp;quot;: [
            &amp;quot;playlist-downloader=cli:app&amp;quot;,
        ],
    },
)
----

=== Pontos-chave do setup.py

1. **Metadados**: Nome, versão, autor, descrição
2. **Dependências**Lista de pacotes necessários
3. **Pontos de entrada**: Comandos CLI expostos
4. **Classificadores**: Metadados para PyPI

== Segurança com os GitHub Secrets

=== Configuração do Token PyPI

1. **Criar um token API no PyPI** :

- Conecte-se ao PyPI - Vá para Account Settings &amp;amp;gt; API tokens - Crie um novo token com as permissões adequadas

1. **Adicionar o segredo no GitHub**:

- Configurações do repositório &amp;amp;gt; Segredos e variáveis &amp;amp;gt; Ações - Crie um novo segredo chamado`PYPI_API_TOKEN` - Cole seu token PyPI

[plantuml, secrets-flow, svg]
----
@startuml
!theme plain

actor Developer as dev
participant &amp;quot;Repositório GitHub&amp;quot; as repo
participant &amp;quot;GitHub Actions&amp;quot; as actions
participant PyPI

dev -&amp;amp;gt; repo : Configure PYPI_API_TOKEN secret
repo -&amp;amp;gt; actions : Trigger CD pipeline on release
actions -&amp;amp;gt; actions : Access secret securely
actions -&amp;amp;gt; PyPI : Authenticate with token
PyPI -&amp;amp;gt; PyPI : Validate and publish package

note over actions, PyPI
  Token never exposed in logs
  Automatic rotation possible
end note

@enduml
----

== Workflow de Implantação Completa

=== Sequência de Implantação

[plantuml, deployment-sequence, svg]
----
@startuml
!theme plain

actor Developer as dev
participant &amp;quot;Git local&amp;quot; as git
participant &amp;quot;GitHub&amp;quot; as github
participant &amp;quot;GitHub Actions&amp;quot; as actions
participant PyPI
participant &amp;quot;Utilizadores finais&amp;quot; as users

dev -&amp;amp;gt; git : git tag v1.0.0
dev -&amp;amp;gt; git : git push origin v1.0.0
git -&amp;amp;gt; github : Push tag
dev -&amp;amp;gt; github : Create release from tag
github -&amp;amp;gt; actions : Trigger CD pipeline
actions -&amp;amp;gt; actions : Checkout code
actions -&amp;amp;gt; actions : Setup Python environment
actions -&amp;amp;gt; actions : Install build tools
actions -&amp;amp;gt; actions : Build distributions (sdist + wheel)
actions -&amp;amp;gt; PyPI : Upload to PyPI with token
PyPI -&amp;amp;gt; PyPI : Validate and publish
users -&amp;amp;gt; PyPI : pip install playlist-downloader

note over dev, github
  Release creation can be automated
  or done manually through GitHub UI
end note

@enduml
----

=== Estados do Pipeline

[plantuml, pipeline-states, svg]
----
@startuml
!theme plain

[*] --&amp;amp;gt; Idle

Idle --&amp;amp;gt; CI_Running : Push/PR created
CI_Running --&amp;amp;gt; CI_Success : All checks pass
CI_Running --&amp;amp;gt; CI_Failed : Linting/Tests fail
CI_Success --&amp;amp;gt; Idle : Merge completed
CI_Failed --&amp;amp;gt; Idle : Fix and retry

Idle --&amp;amp;gt; CD_Running : Release published
CD_Running --&amp;amp;gt; CD_Success : Package published
CD_Running --&amp;amp;gt; CD_Failed : Build/Upload error
CD_Success --&amp;amp;gt; Idle : Package available on PyPI
CD_Failed --&amp;amp;gt; Idle : Fix and retry release

note on link #red : Blocks merge
note on link #green : Allows deployment

@enduml
----

== Boas Práticas e Otimizações

=== Gestão de Versões

Use tags Git semânticos:

----

git tag -a v1.2.3 -m &amp;quot;Release version 1.2.3&amp;quot; git push origin v1.2.3

=== 2. Testes de matriz

Para testar em várias versões do Python :

```yaml
----
----
strategy:
  matrix:
    python-version: [3.8, 3.9, &amp;quot;3.10&amp;quot;, &amp;quot;3.11&amp;quot;]
----

=== 3. Cache de dependências

Acelere os builds com o cache:

----

- name: Cache pip dependencies uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles(&amp;#39;**/requirements.txt&amp;#39;) }}

=== 4. Ambientes de Implantação

Use os ambientes GitHub para implantações controladas:

```yaml
----
----
jobs:
  deploy:
    environment: production
    runs-on: ubuntu-latest
----

== Caso de Uso e Arquitetura

=== Diagrama de Casos de Uso

[plantuml, use-cases, svg]
----
@startuml
!theme plain

left to right direction

actor &amp;quot;desenvolvedor&amp;quot; as dev
actor &amp;quot;Ações do GitHub&amp;quot; as ga
actor &amp;quot;Usuário Final&amp;quot; as user

package &amp;quot;Sistema CI/CD&amp;quot; {
  usecase &amp;quot;Executar Linting&amp;quot; as lint
  usecase &amp;quot;Executar testes&amp;quot; as test
  usecase &amp;quot;Construir Pacote&amp;quot; as build
  usecase &amp;quot;Publicar no PyPI&amp;quot; as publish
  usecase &amp;quot;Criar Lançamento&amp;quot; as release
}

dev --&amp;amp;gt; lint : Pushes code
dev --&amp;amp;gt; test : Pushes code
dev --&amp;amp;gt; release : Creates release
ga --&amp;amp;gt; build : On release trigger
ga --&amp;amp;gt; publish : After successful build
user --&amp;amp;gt; publish : Downloads package

lint .&amp;amp;gt; test : triggers
test .&amp;amp;gt; build : on success
build .&amp;amp;gt; publish : on success

@enduml
----

=== Arquitetura de Implantação

[plantuml, deployment-architecture, svg]
----
@startuml
!theme plain

cloud &amp;quot;GitHub&amp;quot; {
  [Source Repository]
  [GitHub Actions]
  [Secrets Store]
}

cloud &amp;quot;PyPI&amp;quot; {
  [Package Registry]
  [Distribution Files]
}

node &amp;quot;pipeline CI/CD&amp;quot; {
  [Linting]
  [Testing]
  [Building]
  [Publishing]
}

[Source Repository] --&amp;amp;gt; [GitHub Actions] : Triggers
[GitHub Actions] --&amp;amp;gt; [Linting]
[Linting] --&amp;amp;gt; [Testing]
[Testing] --&amp;amp;gt; [Building]
[Building] --&amp;amp;gt; [Publishing]
[Publishing] --&amp;amp;gt; [Package Registry] : Uploads
[Secrets Store] --&amp;amp;gt; [Publishing] : Provides token

note as N1
  Secure token-based
  authentication
end note

[Secrets Store] .. N1

@enduml
----

== Monitorização e Depuração

=== Logs e Monitoring

GitHub Actions fornece logs detalhados para cada etapa. Para depurar :

1. **Examine os logs**de cada step
2. **Ative o debug**com`ACTIONS_STEP_DEBUG`
3. **Use artefatos**para salvar os arquivos de build

----

- name: Upload build artifacts uses: actions/upload-artifact@v3 if: failure() with: name: build-logs path: build/

=== Notificações

Adicione notificações do Slack ou e‑mail :

```yaml
----
----
- name: Notify on failure
  if: failure()
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    webhook_url: ${{ secrets.SLACK_WEBHOOK }}
----

== Conclusão

A implementação de um pipeline CI/CD robusto com GitHub Actions transforma radicalmente a experiência de desenvolvimento. Ao automatizar o linting, os testes e a implantação, você :

- **Reduza os erros**em produção - **Acelere os ciclos**de desenvolvimento - **Melhore a confiança**em seus releases - **Facilite a colaboração**em equipe

Este pipeline pode ser adaptado a diferentes tipos de projetos Python ajustando as ferramentas de linting, os frameworks de teste ou os destinos de implantação.

O investimento inicial na configuração desses fluxos de trabalho é rapidamente amortizado pelo ganho de tempo e pela redução de erros manuais durante as implantações.

== Recursos complementares

- https://docs.github.com/en/actions[Documentação GitHub Actions] - https://packaging.python.org/[Guia de Empacotamento do Python] - https://docs.pytest.org/[Documentação Pytest] - https://docs.astral.sh/ruff/[Documentação Ruff] - https://twine.readthedocs.io/[Documentação Twine]

✅ Pipeline funcional alcançado! Você agora tem um pipeline CI/CD simples que permite automatizar seus testes e publicar seu pacote Python no PyPI diretamente do GitHub Actions.

No entanto, este pipeline permanece intencionalmente minimalista. Ainda não cobre alguns aspectos essenciais em um contexto profissional :

Testes multi-versões do Python,

Análise automática de segurança,

Implantação progressiva via Test PyPI,

Monitoramento e métricas do pipeline,

Automatização do versioning e integração das boas práticas modernas (pyproject.toml).

Na próxima parte, vamos passar para o próximo nível. Você aprenderá a transformar esse pipeline básico em uma verdadeira cadeia de implantação industrial, robusta e segura, pronta para projetos Python de produção.
----&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Desenvolvimento de um Bot Discord Musical: Arquitetura Funcional e Desenvolvimento Orientado por Logs</title>
            <link >https://cheroliv.com//blog/2025/0084_disco_bot_discord_python_post.html</link>
            <pubDate>Wed, 16 Jul 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0084_disco_bot_discord_python_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table of Contents&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_contexto_e_desafios_atuais&amp;quot;&amp;gt;Contexto e Desafios Atuais&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_evolução_das_apis_musicais&amp;quot;&amp;gt;Evolução das APIs Musicais&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_abordagem_de_desenvolvimento_orientado_por_logs&amp;quot;&amp;gt;Abordagem de desenvolvimento orientado por logs&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_conceitual&amp;quot;&amp;gt;Arquitetura Conceitual&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_pilha_técnica_e_paradigma_funcional&amp;quot;&amp;gt;Pilha Técnica e Paradigma Funcional&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_escolha_tecnológicos&amp;quot;&amp;gt;Escolha Tecnológicos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_princípios_funcionais_aplicados&amp;quot;&amp;gt;Princípios Funcionais Aplicados&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_metodologia_ágil_e_backlog&amp;quot;&amp;gt;Metodologia Ágil e Backlog&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_épicas_principais&amp;quot;&amp;gt;Épicas Principais&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_épico_1_infraestrutura_bot_discord&amp;quot;&amp;gt;Épico 1: Infraestrutura Bot Discord&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_épico_2_integração_spotify&amp;quot;&amp;gt;Épico 2: Integração Spotify&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_epic_3_integração_youtube&amp;quot;&amp;gt;Epic 3: Integração YouTube&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_épico_4_funcionalidades_musicais&amp;quot;&amp;gt;Épico 4 : Funcionalidades Musicais&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_histórias_de_usuário_detalhadas&amp;quot;&amp;gt;Histórias de Usuário Detalhadas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel3&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_us1_1_inicialização_do_bot&amp;quot;&amp;gt;US1.1 : Inicialização do Bot&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_us2_1_pesquisa_spotify_inteligente&amp;quot;&amp;gt;US2.1 : Pesquisa Spotify Inteligente&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_us3_1_extração_resiliente_do_youtube&amp;quot;&amp;gt;US3.1 : Extração Resiliente do YouTube&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_abordagem_log_driven_development&amp;quot;&amp;gt;Abordagem Log Driven Development&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estratégia_de_logging&amp;quot;&amp;gt;Estratégia de Logging&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estrutura_dos_logs&amp;quot;&amp;gt;Estrutura dos logs&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_exemplo_de_log_design&amp;quot;&amp;gt;Exemplo de Log Design&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_de_validação_com_pydantic&amp;quot;&amp;gt;Arquitetura de Validação com Pydantic&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_modelos_de_dados&amp;quot;&amp;gt;Modelos de Dados&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_gestão_funcional_de_erros&amp;quot;&amp;gt;Gestão Funcional de Erros&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_monads_e_tratamento_de_erros&amp;quot;&amp;gt;Monads e Tratamento de Erros&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estratégia_de_contorno_das_restrições&amp;quot;&amp;gt;Estratégia de Contorno das Restrições&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_abordagem_multi_fonte&amp;quot;&amp;gt;Abordagem Multi-Fonte&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_plano_de_desenvolvimento_iterativo&amp;quot;&amp;gt;Plano de Desenvolvimento Iterativo&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_planejamento_do_sprint&amp;quot;&amp;gt;Planejamento do Sprint&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_métricas_de_qualidade&amp;quot;&amp;gt;Métricas de Qualidade&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_implantação_e_monitoramento&amp;quot;&amp;gt;Implantação e Monitoramento&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_de_produção&amp;quot;&amp;gt;Arquitetura de Produção&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_monitoramento_proativo&amp;quot;&amp;gt;Monitoramento Proativo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão_e_perspectivas&amp;quot;&amp;gt;Conclusão e Perspectivas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_próximas_etapas&amp;quot;&amp;gt;Próximas Etapas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_introdução&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O desenvolvimento de um bot Discord que integra as APIs do Spotify e do YouTube representa um desafio técnico moderno, especialmente diante das recentes restrições e evoluções das plataformas. Este artigo apresenta uma abordagem metodológica baseada em&amp;lt;strong&amp;gt;Log Driven Development&amp;lt;/strong&amp;gt;(LDD), uma extensão do Test Driven Development, aplicada em um paradigma funcional com Python.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nosso objetivo: criar um bot robusto, mantível e evolutivo, capaz de navegar as restrições atuais das APIs musicais, oferecendo uma experiência de usuário fluida no Discord.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_contexto_e_desafios_atuais&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_contexto_e_desafios_atuais&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Contexto e Desafios Atuais&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_evolução_das_apis_musicais&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_evolução_das_apis_musicais&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Evolução das APIs Musicais&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;As plataformas musicais endureceram significativamente suas políticas de acesso:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Spotify&amp;lt;/strong&amp;gt;Restrições ao acesso aos metadados, limitação das cotas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;YouTube&amp;lt;/strong&amp;gt;Política anti-bot reforçada, complexificação da autenticação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Discord&amp;lt;/strong&amp;gt;Novas exigências de segurança e desempenho&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_abordagem_de_desenvolvimento_orientado_por_logs&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_abordagem_de_desenvolvimento_orientado_por_logs&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Abordagem de desenvolvimento orientado por logs&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O LDD estende o TDD colocando os logs no coração do desenvolvimento:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Definição dos logs&amp;lt;/strong&amp;gt;antes da implementação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Validação por observação&amp;lt;/strong&amp;gt;comportamentos esperados&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Rastreabilidade completa&amp;lt;/strong&amp;gt;fluxos de dados&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Depuração proativa&amp;lt;/strong&amp;gt;por antecipação dos erros&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_arquitetura_conceitual&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_arquitetura_conceitual&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Arquitetura Conceitual&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-4115ce5d8e4bc3a41d4c3f0aa7c225f4.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;777&amp;quot; height=&amp;quot;618&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_pilha_técnica_e_paradigma_funcional&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_pilha_técnica_e_paradigma_funcional&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Pilha Técnica e Paradigma Funcional&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_escolha_tecnológicos&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_escolha_tecnológicos&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Escolha Tecnológicos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A nossa stack gira em torno da programação funcional:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;PyMonade&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Gerenciamento dos efeitos colaterais e composição de funções&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Pydantic&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Validação de dados type-safe e serialização&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Asyncio&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Programação assíncrona para as APIs&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Structlog&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Log estruturado para o LDD&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_princípios_funcionais_aplicados&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_princípios_funcionais_aplicados&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Princípios Funcionais Aplicados&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-233a7e835b20230b1ab00ec0ea9eca96.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;703&amp;quot; height=&amp;quot;554&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_metodologia_ágil_e_backlog&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_metodologia_ágil_e_backlog&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Metodologia Ágil e Backlog&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_épicas_principais&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_épicas_principais&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Épicas Principais&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nosso desenvolvimento se organiza em torno de 4 épicos maiores:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_épico_1_infraestrutura_bot_discord&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_épico_1_infraestrutura_bot_discord&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Épico 1: Infraestrutura Bot Discord&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Valor de negócio&amp;lt;/strong&amp;gt; : Base sólida e extensível&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Critérios de aceitação&amp;lt;/strong&amp;gt; - Conexão Discord estável com gerenciamento de reconexão - Sistema de comandos modular - Logging estruturado integrado - Tratamento centralizado de erros&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_épico_2_integração_spotify&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_épico_2_integração_spotify&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Épico 2: Integração Spotify&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Valor de negócio&amp;lt;/strong&amp;gt; : Acesso aos metadados musicais&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Critérios de aceitação - Autenticação OAuth2 segura - Pesquisa de faixas com cache inteligente Gestão de cotas de API - Fallback em caso de erros de rede&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_epic_3_integração_youtube&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_epic_3_integração_youtube&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Epic 3: Integração YouTube&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Valor de negócio&amp;lt;/strong&amp;gt; : Acesso ao conteúdo de áudio&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Critérios de aceitação&amp;lt;/strong&amp;gt;: Contorno legal das restrições Extração de áudio otimizada Gerenciamento de vídeos privados/excluídos - Respeito das ToS do YouTube&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_épico_4_funcionalidades_musicais&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_épico_4_funcionalidades_musicais&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Épico 4 : Funcionalidades Musicais&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Valor de Negócio&amp;lt;/strong&amp;gt;: Experiência de usuário completa&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Critérios de aceitação&amp;lt;/strong&amp;gt; : - Leitura de áudio de alta qualidade - Fila de leitura inteligente - Comandos de voz Discord - Sincronização multiplataforma&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_histórias_de_usuário_detalhadas&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_histórias_de_usuário_detalhadas&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Histórias de Usuário Detalhadas&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_us1_1_inicialização_do_bot&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_us1_1_inicialização_do_bot&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;US1.1 : Inicialização do Bot&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Como&amp;lt;/strong&amp;gt; desenvolvedor &amp;lt;strong&amp;gt;Eu quero&amp;lt;/strong&amp;gt; um bot Discord que se conecte de forma confiável &amp;lt;strong&amp;gt;Para&amp;lt;/strong&amp;gt; garantir a disponibilidade do serviço&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;DoD (Definição de Concluído)&amp;lt;/strong&amp;gt; : - [ ] O bot conecta-se automaticamente ao iniciar - [ ] Os logs estruturados documentam cada etapa - [ ] Reconexão automática em caso de desconexão - [ ] Testes de integração passam&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_us2_1_pesquisa_spotify_inteligente&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_us2_1_pesquisa_spotify_inteligente&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;US2.1 : Pesquisa Spotify Inteligente&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Como&amp;lt;/strong&amp;gt; utilizador Discord &amp;lt;strong&amp;gt;Eu quero&amp;lt;/strong&amp;gt; procurar faixas via Spotify &amp;lt;strong&amp;gt;Para&amp;lt;/strong&amp;gt; descobrir e compartilhar música&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;DoD&amp;lt;/strong&amp;gt; : - [ ] Ordem`/search`funcional - [ ] Resultados relevantes com metadados - [ ] Cache local para otimizar as requisições - [ ] gestão graciosa dos erros da API&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_us3_1_extração_resiliente_do_youtube&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_us3_1_extração_resiliente_do_youtube&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;US3.1 : Extração Resiliente do YouTube&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Como&amp;lt;/strong&amp;gt; sistema &amp;lt;strong&amp;gt;Quero&amp;lt;/strong&amp;gt; extrair o áudio do YouTube de forma confiável &amp;lt;strong&amp;gt;Para&amp;lt;/strong&amp;gt; manter a continuidade do serviço&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;DoD&amp;lt;/strong&amp;gt; : - [ ] Extração sem violação dos ToS - [ ] Qualidade de áudio ótima - [ ] Gerenciamento de restrições geográficas - [ ] Registros detalhados das operações&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_abordagem_log_driven_development&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_abordagem_log_driven_development&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Abordagem Log Driven Development&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estratégia_de_logging&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_estratégia_de_logging&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Estratégia de Logging&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-dce448ec317d2ffc08a741835f25e1fa.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;687&amp;quot; height=&amp;quot;633&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_estrutura_dos_logs&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_estrutura_dos_logs&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Estrutura dos logs&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nossa abordagem LDD utiliza logs estruturados com níveis semânticos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;RASTRO&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Fluxo de dados detalhado&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;depurar&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Estados internos das funções&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;INFO&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Operações de negócio bem-sucedidas&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;WARN&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Situações degradadas mas geridas&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;ERRO&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Erros que exigem intervenção&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;crítica&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Falhas do sistema&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_exemplo_de_log_design&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_exemplo_de_log_design&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Exemplo de Log Design&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes de implementar a função de pesquisa do Spotify, definimos seus logs :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;INFO: spotify.search.start query=&amp;quot;bohemian rhapsody&amp;quot; user_id=123456
DEBUG: spotify.search.validation query_length=16 safe_chars=true
DEBUG: spotify.search.api_call endpoint=&amp;quot;/search&amp;quot; params={...}
INFO: spotify.search.success results_count=15 duration_ms=340&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_arquitetura_de_validação_com_pydantic&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_arquitetura_de_validação_com_pydantic&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Arquitetura de Validação com Pydantic&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_modelos_de_dados&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_modelos_de_dados&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Modelos de Dados&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nossa abordagem funcional prioriza a validação a montante:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-ebd4e1dfbd7b19d5a6054ecf213e9e2d.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;696&amp;quot; height=&amp;quot;339&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_gestão_funcional_de_erros&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_gestão_funcional_de_erros&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Gestão Funcional de Erros&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_monads_e_tratamento_de_erros&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_monads_e_tratamento_de_erros&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Monads e Tratamento de Erros&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O uso do PyMonade permite um tratamento elegante de erros:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-d56a173ea25719cbd639a4eb34a4919b.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;508&amp;quot; height=&amp;quot;494&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_estratégia_de_contorno_das_restrições&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_estratégia_de_contorno_das_restrições&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Estratégia de Contorno das Restrições&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_abordagem_multi_fonte&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_abordagem_multi_fonte&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Abordagem Multi-Fonte&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Diante das restrições das APIs, adotamos uma estratégia de diversificação:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-b8ae9e873554f0efebee724ae2693523.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;665&amp;quot; height=&amp;quot;744&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_plano_de_desenvolvimento_iterativo&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_plano_de_desenvolvimento_iterativo&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Plano de Desenvolvimento Iterativo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_planejamento_do_sprint&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_planejamento_do_sprint&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Planejamento do Sprint&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nosso desenvolvimento segue um ciclo de sprints de 2 semanas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sprint 1-2&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Infraestrutura e Núcleo do Bot do Discord&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sprint 3-4&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Integração Spotify com LDD&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sprint 5-6&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Integração YouTube e contornos&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sprint 7-8&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Funcionalidades musicais avançadas&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sprint 9-10&amp;lt;/strong&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Otimização e produção&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_métricas_de_qualidade&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_métricas_de_qualidade&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Métricas de Qualidade&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cada sprint é avaliado em :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Cobertura de logs&amp;lt;/strong&amp;gt;: &amp;amp;gt;90% dos caminhos críticos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fiabilidade da API&amp;lt;/strong&amp;gt;: &amp;amp;lt;1% de erros não tratados&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;desempenho&amp;lt;/strong&amp;gt;: &amp;amp;lt;500ms tempo médio de resposta&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Manutenibilidade&amp;lt;/strong&amp;gt;: Complexidade ciclomática &amp;amp;lt;10&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_implantação_e_monitoramento&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_implantação_e_monitoramento&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Implantação e Monitoramento&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_arquitetura_de_produção&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_arquitetura_de_produção&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Arquitetura de Produção&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-9c8fabc0e53dede0f678193b1cccd21e.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;946&amp;quot; height=&amp;quot;381&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_monitoramento_proativo&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_monitoramento_proativo&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Monitoramento Proativo&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O LDD facilita um monitoramento inteligente:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Alertas baseados nos padrões de logs&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Detecção de anomalias comportamentais&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Métricas de negócio em tempo real&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Depuração assistida por correlação de logs&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_e_perspectivas&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_conclusão_e_perspectivas&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Conclusão e Perspectivas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta abordagem metodológica combina os benefícios do paradigma funcional com a robustez do Log Driven Development. Ela nos permite:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Antecipar os problemas&amp;lt;/strong&amp;gt;graças aos logs projetados com antecedência&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Manter a qualidade&amp;lt;/strong&amp;gt;por meio da validação contínua&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Adaptar rapidamente&amp;lt;/strong&amp;gt;às alterações das APIs&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Garantir a rastreabilidade&amp;lt;/strong&amp;gt;completa das operações&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O desenvolvimento iterativo e a arquitetura modular garantem uma escalabilidade face às restrições mutáveis das plataformas musicais.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_próximas_etapas&amp;quot;&amp;gt;&amp;lt;a class=&amp;quot;anchor&amp;quot; href=&amp;quot;#_próximas_etapas&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;Próximas Etapas&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fase 1&amp;lt;/strong&amp;gt;Implementação do core com PyMonade&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fase 2&amp;lt;/strong&amp;gt;: Integração Spotify com cache inteligente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fase 3&amp;lt;/strong&amp;gt;: Solução YouTube resiliente&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Fase 4&amp;lt;/strong&amp;gt;: Funcionalidades avançadas e otimização&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta fundação conceitual sólida nos permitirá navegar pelos desafios técnicos, enquanto oferece uma experiência do usuário excepcional.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Este artigo será seguido de uma série técnica detalhando a implementação de cada componente com exemplos de código e padrões funcionais.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Resfriar e monitorizar a temperatura do seu laptop no Ubuntu</title>
            <link >https://cheroliv.com//blog/2025/0083_cold_computer_post.html</link>
            <pubDate>Tue, 15 Jul 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0083_cold_computer_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table of Contents&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_objetivo_deste_guia&amp;quot;&amp;gt;Objetivo deste guia&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_pré_requisitos&amp;quot;&amp;gt;Pré-requisitos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_instalar_e_configurar_as_ferramentas&amp;quot;&amp;gt;Instalar e configurar as ferramentas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_lm_sensors_para_detectar_os_sensores&amp;quot;&amp;gt;lm-sensors` para detectar os sensores&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_tlp_gerenciador_de_energia_inteligente&amp;quot;&amp;gt;tlp` : gerenciador de energia inteligente&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_cpufrequtils_limitar_as_frequências_da_cpu&amp;quot;&amp;gt;cpufrequtils` : limitar as frequências da CPU&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_auto_cpufreq_gerenciamento_dinâmico_e_simplificado&amp;quot;&amp;gt;auto-cpufreq`: gerenciamento dinâmico e simplificado&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_thermald_proteção_térmica_automática&amp;quot;&amp;gt;thermald` : proteção térmica automática&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_vigilância_em_tempo_real&amp;quot;&amp;gt;Vigilância em tempo real&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_sensors_na_linha_de_comando&amp;quot;&amp;gt;sensors` na linha de comando&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_opção_gui_psensor&amp;quot;&amp;gt;Opção GUI : `psensor&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_dicas_adicionais&amp;quot;&amp;gt;Dicas adicionais&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_limpar_o_sistema_para_limitar_as_sobrecargas&amp;quot;&amp;gt;Limpar o sistema para limitar as sobrecargas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_para_ir_mais_longe&amp;quot;&amp;gt;Para ir mais longe&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;�📚 Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você sente que seu computador portátil está esquentando anormalmente sob Ubuntu? Este artigo propõe uma abordagem&amp;lt;strong&amp;gt;software&amp;lt;/strong&amp;gt;completa para&amp;lt;strong&amp;gt;resfriar&amp;lt;/strong&amp;gt;, &amp;lt;strong&amp;gt;vigiar&amp;lt;/strong&amp;gt; et &amp;lt;strong&amp;gt;controlar a temperatura&amp;lt;/strong&amp;gt;da sua máquina.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_objetivo_deste_guia&amp;quot;&amp;gt;Objetivo deste guia&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Reduzir a temperatura global do computador&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Otimizar a gestão de energia&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Monitorar os componentes críticos (CPU, GPU, etc.)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Evitar os atrasos decorrentes de&amp;lt;strong&amp;gt;throttle térmico&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;gestion_thermique.svg&amp;quot; alt=&amp;quot;gestion thermique&amp;quot; width=&amp;quot;392&amp;quot; height=&amp;quot;298&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_pré_requisitos&amp;quot;&amp;gt;Pré-requisitos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Uma máquina Ubuntu (20.04 ou superior recomendada)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Conexão à Internet&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Competência em linha de comando (nível iniciante a intermediário)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_instalar_e_configurar_as_ferramentas&amp;quot;&amp;gt;Instalar e configurar as ferramentas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_lm_sensors_para_detectar_os_sensores&amp;quot;&amp;gt;lm-sensors` para detectar os sensores&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt install lm-sensors
sudo sensors-detect
sensors&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Responda`YES`para todas as perguntas de`sensors-detect`para detectar os módulos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_tlp_gerenciador_de_energia_inteligente&amp;quot;&amp;gt;tlp` : gerenciador de energia inteligente&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt install tlp tlp-rdw
sudo systemctl enable tlp
sudo systemctl start tlp&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;tlp_etat.svg&amp;quot; alt=&amp;quot;tlp etat&amp;quot; width=&amp;quot;367&amp;quot; height=&amp;quot;390&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_cpufrequtils_limitar_as_frequências_da_cpu&amp;quot;&amp;gt;cpufrequtils` : limitar as frequências da CPU&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt install cpufrequtils
cpufreq-info
sudo cpufreq-set -g powersave&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;`powersave`limita os picos de frequência&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;performance&amp;lt;/code&amp;gt;: favorece a potência em detrimento da temperatura&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_auto_cpufreq_gerenciamento_dinâmico_e_simplificado&amp;quot;&amp;gt;auto-cpufreq`: gerenciamento dinâmico e simplificado&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;git clone https://github.com/AdnanHodzic/auto-cpufreq.git
cd auto-cpufreq
sudo ./auto-cpufreq-installer
sudo auto-cpufreq --install&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;auto_cpufreq_cycle.svg&amp;quot; alt=&amp;quot;auto cpufreq cycle&amp;quot; width=&amp;quot;189&amp;quot; height=&amp;quot;298&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_thermald_proteção_térmica_automática&amp;quot;&amp;gt;thermald` : proteção térmica automática&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt install thermald
sudo systemctl enable thermald
sudo systemctl start thermald&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_vigilância_em_tempo_real&amp;quot;&amp;gt;Vigilância em tempo real&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_sensors_na_linha_de_comando&amp;quot;&amp;gt;sensors` na linha de comando&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;watch -n 2 sensors&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_opção_gui_psensor&amp;quot;&amp;gt;Opção GUI : `psensor&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt install psensor&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Interface gráfica simples para CPU, GPU, ventoinhas&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_dicas_adicionais&amp;quot;&amp;gt;Dicas adicionais&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Fechar as abas e aplicações pesadas (ex: navegadores)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Evite o uso intenso na bateria&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Monitorar os processos via`htop` :&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt install htop
htop&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_limpar_o_sistema_para_limitar_as_sobrecargas&amp;quot;&amp;gt;Limpar o sistema para limitar as sobrecargas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash hljs&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt autoremove
sudo apt autoclean
sudo journalctl --vacuum-time=3d&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_para_ir_mais_longe&amp;quot;&amp;gt;Para ir mais longe&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você pode criar um serviço`systemd`personalizado para aplicar automaticamente algumas configurações na inicialização. Gostaria de um exemplo num próximo artigo? Deixe-me saber!&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;�📚 Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Combinando estas ferramentas simples, você ganhará:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Temperatura da CPU controlada&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Menos ruído de ventilação&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Uma melhor autonomia&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não se esqueça de monitorar suas temperaturas regularmente para evitar qualquer superaquecimento.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&amp;lt;em&amp;gt;_ É melhor prevenir do que derreter a sua CPU. &amp;lt;/em&amp;gt;_&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Relato de uma depuração: domando os caprichos do Firefox com Bootstrap 5</title>
            <link >https://cheroliv.com//blog/2025/0082_ui_struggle_post.html</link>
            <pubDate>Mon, 14 Jul 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0082_ui_struggle_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_campo_de_batalha_um_modelo_três_navegadores_renderizações_múltiplas&amp;quot;&amp;gt;O campo de batalha : um modelo, três navegadores, renderizações múltiplas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_problema_1_o_cartão_flutuante_da_seção_hero&amp;quot;&amp;gt;Problema 1: O cartão flutuante da seção &amp;quot;Hero&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_problema_2_a_barra_de_navegação_canibal&amp;quot;&amp;gt;Problema 2 : A barra de navegação canibal&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_problema_3_a_tipografia_anárquica&amp;quot;&amp;gt;Problema 3: A tipografia anárquica&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_problema_4_a_barra_de_navegação_que_transborda&amp;quot;&amp;gt;Problema 4: A barra de navegação que transborda&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_problema_5_o_menu_hambúrguer_invisível&amp;quot;&amp;gt;Problema 5: O menu hambúrguer invisível&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão_as_lições_de_uma_depuração_incansável&amp;quot;&amp;gt;Conclusão: as lições de uma depuração incansável&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock note&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-note&amp;quot; title=&amp;quot;Note&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Tempo de leitura :&amp;lt;/strong&amp;gt; aproximadamente 7 minutos &amp;lt;strong&amp;gt;Nível :&amp;lt;/strong&amp;gt; Intermediário&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Neste estudo de caso detalhado, vamos analisar os problemas de compatibilidade de navegador mais comuns e, às vezes, mais frustrantes encontrados ao criar uma maqueta UI com o Bootstrap 5.2. Desde o gerenciamento do Flexbox até a sutileza da renderização das fontes, descubra nossa jornada de depuração para transformar um design instável no Firefox em uma experiência pixel-perfeito em todos os navegadores.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_campo_de_batalha_um_modelo_três_navegadores_renderizações_múltiplas&amp;quot;&amp;gt;O campo de batalha : um modelo, três navegadores, renderizações múltiplas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Todo desenvolvedor front-end conhece esse sentimento. Depois de horas de trabalho, a maquete está finalmente perfeita. Os alinhamentos são nítidos, a tipografia é elegante, as animações são fluidas. Damos um suspiro de satisfação, admiramos nosso trabalho no Chrome (ou Brave, ou Edge), e então, por precaução, abrimos o projeto no Firefox. E aí, é o drama. Elementos que transbordam, alinhamentos quebrados, fontes com tamanhos caóticos&amp;amp;#8230;&amp;amp;#8203; a bela harmonia se foi.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É precisamente o cenário que enfrentamos ao desenvolver uma nova interface de portfólio. O briefing era simples: uma página inicial moderna, responsiva, com um seletor de temas (claro, escuro e de alto contraste), usando &amp;lt;strong&amp;gt;vanilla JavaScript&amp;lt;/strong&amp;gt; e a última versão do &amp;lt;strong&amp;gt;Bootstrap 5.2&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este artigo não é uma lista de soluções milagrosas. É o diário de bordo da nossa sessão de depuração, uma imersão no &amp;quot;porquê&amp;quot; das diferenças de renderização entre os motores Blink (Chromium, Brave) e Gecko (Firefox), e a demonstração de que soluções robustas e modernas valem sempre mais do que remendos feitos às pressas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/usecase-debugging-firefox.svg&amp;quot; alt=&amp;quot;usecase debugging firefox&amp;quot; width=&amp;quot;1049&amp;quot; height=&amp;quot;386&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Figure 1. Diagrama de Caso de Uso : O processo de depuração&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/components-solution-architecture.svg&amp;quot; alt=&amp;quot;components solution architecture&amp;quot; width=&amp;quot;1226&amp;quot; height=&amp;quot;5160&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Figure 2. Diagrama de Componentes : Arquitetura da solução front-end&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_problema_1_o_cartão_flutuante_da_seção_hero&amp;quot;&amp;gt;Problema 1: O cartão flutuante da seção &amp;quot;Hero&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O primeiro bug também foi o mais visível. A seção de boas-vindas (&amp;quot;hero&amp;quot;) é composta por duas colunas: à esquerda, o título principal; à direita, um cartão de apresentação.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O sintoma:&amp;lt;/strong&amp;gt;No Chrome e no Brave, as duas colunas estavam perfeitamente centralizadas verticalmente. No Firefox, o cartão à direita caía inexplicavelmente abaixo do texto da esquerda.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A investigação:&amp;lt;/strong&amp;gt;O código HTML parecia porém simples e correto, utilizando as classes padrão do Bootstrap.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html hljs&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;section id=&amp;quot;home&amp;quot; class=&amp;quot;hero-section&amp;quot;&amp;amp;gt;
    &amp;amp;lt;div class=&amp;quot;container&amp;quot;&amp;amp;gt;
        &amp;amp;lt;!-- Cette ligne est la clé du problème --&amp;amp;gt;
        &amp;amp;lt;div class=&amp;quot;row align-items-center min-vh-100&amp;quot;&amp;amp;gt;
            &amp;amp;lt;div class=&amp;quot;col-lg-6&amp;quot;&amp;amp;gt;
                &amp;amp;lt;!-- Contenu de gauche --&amp;amp;gt;
            &amp;amp;lt;/div&amp;amp;gt;
            &amp;amp;lt;div class=&amp;quot;col-lg-6&amp;quot;&amp;amp;gt;
                &amp;amp;lt;!-- Carte de droite --&amp;amp;gt;
            &amp;amp;lt;/div&amp;amp;gt;
        &amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;/div&amp;amp;gt;
&amp;amp;lt;/section&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Nosso CSS personalizado para`.hero-section`definia`display: flex` et &amp;lt;code&amp;gt;align-items: center&amp;lt;/code&amp;gt;, e a classe`min-vh-100`dava de facto uma altura mínima à linha (&amp;lt;code&amp;gt;div.row&amp;lt;/code&amp;gt;). Então, por que o Firefox se recusava a centralizar as colunas?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A causa profunda :&amp;lt;/strong&amp;gt;É um caso de escola sobre a forma como os mecanismos de renderização interpretam as alturas implícitas. La`&amp;amp;lt;section&amp;amp;gt;`tinha uma`min-height`, mas não`height`explícito. Para aplicar`align-items-center`dentro de`div.row`, O Firefox precisa saber a altura de referência deste contêiner. Como os pais (&amp;lt;code&amp;gt;div.container&amp;lt;/code&amp;gt; et la `&amp;amp;lt;section&amp;amp;gt;`ela mesma) não tinham altura&amp;lt;strong&amp;gt;estrita&amp;lt;/strong&amp;gt;, Firefox estava perdido. O motor Blink, mais permissivo, consegue &amp;quot;adivinhar&amp;quot; a intenção e centralizar os elementos. Gecko, mais fiel à especificação, não o faz.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A solução:&amp;lt;/strong&amp;gt;Tornar a altura explícita. Nós forçamos o`.container` et le `.row`para ocupar 100% da altura de seu respectivo pai usando a classe utilitária`h-100`do Bootstrap.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html hljs&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;section id=&amp;quot;home&amp;quot; class=&amp;quot;hero-section&amp;quot;&amp;amp;gt;
    &amp;amp;lt;div class=&amp;quot;container h-100&amp;quot;&amp;amp;gt;
        &amp;amp;lt;div class=&amp;quot;row align-items-center min-vh-100 h-100&amp;quot;&amp;amp;gt;
            &amp;amp;lt;!-- ... --&amp;amp;gt;
        &amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;/div&amp;amp;gt;
&amp;amp;lt;/section&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock tip&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-tip&amp;quot; title=&amp;quot;Tip&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando um`align-items`Flexbox não funciona como esperado no eixo vertical, sempre verifique se o contêiner tem altura`height` ou &amp;lt;code&amp;gt;min-height&amp;lt;/code&amp;gt;) definida e, se o problema persistir (especialmente no Firefox), certifique-se de que os contêineres pais intermediários propaguem bem essa altura.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_problema_2_a_barra_de_navegação_canibal&amp;quot;&amp;gt;Problema 2 : A barra de navegação canibal&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Uma vez que a seção &amp;quot;Hero&amp;quot; estiver alinhada, outro problema surgiu: a barra de navegação, com sua classe`fixed-top`, sobrepunha-se ao título. Um`padding-top: 80px`tinha sido aplicado à seção &amp;quot;Hero&amp;quot; como solução.&amp;lt;/div&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O sintoma:&amp;lt;/strong&amp;gt; Le `padding`era suficiente no Chrome, mas não no Firefox, onde o título ainda estava parcialmente mascarado.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A causa profunda:&amp;lt;/strong&amp;gt;O uso de um &amp;quot;número mágico&amp;quot; (&amp;lt;code&amp;gt;80px&amp;lt;/code&amp;gt;) É uma prática muito má. A altura de um elemento como uma barra de navegação pode variar alguns píxeis entre navegadores devido a diferenças sutis na renderização das fontes, do espaçamento ou mesmo nas opções do utilizador. Confiar em um valor fixo é a receita para um design frágil.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A solução :&amp;lt;/strong&amp;gt;Uma solução dinâmica e infalível em JavaScript. Escrevemos um pequeno script para:&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Medir a altura &amp;lt;strong&amp;gt;real&amp;lt;/strong&amp;gt; da barra de navegação após o render da página.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Aplicar esta altura medida como`padding-top`para a seção &amp;quot;Hero&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Reexecutar essa função a cada redimensionamento da janela para se adaptar às mudanças (como a transição para o menu hambúrguer).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript hljs&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;document.addEventListener(&amp;#39;DOMContentLoaded&amp;#39;, function() {
    function adjustHeroPadding() {
        const navbar = document.querySelector(&amp;#39;.navbar.fixed-top&amp;#39;);
        const heroSection = document.querySelector(&amp;#39;.hero-section&amp;#39;);

        if (navbar &amp;amp;amp;&amp;amp;amp; heroSection) {
            const navbarHeight = navbar.offsetHeight;
            heroSection.style.paddingTop = navbarHeight + &amp;#39;px&amp;#39;;
        }
    }

    // Ajuster au chargement et au redimensionnement
    adjustHeroPadding();
    window.addEventListener(&amp;#39;resize&amp;#39;, adjustHeroPadding);
});&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/sequence-dynamic-padding.svg&amp;quot; alt=&amp;quot;sequence dynamic padding&amp;quot; width=&amp;quot;887&amp;quot; height=&amp;quot;703&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Figure 3. Diagrama de Sequência : Ajustamento dinâmico do padding&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em paralelo, claro, removemos a regra`padding-top: 80px;`do nosso arquivo`styles.css`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_problema_3_a_tipografia_anárquica&amp;quot;&amp;gt;Problema 3: A tipografia anárquica&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;A preocupação seguinte era a mais prejudicial à imagem da marca: o tamanho do título principal.&amp;lt;/div&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O sintoma :&amp;lt;/strong&amp;gt;No Firefox, a fonte do título &amp;quot;Desenvolvedor Formador&amp;amp;#8230;&amp;amp;#8203;&amp;quot; era enorme, quase chocante, enquanto que era harmoniosa nos outros navegadores.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A causa profunda:&amp;lt;/strong&amp;gt;O título utilizava uma classe`display-4`do Bootstrap. Estas classes utilizam tamanhos de fonte responsivos, frequentemente baseados na unidade`rem`e media queries. Mais uma vez, o algoritmo de renderização do Firefox, combinado com a fonte &amp;quot;Inter&amp;quot;, resultava em um cálculo de tamanho muito maior em nossa resolução de 1920x1080.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A solução :&amp;lt;/strong&amp;gt;Retomar o controle com a função CSS`clamp()&amp;lt;code&amp;gt;. Esta função é uma revolução para a tipografia fluida. Ela permite definir um tamanho de fonte com três valores: um tamanho &amp;quot;preferido&amp;quot; (que se adapta à largura da visualização,`vw&amp;lt;/code&amp;gt;), e um tamanho máximo.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css hljs&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;.hero-content h1 {
    color: var(--text-primary);
    line-height: 1.2;
    /*
       Min: 2rem
       Préférée: s&amp;#39;adapte à la vue
       Max: 2.5rem (40px)
    */
    font-size: clamp(2rem, 1.2rem + 1.5vw, 2.5rem);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Com`clamp()&amp;lt;code&amp;gt;, nós pudemos dizer ao navegador: &amp;quot;Faça a fonte crescer com a tela, mas não ultrapasse&amp;lt;strong&amp;gt;nunca&amp;lt;/strong&amp;gt;o limite de`2.5rem&amp;lt;/code&amp;gt;. Isso instantaneamente harmonizou a renderização em todos os navegadores, nos dando controle absoluto sobre a estética final.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_problema_4_a_barra_de_navegação_que_transborda&amp;quot;&amp;gt;Problema 4: A barra de navegação que transborda&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este foi o problema que nos deu o maior trabalho e que acabou por revelar a natureza profunda das microdiferenças de renderização.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O sintoma:&amp;lt;/strong&amp;gt;Em uma tela de 1920x1080, os últimos links do menu (&amp;quot;Blog&amp;quot;, &amp;quot;Contact&amp;quot;) foram empurrados para fora da tela à direita, mas apenas no Firefox.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A causa profunda :&amp;lt;/strong&amp;gt;Após várias tentativas infrutíferas (alterar os pontos de ruptura do Bootstrap de`lg` à &amp;lt;code&amp;gt;xl`então`xxl&amp;lt;/code&amp;gt;), nós entendemos. A causa não era a lógica do Bootstrap, mas um simples cálculo de largura. No Firefox, a soma das larguras de todos os links, incluindo seus`padding` et `margin`com precisão de sub-pixel, era &amp;lt;strong&amp;gt;ligeiramente&amp;lt;/strong&amp;gt; superior a 1920 pixels. No Chrome, essa mesma soma era &amp;lt;strong&amp;gt;ligeiramente&amp;lt;/strong&amp;gt; inferior. Nos faltavam alguns pixels.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A solução:&amp;lt;/strong&amp;gt;Uma redução cirúrgica dos espaçamentos. Já que não era questão de fazer um alvo específico do Firefox com &amp;quot;hacks&amp;quot; CSS obsoletos, a única solução limpa era encontrar um estilo comum que funcionasse em qualquer lugar. Então reduzimos ligeiramente as margens e o padding horizontal de cada link de navegação.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css hljs&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;/* La version finale, légèrement plus compacte */
.navbar-nav .nav-link {
    font-size: 0.9rem; /* 90% de la taille de base */
    color: var(--text-primary) !important;
    font-weight: 500;
    margin: 0 0.2rem;
    padding: 0.5rem 0.5rem !important;
    border-radius: 0.375rem;
    transition: all 0.3s ease;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta redução, quase invisível a olho nu em um único elemento, nos fez ganhar coletivamente o espaço necessário para que todos os links caibam dentro do quadro no Firefox, mantendo ao mesmo tempo uma aparência quase idêntica nos outros navegadores.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_problema_5_o_menu_hambúrguer_invisível&amp;quot;&amp;gt;Problema 5: O menu hambúrguer invisível&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;O último problema, e não dos menores, concernia a acessibilidade móvel.&amp;lt;/div&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;O sintoma :&amp;lt;/strong&amp;gt;No modo responsivo (menu &amp;quot;hamburger&amp;quot;), o ícone do menu era invisível nos temas &amp;quot;dark&amp;quot; e &amp;quot;high-contrast&amp;quot;.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A causa profunda :&amp;lt;/strong&amp;gt;O ícone do menu do Bootstrap 5 é um`background-image`(Um SVG codificado em URL). Por padrão, a cor do traço é escura, otimizada para um fundo claro. Ela não se adapta automaticamente à mudança de tema.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;A solução :&amp;lt;/strong&amp;gt;Usar as variáveis CSS do Bootstrap para sobrescrever o ícone. Definimos um novo ícone SVG, mas desta vez com um traço branco (&amp;lt;code&amp;gt;#ffffff&amp;lt;/code&amp;gt;), e nós a aplicamos especificamente quando os temas`dark` ou `high-contrast`estão ativos.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css hljs&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;/* ======================
   Fix pour l&amp;#39;icône du menu Burger (Blanc)
   ====================== */
[data-bs-theme=&amp;quot;dark&amp;quot;] .navbar-toggler-icon,
[data-bs-theme=&amp;quot;high-contrast&amp;quot;] .navbar-toggler-icon {
    --bs-navbar-toggler-icon-bg: url(&amp;quot;data:image/svg+xml,%3csvg xmlns=&amp;#39;http://www.w3.org/2000/svg&amp;#39; viewBox=&amp;#39;0 0 30 30&amp;#39;%3e%3cpath stroke=&amp;#39;%23ffffff&amp;#39; stroke-linecap=&amp;#39;round&amp;#39; stroke-miterlimit=&amp;#39;10&amp;#39; stroke-width=&amp;#39;2&amp;#39; d=&amp;#39;M4 7h22M4 15h22M4 23h22&amp;#39;/%3e%3c/svg%3e&amp;quot;);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock important&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-important&amp;quot; title=&amp;quot;Important&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A personalização dos componentes do Bootstrap, como este, está sendo feita cada vez mais por meio da sobrescrita de variáveis CSS (&amp;lt;code&amp;gt;--bs-component-property&amp;lt;/code&amp;gt;), que é o método mais limpo e mais duradouro.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão_as_lições_de_uma_depuração_incansável&amp;quot;&amp;gt;Conclusão: as lições de uma depuração incansável&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este percurso, embora por vezes frustrante, é rico em ensinamentos. Cada problema resolvido reforça uma verdade fundamental do desenvolvimento web :&amp;lt;strong&amp;gt;nada substitui um teste rigoroso de múltiplos navegadores&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/mindmap-debugging-summary.svg&amp;quot; alt=&amp;quot;mindmap debugging summary&amp;quot; width=&amp;quot;999&amp;quot; height=&amp;quot;866&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Figure 4. Mindmap : Síntese da depuração e das lições aprendidas&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Estas são as lições que nós retenemos:&amp;lt;/div&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Firefox é um aliado:&amp;lt;/strong&amp;gt;Sua maior fidelidade aos padrões CSS nos força a escrever um código mais robusto e menos ambíguo.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Evite os &amp;quot;números mágicos&amp;quot; :&amp;lt;/strong&amp;gt;Os valeurs fixos (como`padding-top: 80px`) são bombas-relógio. Prefiram sempre soluções dinâmicas (JavaScript) ou relativas (Flexbox, Grid).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Domine sua tipografia :&amp;lt;/strong&amp;gt;Utilize`clamp()`para um controle total sobre o tamanho das fontes responsivas.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Pense em &amp;quot;componentes&amp;quot; :&amp;lt;/strong&amp;gt;Para personalizar o Bootstrap, sobrescreva suas variáveis CSS em vez de multiplicar as regras que sobrescrevem o framework.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Não visem um navegador :&amp;lt;/strong&amp;gt;A solução raramente é fazer um &amp;quot;hack&amp;quot; para um navegador específico, mas sim encontrar um estilo comum que funcione em todos os lugares.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No final, a maquete agora está sólida, previsível e pronta para ser integrada em um sistema de templating. Cada bug corrigido não foi um fracasso, mas um passo em direção a um código de melhor qualidade.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Resolver o Desafio dos Testes Unitários do Gradle com `gradle.properties</title>
            <link >https://cheroliv.com//blog/2025/0081_TDD_Gradle_Properties_Fix_post.html</link>
            <pubDate>Sun, 13 Jul 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0081_TDD_Gradle_Properties_Fix_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_isolar_os_testes_unitários_de_um_plugin_gradle&amp;quot;&amp;gt;O Problema: Isolar os Testes Unitários de um Plugin Gradle&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_do_problema&amp;quot;&amp;gt;Arquitetura do Problema&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_tentação_de_uma_falsa_boa_ideia&amp;quot;&amp;gt;A Tentação de uma Falsa Boa Ideia&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_simular_a_propriedade_com_extrapropertiesextension&amp;quot;&amp;gt;A Solução: Simular a Propriedade com `ExtraPropertiesExtension&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_1_compreender_extrapropertiesextension&amp;quot;&amp;gt;Passo 1: Compreender ExtraPropertiesExtension&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_2_configuração_do_teste_preparação&amp;quot;&amp;gt;Etapa 2: Configuração do Teste - Preparação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_3_injeção_da_propriedade&amp;quot;&amp;gt;Passo 3: Injeção da Propriedade&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_4_diagrama_de_fluxo_da_solução&amp;quot;&amp;gt;Passo 4 : Diagrama de Fluxo da Solução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_5_comparação_antesdepois&amp;quot;&amp;gt;Etapa 5 : comparação antes/depois&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_6_gestão_dos_casos_de_teste_múltiplos&amp;quot;&amp;gt;Etapa 6 : Gestão dos Casos de Teste Múltiplos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_final_da_solução&amp;quot;&amp;gt;Arquitetura Final da Solução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_as_vantagens_desta_abordagem&amp;quot;&amp;gt;As Vantagens desta Abordagem&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_boas_práticas_e_dicas&amp;quot;&amp;gt;Boas Práticas e Dicas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_criar_um_método_utilitário&amp;quot;&amp;gt;Criar um Método Utilitário&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_validação_das_propriedades&amp;quot;&amp;gt;Validação das Propriedades&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_problema_isolar_os_testes_unitários_de_um_plugin_gradle&amp;quot;&amp;gt;O Problema: Isolar os Testes Unitários de um Plugin Gradle&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Durante a escrita de testes unitários para um plugin Gradle, um desafio comum é lidar com as dependências da configuração do projeto, como as propriedades definidas no arquivo`gradle.properties`. No nosso caso, o plugin`jbake.ghpages`devia ler uma propriedade`site_config_path`para funcionar corretamente. O teste unitário para a tarefa`initialize`Precisava verificar o comportamento do plugin na presença desta propriedade.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O problema fundamental é que os testes unitários, por design, devem ser &amp;lt;strong&amp;gt;isolados&amp;lt;/strong&amp;gt;. O uso de`ProjectBuilder`de Gradle cria uma instância de`Project`na memória, totalmente desconectada de um verdadeiro projeto no sistema de arquivos. Portanto, esta instância de teste não lê automaticamente o arquivo`gradle.properties`e portanto não tem conhecimento da propriedade`site_config_path`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_arquitetura_do_problema&amp;quot;&amp;gt;Arquitetura do Problema&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O diagrama a seguir ilustra a desconexão entre o ambiente de teste e o sistema de arquivos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/test-isolation-problem.svg&amp;quot; alt=&amp;quot;test isolation problem&amp;quot; width=&amp;quot;1212&amp;quot; height=&amp;quot;5787&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_tentação_de_uma_falsa_boa_ideia&amp;quot;&amp;gt;A Tentação de uma Falsa Boa Ideia&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Uma primeira abordagem poderia ser criar um arquivo`gradle.properties`fictício nos recursos de teste.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;// src/test/resources/gradle.properties
site_config_path=src/jbake/settings/site.yml&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Contudo, este método está destinado ao fracasso porque`ProjectBuilder`não é concebido para escanear o sistema de arquivos em busca de arquivos de configuração. O teste permaneceria isolado e ignoraria este arquivo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_simular_a_propriedade_com_extrapropertiesextension&amp;quot;&amp;gt;A Solução: Simular a Propriedade com `ExtraPropertiesExtension&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A solução elegante para este problema não é de &amp;lt;strong&amp;gt;ler&amp;lt;/strong&amp;gt; o arquivo, mas de &amp;lt;strong&amp;gt;simular&amp;lt;/strong&amp;gt; a presença da propriedade diretamente no objeto`Project`de teste. O Gradle fornece um mecanismo poderoso para isso: as &amp;lt;strong&amp;gt;propriedades adicionais&amp;lt;/strong&amp;gt; (Extra Properties).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_passo_1_compreender_extrapropertiesextension&amp;quot;&amp;gt;Passo 1: Compreender ExtraPropertiesExtension&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`ExtraPropertiesExtension`é um contêiner chave-valor anexado a cada objeto do modelo Gradle. Ele permite adicionar propriedades dinamicamente a um projeto, uma tarefa ou qualquer outra extensão Gradle.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/extra-properties-architecture.svg&amp;quot; alt=&amp;quot;extra properties architecture&amp;quot; width=&amp;quot;533&amp;quot; height=&amp;quot;436&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_etapa_2_configuração_do_teste_preparação&amp;quot;&amp;gt;Etapa 2: Configuração do Teste - Preparação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Vamos começar criando a estrutura básica do nosso teste unitário :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;JbakeGhPagesPluginTest.kt - Structure de base&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;class JbakeGhPagesPluginTest {

    @TempDir
    lateinit var testProjectDir: File

    @Test
    fun `check initialize and config yaml file if not existing`() {
        // Étape 1 : Créer un projet de test isolé
        val project = ProjectBuilder.builder()
            .withProjectDir(testProjectDir)
            .build()

        // Suite des étapes...
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_passo_3_injeção_da_propriedade&amp;quot;&amp;gt;Passo 3: Injeção da Propriedade&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está o processo detalhado para injetar a propriedade no projeto de teste :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;JbakeGhPagesPluginTest.kt - Injection complète&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test
fun `check initialize and config yaml file if not existing`() {
    // Étape 1 : Créer un projet de test isolé en mémoire
    val project = ProjectBuilder.builder()
        .withProjectDir(testProjectDir)
        .build()

    // Étape 2 : Récupérer le gestionnaire de propriétés supplémentaires
    val extra = project.extensions.getByType(ExtraPropertiesExtension::class.java)

    // Étape 3 : Définir la propriété requise pour ce test
    extra.set(&amp;quot;site_config_path&amp;quot;, &amp;quot;src/jbake/settings/site.yml&amp;quot;)

    // Étape 4 : Vérifier que la propriété est bien injectée
    assertTrue(project.hasProperty(&amp;quot;site_config_path&amp;quot;))

    // Étape 5 : Appliquer le plugin qui pourra maintenant accéder à la propriété
    project.plugins.apply(&amp;quot;jbake.ghpages&amp;quot;)

    // Étape 6 : Exécuter la tâche et vérifier son comportement
    val task: Task = project.tasks.findByName(&amp;quot;initialize&amp;quot;)
        .apply(::assertNotNull)!!

    // Étape 7 : Exécuter les actions de la tâche
    task.actions.forEach { it.execute(task) }

    // Étape 8 : Assertions finales
    assertEquals(
        &amp;quot;src/jbake/settings/site.yml&amp;quot;,
        project.properties[&amp;quot;site_config_path&amp;quot;],
        &amp;quot;La propriété devrait être accessible dans le projet&amp;quot;
    )
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_passo_4_diagrama_de_fluxo_da_solução&amp;quot;&amp;gt;Passo 4 : Diagrama de Fluxo da Solução&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/solution-flow.svg&amp;quot; alt=&amp;quot;solution flow&amp;quot; width=&amp;quot;242&amp;quot; height=&amp;quot;807&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_etapa_5_comparação_antesdepois&amp;quot;&amp;gt;Etapa 5 : comparação antes/depois&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/before-after-comparison.svg&amp;quot; alt=&amp;quot;before after comparison&amp;quot; width=&amp;quot;1191&amp;quot; height=&amp;quot;5759&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_etapa_6_gestão_dos_casos_de_teste_múltiplos&amp;quot;&amp;gt;Etapa 6 : Gestão dos Casos de Teste Múltiplos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para testar diferentes cenários, vamos criar vários testes com configurações variadas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Tests multiples avec configurations différentes&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;class JbakeGhPagesPluginTest {

    @TempDir
    lateinit var testProjectDir: File

    private fun createProjectWithProperty(propertyValue: String?): Project {
        val project = ProjectBuilder.builder()
            .withProjectDir(testProjectDir)
            .build()

        // Injection conditionnelle de la propriété
        propertyValue?.let { value -&amp;amp;gt;
            val extra = project.extensions.getByType(ExtraPropertiesExtension::class.java)
            extra.set(&amp;quot;site_config_path&amp;quot;, value)
        }

        return project
    }

    @Test
    fun `should work with valid property`() {
        val project = createProjectWithProperty(&amp;quot;src/jbake/settings/site.yml&amp;quot;)
        project.plugins.apply(&amp;quot;jbake.ghpages&amp;quot;)

        val task = project.tasks.findByName(&amp;quot;initialize&amp;quot;)!!
        task.actions.forEach { it.execute(task) }

        // Assertions pour le cas normal
        assertEquals(&amp;quot;src/jbake/settings/site.yml&amp;quot;, project.properties[&amp;quot;site_config_path&amp;quot;])
    }

    @Test
    fun `should handle missing property gracefully`() {
        val project = createProjectWithProperty(null) // Pas de propriété
        project.plugins.apply(&amp;quot;jbake.ghpages&amp;quot;)

        val task = project.tasks.findByName(&amp;quot;initialize&amp;quot;)!!

        // Le plugin devrait gérer l&amp;#39;absence de propriété
        assertDoesNotThrow {
            task.actions.forEach { it.execute(task) }
        }
    }

    @Test
    fun `should handle invalid property path`() {
        val project = createProjectWithProperty(&amp;quot;invalid/path/to/config.yml&amp;quot;)
        project.plugins.apply(&amp;quot;jbake.ghpages&amp;quot;)

        val task = project.tasks.findByName(&amp;quot;initialize&amp;quot;)!!

        // Test du comportement avec un chemin invalide
        assertThrows&amp;amp;lt;FileNotFoundException&amp;amp;gt; {
            task.actions.forEach { it.execute(task) }
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_arquitetura_final_da_solução&amp;quot;&amp;gt;Arquitetura Final da Solução&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/final-architecture.svg&amp;quot; alt=&amp;quot;final architecture&amp;quot; width=&amp;quot;754&amp;quot; height=&amp;quot;631&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_as_vantagens_desta_abordagem&amp;quot;&amp;gt;As Vantagens desta Abordagem&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Isolamento Completo&amp;lt;/strong&amp;gt;: O teste não depende de nenhum arquivo externo. Ele é autônomo e pode ser executado de forma confiável em qualquer ambiente (local, CI/CD, etc.).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Clareza e Intenção&amp;lt;/strong&amp;gt;: O teste declara explicitamente as pré-condições para sua execução. Quem lê o teste vê imediatamente que o plugin requer a propriedade`site_config_path`para funcionar.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Manutenabilidade&amp;lt;/strong&amp;gt;Se o nome da propriedade mudar, basta atualizá-lo em apenas um local no teste, sem precisar manipular os arquivos de configuração do teste.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Flexibilidade&amp;lt;/strong&amp;gt;Tornou-se trivial testar diferentes cenários simplesmente alterando o valor da propriedade injetada em cada teste (valor válido, inválido, ausente, etc.).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Desempenho&amp;lt;/strong&amp;gt;: Sem leitura de arquivos, tudo acontece na memória, o que deixa os testes mais rápidos.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Reprodutibilidade&amp;lt;/strong&amp;gt;Os testes são determinísticos porque não dependem do estado do sistema de arquivos.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_boas_práticas_e_dicas&amp;quot;&amp;gt;Boas Práticas e Dicas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_criar_um_método_utilitário&amp;quot;&amp;gt;Criar um Método Utilitário&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Méthode utilitaire pour la réutilisation&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;class GradleTestUtils {
    companion object {
        fun createProjectWithProperties(
            projectDir: File,
            properties: Map&amp;amp;lt;String, String&amp;amp;gt;
        ): Project {
            val project = ProjectBuilder.builder()
                .withProjectDir(projectDir)
                .build()

            val extra = project.extensions.getByType(ExtraPropertiesExtension::class.java)
            properties.forEach { (key, value) -&amp;amp;gt;
                extra.set(key, value)
            }

            return project
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_validação_das_propriedades&amp;quot;&amp;gt;Validação das Propriedades&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Validation robuste&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test
fun `should validate property injection`() {
    val project = createProjectWithProperty(&amp;quot;test-value&amp;quot;)

    // Vérifications multiples
    assertTrue(project.hasProperty(&amp;quot;site_config_path&amp;quot;))
    assertEquals(&amp;quot;test-value&amp;quot;, project.property(&amp;quot;site_config_path&amp;quot;))
    assertNotNull(project.properties[&amp;quot;site_config_path&amp;quot;])

    // Vérification que la propriété est accessible par le plugin
    project.plugins.apply(&amp;quot;jbake.ghpages&amp;quot;)
    // ... reste du test
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em vez de lutar para fazer ler arquivos de configuração a um ambiente de teste unitário, a melhor prática consiste em simular o estado necessário. O uso de`ExtraPropertiesExtension`Para definir programaticamente as propriedades do Gradle é o método mais limpo e mais robusto para realizar testes unitários de plugins eficazes e fiáveis.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta técnica transforma um problema de dependência externa em uma simples injeção de dependência, no cerne mesmo da filosofia do Desenvolvimento Orientado por Testes (TDD). Ela oferece controle total sobre o ambiente de teste, mantendo o isolamento necessário para testes unitários de qualidade.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os diagramas e exemplos apresentados neste artigo mostram como essa abordagem pode ser implementada de forma progressiva e metódica, permitindo criar um conjunto de testes robusto e mantível para seus plugins Gradle.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Otimizar o Gerenciamento das Tarefas do Gradle com Kotlin DSL e buildSrc</title>
            <link >https://cheroliv.com//blog/2025/0079_gradle_tasks_tests_report_post.html</link>
            <pubDate>Fri, 11 Jul 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0079_gradle_tasks_tests_report_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_centralizar_as_configurações_com_allprojects_e_subprojects&amp;quot;&amp;gt;Centralizar as Configurações com &amp;lt;code&amp;gt;allprojects&amp;lt;/code&amp;gt; e `subprojects&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_sourceplantuml&amp;quot;&amp;gt;[source,plantuml]&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_enduml&amp;quot;&amp;gt;@enduml&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_sourcekotlin&amp;quot;&amp;gt;[source,kotlin]&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A automação das tarefas de construção é crucial para todo projeto de software, e o Gradle, com seu DSL Kotlin, oferece uma flexibilidade excepcional. Durante nossa conversa, exploramos como centralizar e reutilizar a lógica de construção, especialmente para os relatórios de testes, ao aproveitar&amp;lt;strong&amp;gt;todos os projetos&amp;lt;/strong&amp;gt;, &amp;lt;strong&amp;gt;buildSrc&amp;lt;/strong&amp;gt;e tarefas personalizadas em Kotlin.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_centralizar_as_configurações_com_allprojects_e_subprojects&amp;quot;&amp;gt;Centralizar as Configurações com &amp;lt;code&amp;gt;allprojects&amp;lt;/code&amp;gt; e `subprojects&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os blocos`allprojects` et `subprojects`no seu`build.gradle.kts`A raiz são fundamentais para aplicar configurações comuns através do seu projeto multi-módulo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;*&amp;lt;code&amp;gt;allprojects { &amp;amp;#8230;&amp;amp;#8203; }&amp;lt;/code&amp;gt;: Aplique a configuração para&amp;lt;strong&amp;gt;projeto raiz e a todos os seus subprojetos&amp;lt;/strong&amp;gt;. Ideal para definir um`group`, uma`version`, alguns`repositories`comuns *&amp;lt;code&amp;gt;subprojects { &amp;amp;#8230;&amp;amp;#8203; }`Aplica a configuração&amp;lt;strong&amp;gt;exclusivamente aos subprojetos&amp;lt;/strong&amp;gt;, excluindo o projeto raiz. Perfeito para aplicar plugins específicos aos módulos (como`java&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;kotlin-jvm&amp;lt;/code&amp;gt;) ou dependências comuns às suas bibliotecas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está um exemplo ilustrativo :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// build.gradle.kts (projet racine)
plugins {
    base // Appliqué au projet racine
}

allprojects {
    group = &amp;quot;com.example&amp;quot;
    version = &amp;quot;1.0.0&amp;quot;

    repositories {
        mavenCentral()
    }

    tasks.withType&amp;amp;lt;org.gradle.api.tasks.testing.Test&amp;amp;gt; {
        useJUnitPlatform() // Configuration commune des tests pour tous les projets
    }
}

subprojects {
    apply(plugin = &amp;quot;java&amp;quot;)
    apply(plugin = &amp;quot;org.jetbrains.kotlin.jvm&amp;quot;)

    dependencies {
        implementation(&amp;quot;org.jetbrains.kotlin:kotlin-stdlib-jdk8&amp;quot;)
    }

}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;amp;lt;/think&amp;amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;amp;lt;/think&amp;amp;gt;`buildSrc`: O Canivete Suíço da Lógica de Build&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando a lógica das suas tarefas se tornar complexa ou precisar ser reutilizada,&amp;lt;strong&amp;gt;buildSrc&amp;lt;/strong&amp;gt;É a solução preferida. É um módulo especial do Gradle que é compilado antes dos scripts de construção principais, tornando suas classes disponíveis no class path de todo o seu build.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Por que usar`buildSrc`para as tarefas?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;reutilizabilidade&amp;lt;/strong&amp;gt;Uma tarefa definida em`buildSrc`pode ser aplicada a qualquer projeto do build.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Organização&amp;lt;/strong&amp;gt;: Centraliza o código de compilação, tornando-o mais limpo e manutenível.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Type Safety e Autocompletar&amp;lt;/strong&amp;gt;: O código Kotlin em`buildSrc`é compilado, oferecendo a verificação de erros e o autocomplete da sua IDE, melhorando a experiência de desenvolvimento.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;\=== Diagrama de Fluxo`buildSrc`(Código PlantUML)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para gerar este diagrama, copie o código abaixo e cole-o em uma ferramenta que suporte PlantUML.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_sourceplantuml&amp;quot;&amp;gt;[source,plantuml]&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;@startuml skinparam handwritten true skinparam monochrome true&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;rectangle &amp;quot;Processo de Construção Gradle&amp;quot; { componente &amp;quot;buildSrc&amp;quot; como BS { arquivo &amp;quot;MyCustomTask.kt&amp;quot; como T arquivo &amp;quot;MyConventionPlugin.kt&amp;quot; como P } componente &amp;quot;Root Project&amp;quot; como RP componente &amp;quot;Subproject A&amp;quot; como SA componente &amp;quot;Subproject B&amp;quot; como SB }&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;T --\&amp;amp;gt; P : &amp;quot;está definido em P --\&amp;amp;gt; RP : &amp;quot;is applied to P --\&amp;amp;gt; SA : &amp;quot;is applied to P -&amp;amp;#8594; SB : &amp;quot;é aplicado a&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;RP --|\&amp;amp;gt; SA : &amp;quot;contém RP --|\&amp;amp;gt; SB : &amp;quot;contém&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;RP -up&amp;amp;#8594; BS : &amp;quot;depende de (para a lógica de construção) SA -up-\&amp;amp;gt; BS : &amp;quot;depende de (para a lógica de construção) SB -up-\&amp;amp;gt; BS : &amp;quot;depende de (para a lógica de construção)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;nota à direita de T Classes de tarefas personalizadas (ex: OpenTestReportTask) nota final&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;nota à direita de P Plugins de convenção que registram as tarefas nota final&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_enduml&amp;quot;&amp;gt;@enduml&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;\== Criação de Tarefas de Relatórios Abstratas&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para gerenciar os relatórios de testes, concebemos uma abordagem modular utilizando uma classe de tarefa abstrata em`buildSrc`. Conforme às suas necessidades, esta classe pode herdar de`DefaultTask` ou de &amp;lt;code&amp;gt;Exec&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;\=== Tarefa Abstrata`OpenTestReportTask`(heredando de`DefaultTask`)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta abordagem é recomendada se você precisar de uma lógica Kotlin personalizada que interaja com o sistema de arquivos ou outras APIs do Gradle e então execute um comando externo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_sourcekotlin&amp;quot;&amp;gt;[source,kotlin]&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;package com.yourpackage&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;import org.gradle.api.DefaultTask import org.gradle.api.tasks.Input import org.gradle.api.tasks.TaskAction import java.io.File&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;abstract class OpenTestReportTask : DefaultTask() {&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-none hljs&amp;quot;&amp;gt;----
----
init {
    group = &amp;quot;verification&amp;quot;
    description = &amp;quot;Opens a test report in Firefox.&amp;quot;
    dependsOn(&amp;quot;check&amp;quot;) // Assure que les rapports sont générés
}

@get:Input
abstract var reportPath: String

@TaskAction
fun openReport() {
    val separator = File.separator
    val reportFile = project.layout.projectDirectory.asFile.toPath()
        .resolve(reportPath.replace(&amp;quot;/&amp;quot;, separator))
        .toAbsolutePath()
        .toFile()

    if (!reportFile.exists()) {
        logger.warn(&amp;quot;Report file does not exist: $reportFile. Ensure &amp;#39;check&amp;#39; ran.&amp;quot;)
        return
    }

    project.exec {
        commandLine(&amp;quot;firefox&amp;quot;, &amp;quot;--new-tab&amp;quot;, reportFile.absolutePath)
    }
    logger.lifecycle(&amp;quot;Opened test report: ${reportFile.absolutePath}&amp;quot;)
}
----

## }

\=== Implementações Concretas

Essas classes herdam da tarefa abstrata e definem o caminho específico do relatório.

## [source,kotlin]

pacote com.yourpackage

classe abstrata ReportUnitTestsTask : OpenTestReportTask() { init { Abre o relatório de teste unitário no Firefox. reportPath = &amp;quot;build/reports/tests/test/index.html } }

package com.yourpackage

abstract class ReportFunctionalTestsTask : OpenTestReportTask() { init { description = &amp;quot;Abre o relatório de teste funcional no Firefox.&amp;quot; reportPath = &amp;quot;build/reports/tests/functionalTest/index.html&amp;quot; } }

\=== Registro das Tarefas

No`build.gradle.kts`do seu projeto raiz :

## [source,kotlin]

tasks.register\&amp;amp;lt;com.yourpackage.ReportUnitTestsTask\&amp;amp;gt;(&amp;quot;reportTests&amp;quot;) {} tasks.register\&amp;amp;lt;com.yourpackage.ReportFunctionalTestsTask\&amp;amp;gt;(&amp;quot;reportFunctionalTests&amp;quot;) {}

\=== Diagrama UML das Tarefas de Relatório (Código PlantUML)

Copie o código abaixo e cole-o em uma ferramenta que suporte PlantUML.

## [source,plantuml]

@startuml skinparam handwritten true skinparam monocromático verdadeiro

classe abstrata DefaultTask { }

classe abstrata Exec { \+ commandLine(args: String...) \+ exec() }

classe abstrata OpenTestReportTask estende DefaultTask { \+ group: String = &amp;quot;verification \+ descrição: String \+ dependsOn(&amp;quot;check&amp;quot;) \+ abstract reportPath: String \+ openReport() : void }

classe abstrata AbstractJbakeExecTask extends Exec { \+ group: String = &amp;quot;verification + descrição: String \+ dependsOn(&amp;quot;check&amp;quot;) \+ abstract reportRelativePath: String \+ exec() : void }

class ReportUnitTestsTask extends OpenTestReportTask { \+ reportPath: String = &amp;quot;build/reports/tests/test/index.html }

class ReportFunctionalTestsTask extends OpenTestReportTask { \+ reportPath: String = &amp;quot;build/reports/tests/functionalTest/index.html }

class ReportJbakeTestsTask extends AbstractJbakeExecTask { \+ reportRelativePath: String = &amp;quot;build/reports/tests/test/index.html }

class ReportJbakeFunctionalTestsTask extends AbstractJbakeExecTask { \+ reportRelativePath: String = &amp;quot;build/reports/tests/functionalTest/index.html }

OpenTestReportTask \&amp;amp;lt;-- ReportUnitTestsTask OpenTestReportTask \&amp;amp;lt;-- ReportFunctionalTestsTask

AbstractJbakeExecTask &amp;amp;lt;-- ReportJbakeTestsTask AbstractJbakeExecTask \&amp;amp;lt;-- ReportJbakeFunctionalTestsTask

DefaultTask \&amp;amp;lt;|-- OpenTestReportTask Executar \&amp;amp;lt;|-- AbstractJbakeExecTask DefaultTask \&amp;amp;lt;|-- Exec

## @enduml

\== Tarefa Abstrata`AbstractJbakeExecTask`(herdando de`Exec`)

Se a sua tarefa consiste principalmente em executar um comando externo com argumentos variáveis, herde diretamente de**Exec**é mais direto.

## [source,kotlin]

package com.yourpackage

import org.gradle.api.tasks.Exec import org.gradle.api.tasks.Input import java.io.File

abstract class AbstractJbakeExecTask : Exec() {

----

init { group = &amp;quot;verification&amp;quot; description = &amp;quot;Opens a Jbake project report in Firefox.&amp;quot; dependsOn(&amp;quot;check&amp;quot;) }

@get:Input abstract var reportRelativePath: String

override fun exec() { val separator = File.separator val reportFile = project.layout.projectDirectory.asFile.toPath() .resolve(reportRelativePath.replace(&amp;quot;/&amp;quot;, separator)) .toAbsolutePath() .toFile()

if (!reportFile.exists()) { logger.warn(&amp;quot;Report file does not exist: $reportFile. Ensure &amp;#39;check&amp;#39; ran.&amp;quot;) return }

commandLine(&amp;quot;firefox&amp;quot;, &amp;quot;--new-tab&amp;quot;, reportFile.absolutePath) logger.lifecycle(&amp;quot;Attempting to open report: ${reportFile.absolutePath}&amp;quot;) super.exec() // Appelle la méthode exec() de la super-classe Exec }

## }

\=== Implementações Concretas para`Exec`

## [source,kotlin]

pacote com.yourpackage

abstract class ReportJbakeTestsTask : AbstractJbakeExecTask() { init { description = &amp;quot;Abre o relatório de testes unitários do Jbake no Firefox. reportRelativePath = &amp;quot;build/reports/tests/test/index.html } }

package com.yourpackage

abstract class ReportJbakeFunctionalTestsTask : AbstractJbakeExecTask() { init { description = &amp;quot;Abre o relatório de teste funcional no Firefox.&amp;quot; reportRelativePath = &amp;quot;build/reports/tests/functionalTest/index.html&amp;quot; } }

Visibilidade das Tarefas`buildSrc`

As tarefas e classes que você define em**buildSrc**são :

***Visíveis e utilizáveis**por todos os projetos do seu build principal (raiz e subprojetos). É por isso que você pode usar`com.yourpackage.ReportUnitTestsTask`em`allprojects { ... }`. ***Não executáveis**diretamente como tarefas de buildSrc (por exemplo`gradle :buildSrc:reportTests`não funcionaria se a tarefa não fosse registrada especificamente em`buildSrc/build.gradle.kts`)`buildSrc`É um módulo de compilação, não é um módulo de aplicativo executável para essas tarefas do build principal.

Executar um relatório para os testes de`buildSrc`ele mesmo

Si `buildSrc`tem os seus próprios testes e gera relatórios, você pode gravar uma tarefa de relatório diretamente em`buildSrc/build.gradle.kts`:

## [source,kotlin]

plugins { `kotlin-jvm` }

repositórios { mavenCentral() }

tasks.withType&amp;amp;lt;Test&amp;amp;gt; { useJUnitPlatform() reports.html.outputLocation.set(layout.buildDirectory.dir(&amp;quot;reports/tests&amp;quot;)) }

import com.yourpackage.ReportJbakeTestsTask // Importe sua classe de tarefa

## tasks.register\&amp;amp;lt;ReportJbakeTestsTask\&amp;amp;gt;(&amp;quot;reportBuildSrcTests&amp;quot;) { // O caminho já está definido na classe, ele apontará para os relatórios de buildSrc }

Você poderá então executar:`./gradlew :buildSrc:test`seguido de`./gradlew :buildSrc:reportBuildSrcTests`.

Ao adotar essas práticas, você construirá sistemas de build do Gradle em Kotlin DSL que não são apenas poderosos, mas também incrivelmente modulares, manuteníveis e fáceis de entender.&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>TDD de um Plugin Gradle Kotlin configurável com JBake e JGit</title>
            <link >https://cheroliv.com//blog/2025/0078_jbake_gradle_plugin_syntaxe_post.html</link>
            <pubDate>Wed, 9 Jul 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0078_jbake_gradle_plugin_syntaxe_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_objetivo&amp;quot;&amp;gt;Objetivo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configuração_declarativa&amp;quot;&amp;gt;Configuração declarativa&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_do_plugin&amp;quot;&amp;gt;Arquitetura do plugin&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_definição_da_extensão_dsl&amp;quot;&amp;gt;Definição da extensão DSL&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_definição_da_tarefa_personalizada&amp;quot;&amp;gt;Definição da tarefa personalizada&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_interface_gitadapter&amp;quot;&amp;gt;Interface GitAdapter&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_implementação_concreta_com_jgit&amp;quot;&amp;gt;Implementação concreta com JGit&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configuração_dsl_kotlin&amp;quot;&amp;gt;Configuração DSL Kotlin&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_testes_unitários_com_mockito&amp;quot;&amp;gt;Testes unitários com Mockito&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_dependências&amp;quot;&amp;gt;dependências&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_teste_do_comportamento_git&amp;quot;&amp;gt;Teste do comportamento Git&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_próximas_etapas&amp;quot;&amp;gt;Próximas etapas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este post apresenta a abordagem completa de projeto e teste (TDD) de um plugin Gradle escrito em Kotlin DSL. Este plugin automatiza a geração de sites estáticos com &amp;lt;strong&amp;gt;JBake&amp;lt;/strong&amp;gt; e a &amp;lt;strong&amp;gt;implantação Git&amp;lt;/strong&amp;gt; via &amp;lt;strong&amp;gt;JGit&amp;lt;/strong&amp;gt;, tudo a partir de um arquivo YAML de configuração.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_objetivo&amp;quot;&amp;gt;Objetivo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Criar um plugin Gradle :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;configurável em Kotlin DSL ;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;capaz de :&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;gerar um site estático a partir do JBake ;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;implantar em um repositório Git remoto via JGit ;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;integrabel em um fluxo de trabalho CI/CD ;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;testado segundo uma abordagem TDD.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_configuração_declarativa&amp;quot;&amp;gt;Configuração declarativa&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está um exemplo de configuração YAML para modelar na nossa DSL :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-yaml hljs&amp;quot; data-lang=&amp;quot;yaml&amp;quot;&amp;gt;bake:
  srcPath: &amp;quot;./site/jbake&amp;quot;
  destDirPath: &amp;quot;bake&amp;quot;
pushPage:
  from: &amp;quot;bake&amp;quot;
  to: &amp;quot;cvs&amp;quot;
  repo:
    name: &amp;quot;trainings&amp;quot;
    repository: &amp;quot;https://github.com/pages-content/trainings.git&amp;quot;
    credentials:
      username: &amp;quot;USERNAME&amp;quot;
      password: &amp;quot;SECRET_TOKEN&amp;quot;
  branch: &amp;quot;main&amp;quot;
  message: &amp;quot;cheroliv.com&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta configuração é representada via uma extensão Gradle declarável em Kotlin DSL.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_arquitetura_do_plugin&amp;quot;&amp;gt;Arquitetura do plugin&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/jbake-asciidoctor-class-diagram.png&amp;quot; alt=&amp;quot;jbake asciidoctor class diagram&amp;quot; width=&amp;quot;568&amp;quot; height=&amp;quot;261&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_definição_da_extensão_dsl&amp;quot;&amp;gt;Definição da extensão DSL&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;open class WorkspaceEngineExtension {
    var outputDir: String = &amp;quot;build/site&amp;quot;
    var template: String = &amp;quot;freemarker&amp;quot;
    var repoUrl: String = &amp;quot;&amp;quot;
    var branch: String = &amp;quot;main&amp;quot;
    var message: String = &amp;quot;Generated by engine&amp;quot;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_definição_da_tarefa_personalizada&amp;quot;&amp;gt;Definição da tarefa personalizada&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;abstract class GenerateSiteTask : DefaultTask() {
    lateinit var gitAdapter: GitAdapter

    @get:Input abstract val repoUrl: Property&amp;amp;lt;String&amp;amp;gt;
    @get:InputDirectory abstract val outputDir: DirectoryProperty
    @get:Input abstract val branch: Property&amp;amp;lt;String&amp;amp;gt;
    @get:Input abstract val message: Property&amp;amp;lt;String&amp;amp;gt;

    @TaskAction
    fun run() {
        val repo = gitAdapter.cloneRepository(repoUrl.get(), outputDir.get().asFile)
        gitAdapter.commitAndPush(repo, branch.get(), message.get())
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_interface_gitadapter&amp;quot;&amp;gt;Interface GitAdapter&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;interface GitAdapter {
    fun cloneRepository(uri: String, directory: File): Repository
    fun commitAndPush(repo: Repository, branch: String, message: String)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_implementação_concreta_com_jgit&amp;quot;&amp;gt;Implementação concreta com JGit&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;class JGitAdapter : GitAdapter {
    override fun cloneRepository(uri: String, directory: File): Repository {
        return Git.cloneRepository()
            .setURI(uri)
            .setDirectory(directory)
            .call()
            .repository
    }

    override fun commitAndPush(repo: Repository, branch: String, message: String) {
        Git(repo).use {
            it.add().addFilepattern(&amp;quot;.&amp;quot;).call()
            it.commit().setMessage(message).call()
            it.push().call()
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_configuração_dsl_kotlin&amp;quot;&amp;gt;Configuração DSL Kotlin&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;workspaceEngine {
    outputDir = &amp;quot;build/out&amp;quot;
    template = &amp;quot;freemarker&amp;quot;
    repoUrl = &amp;quot;https://github.com/cheroliv/trainings.git&amp;quot;
    branch = &amp;quot;main&amp;quot;
    message = &amp;quot;deploy from plugin&amp;quot;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_testes_unitários_com_mockito&amp;quot;&amp;gt;Testes unitários com Mockito&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_dependências&amp;quot;&amp;gt;dependências&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;testImplementation(&amp;quot;org.mockito:mockito-core:5.12.0&amp;quot;)
testImplementation(&amp;quot;org.mockito.kotlin:mockito-kotlin:5.2.1&amp;quot;)
testImplementation(&amp;quot;org.junit.jupiter:junit-jupiter:5.10.0&amp;quot;)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_teste_do_comportamento_git&amp;quot;&amp;gt;Teste do comportamento Git&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;class GenerateSiteTaskMockitoTest {

    @TempDir
    lateinit var tempDir: File

    @Test
    fun `task should call clone and commitAndPush`() {
        val project = ProjectBuilder.builder().build()
        val task = project.tasks.create(&amp;quot;generateSite&amp;quot;, GenerateSiteTask::class.java)

        val mockGitAdapter = mock&amp;amp;lt;GitAdapter&amp;amp;gt;()
        val mockRepo = mock&amp;amp;lt;Repository&amp;amp;gt;()

        whenever(mockGitAdapter.cloneRepository(any(), any())).thenReturn(mockRepo)

        task.gitAdapter = mockGitAdapter
        task.repoUrl.set(&amp;quot;https://github.com/cheroliv/test.git&amp;quot;)
        task.outputDir.set(project.layout.projectDirectory.dir(tempDir.name))
        task.branch.set(&amp;quot;main&amp;quot;)
        task.message.set(&amp;quot;test commit&amp;quot;)

        task.run()

        verify(mockGitAdapter).cloneRepository(eq(&amp;quot;https://github.com/cheroliv/test.git&amp;quot;), any())
        verify(mockGitAdapter).commitAndPush(eq(mockRepo), eq(&amp;quot;main&amp;quot;), eq(&amp;quot;test commit&amp;quot;))
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/jbake-asciidoctor-sequence-diagram.png&amp;quot; alt=&amp;quot;jbake asciidoctor sequence diagram&amp;quot; width=&amp;quot;777&amp;quot; height=&amp;quot;326&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A separação das responsabilidades via`GitAdapter`permite aplicar completamente o TDD no desenvolvimento de plugin Gradle:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;as chamadas Git são abstratas e testáveis;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;A configuração DSL é limpa e clara;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O plugin permanece agnóstico em relação à implementação real.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta abordagem garante alta testabilidade e extensibilidade (para GitHub API, GitLab, etc.).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_próximas_etapas&amp;quot;&amp;gt;Próximas etapas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Integrar a geração JBake como um`BakeAdapter`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Adicionar testes de integração com`GradleRunner`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Suportar o carregamento automático de arquivos`site.yml`via SnakeYAML&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>AsciiDoc : Descubra e domine a sintaxe para uma documentação eficaz</title>
            <link >https://cheroliv.com//blog/2025/0077_asciidoc_syntaxe_post.html</link>
            <pubDate>Fri, 20 Jun 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0077_asciidoc_syntaxe_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_é_o_asciidoc&amp;quot;&amp;gt;O que é o AsciiDoc?&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_escolher_o_asciidoc&amp;quot;&amp;gt;Por que escolher o AsciiDoc ?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_casos_de_uso_use_case&amp;quot;&amp;gt;Diagrama de casos de uso (Use Case)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_mapa_mental&amp;quot;&amp;gt;Mapa Mental&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_diagrama_de_fluxo_flux&amp;quot;&amp;gt;Diagrama de fluxo (flux)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_estrutura_básica_de_um_arquivo_asciidoc&amp;quot;&amp;gt;Estrutura básica de um arquivo AsciiDoc&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_sintaxe_fundamental&amp;quot;&amp;gt;Sintaxe fundamental&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_títulos_e_seções&amp;quot;&amp;gt;Títulos e seções&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_texto_em_negrito_itálico_e_monoespacado&amp;quot;&amp;gt;Texto em negrito, itálico e monoespacado&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_listas_com_marcadores_e_listas_numeradas&amp;quot;&amp;gt;Listas com marcadores e listas numeradas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_links_e_imagens&amp;quot;&amp;gt;Links e imagens&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_blocos_de_código&amp;quot;&amp;gt;Blocos de código&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_tabelas&amp;quot;&amp;gt;tabelas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_notas_e_admoestações&amp;quot;&amp;gt;Notas e admoestações&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_utilização_de_atributos_e_variáveis&amp;quot;&amp;gt;Utilização de atributos e variáveis&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_casos_de_uso_comuns&amp;quot;&amp;gt;Casos de uso comuns&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_boas_práticas&amp;quot;&amp;gt;Boas práticas&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusion&amp;quot;&amp;gt;Conclusion&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_para_saber_mais&amp;quot;&amp;gt;Para saber mais&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;&amp;lt;em&amp;gt;Público-alvo :&amp;lt;/em&amp;gt; Desenvolvedores, redatores técnicos e toda pessoa que deseja escrever documentação técnica ou artigos ricos.&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;AsciiDoc é uma linguagem de marcação leve, concebida para redigir documentos técnicos estruturados, artigos, livros ou ainda apresentações. Distingue-se pela sua legibilidade, riqueza sintática e capacidade de gerar diversos formatos (HTML, PDF, DocBook, etc.). Neste artigo, vamos explorar a sintaxe essencial do AsciiDoc e propor dicas para uma rápida familiarização.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_que_é_o_asciidoc&amp;quot;&amp;gt;O que é o AsciiDoc?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;AsciiDoc é uma linguagem de descrição de documentos, semelhante ao Markdown, mas mais poderoso. Ele permite estruturar eficientemente textos, títulos, listas, tabelas, blocos de código e muito mais. Sua versatilidade o torna uma escolha popular para a documentação de projetos de código aberto, a redação de livros e a publicação na web.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_por_que_escolher_o_asciidoc&amp;quot;&amp;gt;Por que escolher o AsciiDoc ?&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Sintaxe legível e intuitiva.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Suporte nativo de estruturas complexas (tabelas, notas, avisos, etc.).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Geração multi-formato (HTML, PDF, ePub, DocBook, DocBook…).&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Integração fácil com geradores de sites estáticos como JBake ou Antora.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Personalização avançada com os atributos e extensões.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_diagrama_de_casos_de_uso_use_case&amp;quot;&amp;gt;Diagrama de casos de uso (Use Case)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um diagrama de caso de uso permite apresentar as principais interações entre os usuários e o sistema. Veja um exemplo simples para um sistema de documentação:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-381f40e6e0660c0c53433ff054330b76.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;423&amp;quot; height=&amp;quot;258&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_mapa_mental&amp;quot;&amp;gt;Mapa Mental&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O mind map (mapa mental) é ideal para explorar os conceitos relacionados ao AsciiDoc e suas relações :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-e1560e9cc014158b6fbeed1e5ad8e138.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;394&amp;quot; height=&amp;quot;527&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_diagrama_de_fluxo_flux&amp;quot;&amp;gt;Diagrama de fluxo (flux)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um diagrama de fluxo permite descrever o processo de geração de um documento AsciiDoc&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;diag-plantuml-md5-51318d77b9e18062d3a9b751587670ae.png&amp;quot; alt=&amp;quot;Diagram&amp;quot; width=&amp;quot;299&amp;quot; height=&amp;quot;361&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_estrutura_básica_de_um_arquivo_asciidoc&amp;quot;&amp;gt;Estrutura básica de um arquivo AsciiDoc&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um arquivo AsciiDoc geralmente começa com um título, atributos opcionais e depois o conteúdo estruturado. Aqui está um exemplo mínimo:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;= Titre Principal
Auteur
2024-09-03
:toc:
:icons: font

Votre contenu commence ici...&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_sintaxe_fundamental&amp;quot;&amp;gt;Sintaxe fundamental&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_títulos_e_seções&amp;quot;&amp;gt;Títulos e seções&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;AsciiDoc suporta vários níveis de títulos :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;= Titre de niveau 1
== Titre de niveau 2
=== Titre de niveau 3
==== Titre de niveau 4&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_texto_em_negrito_itálico_e_monoespacado&amp;quot;&amp;gt;Texto em negrito, itálico e monoespacado&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;*gras* _italique_ `monospace`&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_listas_com_marcadores_e_listas_numeradas&amp;quot;&amp;gt;Listas com marcadores e listas numeradas&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;* Élément 1
* Élément 2

. Premier
. Deuxième
. Troisième&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_links_e_imagens&amp;quot;&amp;gt;Links e imagens&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;Lien standard : https://asciidoc.org[AsciiDoc]
Image : image::images/logo.png[AsciiDoc Logo]&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_blocos_de_código&amp;quot;&amp;gt;Blocos de código&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;[source,python]&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;def hello(): print(&amp;quot;Olá AsciiDoc !&amp;quot;)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_tabelas&amp;quot;&amp;gt;tabelas&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;|===
| Colonne 1 | Colonne 2

| Valeur A
| Valeur B

| Valeur C
| Valeur D
|===&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_notas_e_admoestações&amp;quot;&amp;gt;Notas e admoestações&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;AsciiDoc propõe uns blocos de informação visual :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;NOTE: Ceci est une note importante.
TIP: Conseil utile pour l’utilisateur.
WARNING: Attention à ce point.&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_utilização_de_atributos_e_variáveis&amp;quot;&amp;gt;Utilização de atributos e variáveis&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os atributos personalizados permitem reutilizar valores ou configurar comportamentos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-asciidoc hljs&amp;quot; data-lang=&amp;quot;asciidoc&amp;quot;&amp;gt;:project-name: AsciiDoc Explorer

Le projet s’appelle {project-name}.&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_casos_de_uso_comuns&amp;quot;&amp;gt;Casos de uso comuns&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Documentação de projeto open source (README, guias técnicas) - Redação de livros e ebooks - Geração automática de sites web estáticos (JBake, Antora) - Apresentações técnicas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_boas_práticas&amp;quot;&amp;gt;Boas práticas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Utilize títulos consistentes e um sumário automático (:toc:). - Prefira usar as admonições para chamar a atenção aos pontos-chave. - Organize seus arquivos para facilitar a manutenção. - Aproveite os blocos de código anotados para ilustrar exemplos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusion&amp;quot;&amp;gt;Conclusion&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;AsciiDoc é uma ferramenta poderosa e acessível para escrever toda documentação técnica ou artigo estruturado. Sua sintaxe rica, combinada à geração multi-formato, faz dele um aliado de escolha para desenvolvedores e redatores exigentes. Experimente o AsciiDoc em seu próximo projeto e descubra a diferença!&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_para_saber_mais&amp;quot;&amp;gt;Para saber mais&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Documentação oficial: &amp;lt;a href=&amp;quot;https://asciidoc.org&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://asciidoc.org&amp;lt;/a&amp;gt; - Asciidoctor : &amp;lt;a href=&amp;quot;https://asciidoctor.org&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://asciidoctor.org&amp;lt;/a&amp;gt; - JBake e AsciiDoc : &amp;lt;a href=&amp;quot;https://jbake.org/docs/2.6.4/#asciidoc_support&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://jbake.org/docs/2.6.4/#asciidoc_support&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Compartilhe suas experiências e dicas nos comentários !&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Expor um serviço local na Internet a partir do Ubuntu: Guia completo do encaminhamento de portas</title>
            <link >https://cheroliv.com//blog/2025/0076_port_forwarding_post.html</link>
            <pubDate>Wed, 28 May 2025 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0076_port_forwarding_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_problema_nat_e_endereços_privados&amp;quot;&amp;gt;O problema: NAT e endereços privados&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_1_identificar_o_seu_endereço_ip_público&amp;quot;&amp;gt;Etapa 1: Identificar o seu endereço IP público&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_2_conheça_o_endereço_ip_local_da_sua_máquina_ubuntu&amp;quot;&amp;gt;Passo 2: Conheça o endereço IP local da sua máquina Ubuntu&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_3_configurar_o_encaminhamento_de_portas_na_sua_box&amp;quot;&amp;gt;Passo 3: Configurar o encaminhamento de portas na sua box&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_acesso_à_interface_de_administração&amp;quot;&amp;gt;Acesso à interface de administração&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_configuração_da_redireção&amp;quot;&amp;gt;Configuração da redireção&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_4_configuração_do_serviço_no_ubuntu&amp;quot;&amp;gt;Etapa 4: Configuração do serviço no Ubuntu&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_passo_5_configuração_do_firewall_ubuntu&amp;quot;&amp;gt;Passo 5: Configuração do firewall Ubuntu&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_etapa_6_teste_e_validação&amp;quot;&amp;gt;Etapa 6: Teste e validação&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_teste_a_partir_da_rede_local&amp;quot;&amp;gt;Teste a partir da rede local&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_teste_de_fora&amp;quot;&amp;gt;Teste de fora&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_boas_práticas_e_segurança&amp;quot;&amp;gt;Boas práticas e segurança&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_segurança_básica&amp;quot;&amp;gt;Segurança básica&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_monitoramento_de_conexões&amp;quot;&amp;gt;Monitoramento de conexões&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_utilização_de_um_proxy_reverso&amp;quot;&amp;gt;Utilização de um proxy reverso&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_alternativas_ao_encaminhamento_de_portas&amp;quot;&amp;gt;Alternativas ao encaminhamento de portas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_soluções_em_nuvem&amp;quot;&amp;gt;Soluções em nuvem&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_vps_e_proxy_reverso&amp;quot;&amp;gt;VPS e proxy reverso&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_solução_de_problemas&amp;quot;&amp;gt;Solução de problemas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_problemas_comuns&amp;quot;&amp;gt;Problemas comuns&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_depurar_rede&amp;quot;&amp;gt;Depurar rede&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;.\ _Tempo de leitura:&amp;lt;/em&amp;gt; 15 minutos&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Público-alvo : Intermediário&amp;lt;/div&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Pré-requisitos assumidos&amp;lt;/strong&amp;gt; : conhecimento básico de Ubuntu, terminal, conceitos básicos de rede&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Conceitos técnicos&amp;lt;/strong&amp;gt; : NAT, encaminhamento de portas, firewall, proxy reverso&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Comandos do sistema&amp;lt;/strong&amp;gt;: utilização do terminal, configuração de rede&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Seções avançadas&amp;lt;/strong&amp;gt; : monitoramento, segurança, alternativas cloud&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_problema_nat_e_endereços_privados&amp;quot;&amp;gt;O problema: NAT e endereços privados&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Problemática : Você está desenvolvendo um aplicativo web em sua máquina Ubuntu e deseja torná-lo acessível de fora da sua rede doméstica? Você está no lugar certo! Neste artigo, vamos ver como expor um serviço local (por exemplo, na porta 8080) para a Internet configurando o encaminhamento de porta na sua box de internet.&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Por padrão, sua caixa de internet utiliza o NAT (Network Address Translation) para compartilhar um único endereço IP público entre todos os dispositivos da sua rede doméstica. Seus dispositivos locais utilizam endereços IP privados (192.168.x.x, 10.x.x.x, etc.) que não são roteáveis na Internet.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando você inicia um serviço em sua máquina Ubuntu (por exemplo, um servidor web na porta 8080), ele só é acessível a partir da sua rede local. Para torná-lo acessível a partir da Internet, precisamos configurar um redirecionamento de porta.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_etapa_1_identificar_o_seu_endereço_ip_público&amp;quot;&amp;gt;Etapa 1: Identificar o seu endereço IP público&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Antes de tudo, vamos obter o endereço IP público da sua conexão à Internet :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Plusieurs méthodes au choix
curl ifconfig.me
curl ipecho.net/plain
wget -qO- http://ipecho.net/plain

# Ou encore
curl -s checkip.amazonaws.com&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este endereço IP é aquele que os internautas usarão para acessar o seu serviço.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_passo_2_conheça_o_endereço_ip_local_da_sua_máquina_ubuntu&amp;quot;&amp;gt;Passo 2: Conheça o endereço IP local da sua máquina Ubuntu&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Sua máquina Ubuntu tem um endereço IP privado na sua rede local. Para saber:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Afficher toutes les interfaces réseau
ip addr show

# Ou plus spécifiquement pour l&amp;#39;interface principale
ip route get 1.1.1.1 | awk &amp;#39;{print $7; exit}&amp;#39;

# Alternative avec hostname
hostname -I | awk &amp;#39;{print $1}&amp;#39;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Anota este endereço (por exemplo`192.168.1.100`), vamos precisar disso para a configuração.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_passo_3_configurar_o_encaminhamento_de_portas_na_sua_box&amp;quot;&amp;gt;Passo 3: Configurar o encaminhamento de portas na sua box&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A configuração varia conforme o modelo da sua caixa, mas o princípio permanece o mesmo:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_acesso_à_interface_de_administração&amp;quot;&amp;gt;Acesso à interface de administração&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Abra seu navegador e vá para a interface web da sua caixa:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Freebox&amp;lt;/strong&amp;gt;:`192.168.1.1` ou &amp;lt;code&amp;gt;mafreebox.freebox.fr&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Livebox Orange&amp;lt;/strong&amp;gt;:`192.168.1.1`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Caixa SFR&amp;lt;/strong&amp;gt; : &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Bbox Bouygues&amp;lt;/strong&amp;gt;:`192.168.1.254`&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_configuração_da_redireção&amp;quot;&amp;gt;Configuração da redireção&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Procure a seção dedicada ao encaminhamento de portas (os nomes variam) :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Redirecionamento de portas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;NAT/PAT&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Encaminhamento de portas&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Servidores de jogos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Crie uma nova regra com estes parâmetros :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 25%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 75%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Campo&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Valor&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nome/Descrição&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Serviço Web Ubuntu&amp;quot; (ou outro nome explícito)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Porta externa (ou porta pública)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;8080&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Endereço IP interno&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O endereço IP da sua máquina Ubuntu (ex: 192.168.1.100)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Porta interna (ou porta privada)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;8080&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Protocolo&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;TCP&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Estado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;ativado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_etapa_4_configuração_do_serviço_no_ubuntu&amp;quot;&amp;gt;Etapa 4: Configuração do serviço no Ubuntu&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Certifique-se de que seu serviço esteja escutando em todas as interfaces, não apenas no localhost:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# ✅ Correct : écoute sur toutes les interfaces
python3 -m http.server 8080 --bind 0.0.0.0

# ❌ Incorrect : écoute seulement en local
python3 -m http.server 8080 --bind 127.0.0.1&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para verificar se seu serviço está ouvindo corretamente :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Vérifier les ports en écoute
sudo netstat -tlnp | grep :8080
# ou avec ss (plus moderne)
sudo ss -tlnp | grep :8080&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você deveria ver algo como`0.0.0.0:8080`e não`127.0.0.1:8080`.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_passo_5_configuração_do_firewall_ubuntu&amp;quot;&amp;gt;Passo 5: Configuração do firewall Ubuntu&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ubuntu utiliza o UFW (Uncomplicated Firewall) por padrão. Verifique seu status e permita a porta, se necessário :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Vérifier le statut du pare-feu
sudo ufw status

# Si le pare-feu est actif, autoriser le port 8080
sudo ufw allow 8080/tcp

# Ou plus spécifiquement pour un service web
sudo ufw allow &amp;#39;Apache&amp;#39;  # si vous utilisez Apache
sudo ufw allow &amp;#39;Nginx Full&amp;#39;  # si vous utilisez Nginx&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_etapa_6_teste_e_validação&amp;quot;&amp;gt;Etapa 6: Teste e validação&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_teste_a_partir_da_rede_local&amp;quot;&amp;gt;Teste a partir da rede local&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Primeiro, teste o acesso a partir de outra máquina da sua rede local :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Remplacez par l&amp;#39;IP locale de votre machine Ubuntu
curl http://192.168.1.100:8080&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_teste_de_fora&amp;quot;&amp;gt;Teste de fora&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Agora, teste desde a Internet usando seu IP público :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Remplacez par votre IP publique
curl http://VOTRE_IP_PUBLIQUE:8080&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você também pode usar ferramentas on-line como:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://www.yougetsignal.com/tools/open-ports/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://www.yougetsignal.com/tools/open-ports/&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://canyouseeme.org/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://canyouseeme.org/&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_boas_práticas_e_segurança&amp;quot;&amp;gt;Boas práticas e segurança&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_segurança_básica&amp;quot;&amp;gt;Segurança básica&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Configurer fail2ban pour protéger contre les attaques par force brute
sudo apt update &amp;amp;amp;&amp;amp;amp; sudo apt install fail2ban

# Limiter l&amp;#39;accès avec UFW (exemple : autoriser seulement certaines IP)
sudo ufw allow from 203.0.113.0/24 to any port 8080&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_monitoramento_de_conexões&amp;quot;&amp;gt;Monitoramento de conexões&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Surveiller les connexions en temps réel
sudo netstat -an | grep :8080

# Logs des connexions (selon votre application)
sudo tail -f /var/log/nginx/access.log  # pour Nginx
sudo journalctl -f -u your-service      # pour un service systemd&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_utilização_de_um_proxy_reverso&amp;quot;&amp;gt;Utilização de um proxy reverso&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para melhor segurança e flexibilidade, considere o uso de um proxy reverso como Nginx :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-nginx&amp;quot; data-lang=&amp;quot;nginx&amp;quot;&amp;gt;server {
    listen 80;
    server_name votre-domaine.com;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_alternativas_ao_encaminhamento_de_portas&amp;quot;&amp;gt;Alternativas ao encaminhamento de portas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_soluções_em_nuvem&amp;quot;&amp;gt;Soluções em nuvem&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;ngrok&amp;lt;/strong&amp;gt;túnel seguro temporário&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Túnel Cloudflare&amp;lt;/strong&amp;gt;: solução gratuita e segura&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;serveo.net&amp;lt;/strong&amp;gt;: túnel SSH simples&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Exemple avec ngrok
ngrok http 8080

# Exemple avec serveo
ssh -R 80:localhost:8080 serveo.net&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_vps_e_proxy_reverso&amp;quot;&amp;gt;VPS e proxy reverso&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para uso profissional, considere usar um VPS com um proxy reverso apontando para sua infraestrutura local via VPN.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_solução_de_problemas&amp;quot;&amp;gt;Solução de problemas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_problemas_comuns&amp;quot;&amp;gt;Problemas comuns&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 40%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 60%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Problema&amp;lt;/th&amp;gt;
&amp;lt;th class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;Solução&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Serviço inacessível de fora&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Verifique a configuração do encaminhamento de portas e que o serviço esteja escutando em 0.0.0.0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Conexão recusada&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O serviço não está iniciado ou está escutando apenas em 127.0.0.1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Tempo limite de conexão&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Problema de firewall (box ou Ubuntu) ou encaminhamento de portas mal configurado&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;IP público muda regularmente&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Configure um DNS dinâmico (DynDNS, No-IP, etc.)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_depurar_rede&amp;quot;&amp;gt;Depurar rede&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;# Tester la connectivité locale
telnet localhost 8080

# Tester depuis une autre machine du réseau
telnet 192.168.1.100 8080

# Vérifier les routes réseau
ip route show

# Analyser le trafic réseau
sudo tcpdump -i any port 8080&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Expor um serviço local na Internet requer uma configuração cuidadosa do encaminhamento de portas, da segurança e do monitoramento. Embora essa abordagem seja perfeita para desenvolvimento ou projetos pessoais, considere as alternativas mais robustas (VPS, CDN, serviços de nuvem) para uso em produção.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O importante é sempre manter a segurança em mente: use senhas fortes, atualize regularmente seu sistema e monitore o acesso aos seus serviços expostos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não hesite em compartilhar suas experiências e perguntas nos comentários!&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Debugging Mockito: Resolver o erro &quot;Wanted but not invoked&quot; nos testes de plugins Gradle</title>
            <link >https://cheroliv.com//blog/2025/0099_debugging-mockito-gradle-plugin_post.html</link>
            <pubDate>Sun, 17 Nov 2024 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2025/0099_debugging-mockito-gradle-plugin_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Table des matières&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_contexto_plugin_gradle_bakery&amp;quot;&amp;gt;O contexto: Plugin Gradle Bakery&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_problema_1_verificar_o_mock_errado&amp;quot;&amp;gt;Problema #1: Verificar o mock errado&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_diagnóstico&amp;quot;&amp;gt;O diagnóstico&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução&amp;quot;&amp;gt;A solução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_problema_2_unfinishedstubbingexception_em_cascata&amp;quot;&amp;gt;Problema #2 : UnfinishedStubbingException em cascata&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_diagnóstico_2&amp;quot;&amp;gt;O diagnóstico&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_2&amp;quot;&amp;gt;A solução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_problema_3_o_arquivo_de_configuração_não_existe&amp;quot;&amp;gt;Problema #3: O arquivo de configuração não existe&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_diagnóstico_3&amp;quot;&amp;gt;O diagnóstico&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_3&amp;quot;&amp;gt;A solução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_problema_4_afterevaluate_e_nullpointerexception&amp;quot;&amp;gt;Problema #4 : afterEvaluate e NullPointerException&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_diagnóstico_4&amp;quot;&amp;gt;O diagnóstico&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_4&amp;quot;&amp;gt;A solução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_solução_completa&amp;quot;&amp;gt;A solução completa&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_lições_aprendidas&amp;quot;&amp;gt;Lições aprendidas&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_verificar_o_mock_correto&amp;quot;&amp;gt;Verificar o mock correto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_2_evitar_os_mocks_aninhados&amp;quot;&amp;gt;2. Evitar os mocks aninhados&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_3_simular_os_callbacks_de_avaliação&amp;quot;&amp;gt;3. Simular os callbacks de avaliação&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_4_testar_a_resolução_dos_caminhos&amp;quot;&amp;gt;4. Testar a resolução dos caminhos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_arquitetura_de_teste_final&amp;quot;&amp;gt;Arquitetura de teste final&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_recursos&amp;quot;&amp;gt;Recursos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Durante o desenvolvimento do plugin Gradle&amp;lt;strong&amp;gt;Padaria&amp;lt;/strong&amp;gt;para meu blog JBake, encontrei um problema aparentemente simples: um teste unitário que falhou com o erro`Wanted but not invoked`. O que parecia ser um bug trivial revelou-se ser um caso de escola perfeito para entender as sutilezas do mocking com Mockito e Kotlin.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Neste artigo, levo você em uma jornada de depuração metódica, onde cada solução revela um novo problema, até a resolução final.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_contexto_plugin_gradle_bakery&amp;quot;&amp;gt;O contexto: Plugin Gradle Bakery&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plugin Bakery é um wrapper em torno do JBake que facilita a publicação de sites estáticos. Aqui está sua estrutura simplificada:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;class BakeryPlugin : Plugin&amp;amp;lt;Project&amp;amp;gt; {
    override fun apply(project: Project) {
        val extension = project.extensions.create(
            &amp;quot;bakery&amp;quot;,
            BakeryExtension::class.java
        )

        project.afterEvaluate {
            if (!project.layout.projectDirectory.asFile
                    .resolve(extension.configPath.get()).exists()) {
                println(&amp;quot;config file does not exists&amp;quot;)
            } else {
                // C&amp;#39;EST ICI QUE ÇA SE PASSE
                project.plugins.apply(JBakePlugin::class.java)

                val site = FileSystemManager.from(project, extension.configPath.get())
                // Configuration de JBake...
            }
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O teste que falhava era simples :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test
fun `plugin applies jbake gradle plugin`() {
    val project = createMockProject()
    val plugin = BakeryPlugin()

    plugin.apply(project)

    verify(project.plugins).apply(JBakePlugin::class.java)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O erro :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;Wanted but not invoked:
pluginContainer.apply(class org.jbake.gradle.JBakePlugin);
Actually, there were zero interactions with this mock.&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_problema_1_verificar_o_mock_errado&amp;quot;&amp;gt;Problema #1: Verificar o mock errado&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_diagnóstico&amp;quot;&amp;gt;O diagnóstico&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/mockito-problem-1.svg&amp;quot; alt=&amp;quot;mockito problem 1&amp;quot; width=&amp;quot;687&amp;quot; height=&amp;quot;521&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O problema: o Mockito não pode rastrear as interações em`project.plugins`porque é apenas um getter que retorna o mock real`mockPluginContainer`. Deve ser feita diretamente na instância do mock&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_solução&amp;quot;&amp;gt;A solução&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Modificar`createMockProject()`para retornar os dois objetos :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;private fun createMockProject(): Pair&amp;amp;lt;Project, PluginContainer&amp;amp;gt; {
    val mockPluginContainer = mock&amp;amp;lt;PluginContainer&amp;amp;gt;()
    val mockProject = mock&amp;amp;lt;Project&amp;amp;gt; {
        on { plugins } doReturn mockPluginContainer
    }
    return Pair(mockProject, mockPluginContainer)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E adaptar o teste :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test
fun `plugin applies jbake gradle plugin`() {
    val (project, mockPluginContainer) = createMockProject()
    val plugin = BakeryPlugin()

    plugin.apply(project)

    // ✅ Vérification directe sur le bon mock
    verify(mockPluginContainer).apply(JBakePlugin::class.java)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_problema_2_unfinishedstubbingexception_em_cascata&amp;quot;&amp;gt;Problema #2 : UnfinishedStubbingException em cascata&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_diagnóstico_2&amp;quot;&amp;gt;O diagnóstico&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Após a aplicação da primeira correção, um novo erro apareceu:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-text hljs&amp;quot; data-lang=&amp;quot;text&amp;quot;&amp;gt;UnfinishedStubbingException:
Unfinished stubbing detected here
Hints:
 3. you are stubbing the behaviour of another mock inside
    before &amp;#39;thenReturn&amp;#39; instruction is completed&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O código problemático usava a sintaxe DSL do Mockito-Kotlin :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val mockProject = mock&amp;amp;lt;Project&amp;amp;gt; {
    on { extensions } doReturn mockExtensionContainer
    on { plugins } doReturn mockPluginContainer
    on { logger } doReturn mock()  // ❌ PROBLÈME ICI !
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/mockito-problem-2.svg&amp;quot; alt=&amp;quot;mockito problem 2&amp;quot; width=&amp;quot;690&amp;quot; height=&amp;quot;316&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_solução_2&amp;quot;&amp;gt;A solução&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Criar&amp;lt;strong&amp;gt;todos&amp;lt;/strong&amp;gt;Os mocks fora de qualquer bloco de stubbing, depois configurá-los com`whenever()`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;private fun createMockProject(): Pair&amp;amp;lt;Project, PluginContainer&amp;amp;gt; {
    // 1️⃣ Créer TOUS les mocks d&amp;#39;abord
    val mockPluginContainer = mock&amp;amp;lt;PluginContainer&amp;amp;gt;()
    val mockExtensionContainer = mock&amp;amp;lt;ExtensionContainer&amp;amp;gt;()
    val mockLogger = mock&amp;amp;lt;org.gradle.api.logging.Logger&amp;amp;gt;()
    val mockProject = mock&amp;amp;lt;Project&amp;amp;gt;()

    // 2️⃣ Configurer les mocks séparément avec whenever()
    whenever(mockProject.plugins).thenReturn(mockPluginContainer)
    whenever(mockProject.extensions).thenReturn(mockExtensionContainer)
    whenever(mockProject.logger).thenReturn(mockLogger)

    return Pair(mockProject, mockPluginContainer)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock tip&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-tip&amp;quot; title=&amp;quot;Tip&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Regra de ouro&amp;lt;/strong&amp;gt;Nunca chamar`mock()`dentro de um bloco de configuração de mock. Sempre criar os mocks primeiro, depois configurá-los.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_problema_3_o_arquivo_de_configuração_não_existe&amp;quot;&amp;gt;Problema #3: O arquivo de configuração não existe&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_diagnóstico_3&amp;quot;&amp;gt;O diagnóstico&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Mesmo com os mocks corretos, o teste sempre falhava porque o plugin não encontrava o arquivo de configuração :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// Dans BakeryPlugin.kt
if (!project.layout.projectDirectory.asFile
        .resolve(extension.configPath.get()).exists()) {
    println(&amp;quot;config file does not exists&amp;quot;)
    return@afterEvaluate  // ❌ Sort avant d&amp;#39;appliquer JBake !
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/mockito-problem-3.svg&amp;quot; alt=&amp;quot;mockito problem 3&amp;quot; width=&amp;quot;605&amp;quot; height=&amp;quot;373&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_solução_3&amp;quot;&amp;gt;A solução&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Configurar os mocks para que a resolução do caminho funcione :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;private fun createMockProject(): Pair&amp;amp;lt;Project, PluginContainer&amp;amp;gt; {
    // ... autres mocks ...

    val configFile = File(&amp;quot;../../site.yml&amp;quot;).canonicalFile
    val projectDir = configFile.parentFile

    // Configuration cohérente des chemins
    whenever(mockConfigPathProperty.get()).thenReturn(&amp;quot;site.yml&amp;quot;)
    whenever(mockProjectDirectory.asFile).thenReturn(projectDir)

    // Maintenant : projectDir.resolve(&amp;quot;site.yml&amp;quot;) existe ! ✅
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/path-resolution.svg&amp;quot; alt=&amp;quot;path resolution&amp;quot; width=&amp;quot;1797&amp;quot; height=&amp;quot;5592&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_problema_4_afterevaluate_e_nullpointerexception&amp;quot;&amp;gt;Problema #4 : afterEvaluate e NullPointerException&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_diagnóstico_4&amp;quot;&amp;gt;O diagnóstico&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O plugin aplica JBake em um bloco`afterEvaluate`, e acede a`buildDirectory.dir()`:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;project.afterEvaluate {
    // ...
    project.tasks.withType(JBakeTask::class.java)
        .getByName(&amp;quot;bake&amp;quot;).apply {
            output = project.layout.buildDirectory
                .dir(site.bake.destDirPath)  // ❌ NPE ici !
                .get()
                .asFile
        }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O mock de`buildDirectory.dir()&amp;lt;code&amp;gt;retornava`null&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_solução_4&amp;quot;&amp;gt;A solução&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;zombar`afterEvaluate`para que ele execute imediatamente e configurar completamente`buildDirectory`(I will output nothing, as there is no provided text to translate.)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;private fun createMockProject(): Pair&amp;amp;lt;Project, PluginContainer&amp;amp;gt; {
    // ... autres mocks ...

    val mockBuildDirectory = mock&amp;amp;lt;DirectoryProperty&amp;amp;gt;()
    val buildDir = File(projectDir, &amp;quot;build&amp;quot;)

    // Mocker dir() pour retourner un Provider valide
    whenever(mockBuildDirectory.dir(any&amp;amp;lt;String&amp;amp;gt;())).doAnswer { invocation -&amp;amp;gt;
        val path = invocation.arguments[0] as String
        val mockDirProvider = mock&amp;amp;lt;Provider&amp;amp;lt;Directory&amp;amp;gt;&amp;amp;gt;()
        val mockDir = mock&amp;amp;lt;Directory&amp;amp;gt;()

        whenever(mockDir.asFile).thenReturn(File(buildDir, path))
        whenever(mockDirProvider.get()).thenReturn(mockDir)

        mockDirProvider
    }

    // Mocker afterEvaluate pour exécution immédiate
    whenever(mockProject.afterEvaluate(any&amp;amp;lt;Action&amp;amp;lt;Project&amp;amp;gt;&amp;amp;gt;())).doAnswer { invocation -&amp;amp;gt;
        val action = invocation.arguments[0] as Action&amp;amp;lt;Project&amp;amp;gt;
        action.execute(mockProject)  // ✅ Exécution synchrone
        null
    }

    return Pair(mockProject, mockPluginContainer)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/after-evaluate-flow.svg&amp;quot; alt=&amp;quot;after evaluate flow&amp;quot; width=&amp;quot;785&amp;quot; height=&amp;quot;357&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_a_solução_completa&amp;quot;&amp;gt;A solução completa&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está a função`createMockProject()`por fim, que resolve todos os problemas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;private fun createMockProject(): Pair&amp;amp;lt;Project, PluginContainer&amp;amp;gt; {
    // 1️⃣ CRÉER tous les mocks (pas de nested mocks !)
    val mockPluginContainer = mock&amp;amp;lt;PluginContainer&amp;amp;gt;()
    val mockExtensionContainer = mock&amp;amp;lt;ExtensionContainer&amp;amp;gt;()
    val mockLogger = mock&amp;amp;lt;org.gradle.api.logging.Logger&amp;amp;gt;()
    val mockTaskContainer = mock&amp;amp;lt;TaskContainer&amp;amp;gt;()
    val mockConfigPathProperty = mock&amp;amp;lt;Property&amp;amp;lt;String&amp;amp;gt;&amp;amp;gt;()
    val mockBakeryExtension = mock&amp;amp;lt;BakeryExtension&amp;amp;gt;()
    val mockProjectDirectory = mock&amp;amp;lt;Directory&amp;amp;gt;()
    val mockBuildDirectory = mock&amp;amp;lt;DirectoryProperty&amp;amp;gt;()
    val mockProjectLayout = mock&amp;amp;lt;ProjectLayout&amp;amp;gt;()
    val mockProject = mock&amp;amp;lt;Project&amp;amp;gt;()

    // 2️⃣ CONFIGURER la résolution des chemins
    val configFile = File(&amp;quot;../../site.yml&amp;quot;).canonicalFile
    val projectDir = configFile.parentFile
    val buildDir = File(projectDir, &amp;quot;build&amp;quot;)

    whenever(mockConfigPathProperty.get()).thenReturn(&amp;quot;site.yml&amp;quot;)
    whenever(mockConfigPathProperty.isPresent).thenReturn(true)
    whenever(mockBakeryExtension.configPath).thenReturn(mockConfigPathProperty)
    whenever(mockProjectDirectory.asFile).thenReturn(projectDir)

    // 3️⃣ CONFIGURER buildDirectory avec dir()
    whenever(mockBuildDirectory.dir(any&amp;amp;lt;String&amp;amp;gt;())).doAnswer { invocation -&amp;amp;gt;
        val path = invocation.arguments[0] as String
        val mockDirProvider = mock&amp;amp;lt;Provider&amp;amp;lt;Directory&amp;amp;gt;&amp;amp;gt;()
        val mockDir = mock&amp;amp;lt;Directory&amp;amp;gt;()
        whenever(mockDir.asFile).thenReturn(File(buildDir, path))
        whenever(mockDirProvider.get()).thenReturn(mockDir)
        mockDirProvider
    }

    // 4️⃣ ASSEMBLER le projet
    whenever(mockProjectLayout.projectDirectory).thenReturn(mockProjectDirectory)
    whenever(mockProjectLayout.buildDirectory).thenReturn(mockBuildDirectory)

    whenever(mockExtensionContainer.create(&amp;quot;bakery&amp;quot;, BakeryExtension::class.java))
        .thenReturn(mockBakeryExtension)
    whenever(mockExtensionContainer.getByType(BakeryExtension::class.java))
        .thenReturn(mockBakeryExtension)

    whenever(mockProject.extensions).thenReturn(mockExtensionContainer)
    whenever(mockProject.plugins).thenReturn(mockPluginContainer)
    whenever(mockProject.tasks).thenReturn(mockTaskContainer)
    whenever(mockProject.layout).thenReturn(mockProjectLayout)
    whenever(mockProject.logger).thenReturn(mockLogger)
    whenever(mockProject.projectDir).thenReturn(projectDir)

    // 5️⃣ CONFIGURER afterEvaluate pour exécution immédiate
    whenever(mockProject.afterEvaluate(any&amp;amp;lt;Action&amp;amp;lt;Project&amp;amp;gt;&amp;amp;gt;())).doAnswer { invocation -&amp;amp;gt;
        val action = invocation.arguments[0] as Action&amp;amp;lt;Project&amp;amp;gt;
        action.execute(mockProject)
        null
    }

    return Pair(mockProject, mockPluginContainer)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;E o teste final que passa :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test
fun `plugin applies jbake gradle plugin`() {
    val (project, mockPluginContainer) = createMockProject()
    val plugin = BakeryPlugin()

    plugin.apply(project)

    verify(mockPluginContainer).apply(JBakePlugin::class.java) // ✅ SUCCÈS !
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_lições_aprendidas&amp;quot;&amp;gt;Lições aprendidas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/lessons-learned.svg&amp;quot; alt=&amp;quot;lessons learned&amp;quot; width=&amp;quot;748&amp;quot; height=&amp;quot;472&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_verificar_o_mock_correto&amp;quot;&amp;gt;Verificar o mock correto&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// ❌ FAUX
verify(project.plugins).apply(JBakePlugin::class.java)

// ✅ CORRECT
val (project, mockPluginContainer) = createMockProject()
verify(mockPluginContainer).apply(JBakePlugin::class.java)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_2_evitar_os_mocks_aninhados&amp;quot;&amp;gt;2. Evitar os mocks aninhados&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// ❌ FAUX - UnfinishedStubbingException
val mockProject = mock&amp;amp;lt;Project&amp;amp;gt; {
    on { logger } doReturn mock()  // Nested mock creation !
}

// ✅ CORRECT - Créer séparément
val mockLogger = mock&amp;amp;lt;org.gradle.api.logging.Logger&amp;amp;gt;()
val mockProject = mock&amp;amp;lt;Project&amp;amp;gt;()
whenever(mockProject.logger).thenReturn(mockLogger)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_3_simular_os_callbacks_de_avaliação&amp;quot;&amp;gt;3. Simular os callbacks de avaliação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// ✅ afterEvaluate doit s&amp;#39;exécuter pour les tests
whenever(mockProject.afterEvaluate(any())).doAnswer { invocation -&amp;amp;gt;
    val action = invocation.arguments[0] as Action&amp;amp;lt;Project&amp;amp;gt;
    action.execute(mockProject)
    null
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_4_testar_a_resolução_dos_caminhos&amp;quot;&amp;gt;4. Testar a resolução dos caminhos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin hljs&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;// Toujours vérifier que les chemins se résolvent correctement
val extension = project.extensions.getByType(BakeryExtension::class.java)
val configPath = extension.configPath.get()
val projectDir = project.layout.projectDirectory.asFile
val resolvedConfig = projectDir.resolve(configPath)

println(&amp;quot;Resolved config: ${resolvedConfig.absolutePath}&amp;quot;)
println(&amp;quot;Exists: ${resolvedConfig.exists()}&amp;quot;)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_arquitetura_de_teste_final&amp;quot;&amp;gt;Arquitetura de teste final&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;./images/test-architecture.svg&amp;quot; alt=&amp;quot;test architecture&amp;quot; width=&amp;quot;1758&amp;quot; height=&amp;quot;5468&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O que parecia ser um simples problema de teste acabou sendo um excelente caso de estudo sobre:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;As sutilezas do Mockito com Kotlin&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;A importância de mockar os objetos corretos&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;A gestão de callbacks assíncronos nos testes&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;A resolução de caminhos nos plugins Gradle&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A depuração metódica, ao compreender cada camada do problema, permitiu chegar a uma solução robusta e manutenível.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;admonitionblock tip&amp;quot;&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;icon&amp;quot;&amp;gt;
&amp;lt;i class=&amp;quot;fa icon-tip&amp;quot; title=&amp;quot;Tip&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Conselho para seus testes&amp;lt;/strong&amp;gt;Se você encontrar &amp;quot;Wanted but not invoked&amp;quot; com Mockito, pergunte-se sempre :
1. Estou verificando o mock certo?
2. Todos os meus mocks são criados fora dos blocos de configuração?
3. Meus callbacks se executam realmente?
4. É que meus caminhos se resolvem corretamente?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_recursos&amp;quot;&amp;gt;Recursos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/mockito/mockito-kotlin&amp;quot;&amp;gt;Documentação do Mockito-Kotlin&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.gradle.org/current/userguide/custom_plugins.html&amp;quot;&amp;gt;Guia de desenvolvimento de plugins do Gradle&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://jbake.org/&amp;quot;&amp;gt;JBake Gerador de Sites Estáticos&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Você encontrou problemas semelhantes nos seus testes? Compartilhe sua experiência nos comentários!&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Kotlin Arrow: Dominando a navegação na mônada Either</title>
            <link >https://cheroliv.com//blog/2024/0075_kotlin_arrow-traverse_right_and_left_post.html</link>
            <pubDate>Tue, 24 Sep 2024 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2024/0075_kotlin_arrow-traverse_right_and_left_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_que_é_a_mónada_either&amp;quot;&amp;gt;O que é a mónada Either ?&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_por_que_usar_either&amp;quot;&amp;gt;Por que usar Either?&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_contexto&amp;quot;&amp;gt;Contexto&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_as_diferentes_abordagens&amp;quot;&amp;gt;As diferentes abordagens&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_abordagem_clássica_com_when&amp;quot;&amp;gt;A abordagem clássica com `when&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_uso_de_fold_para_uma_abordagem_mais_concisa&amp;quot;&amp;gt;Uso de &amp;lt;code&amp;gt;fold&amp;lt;/code&amp;gt; para uma abordagem mais concisa&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_transformação_com_map_e_mapleft&amp;quot;&amp;gt;Transformação com &amp;lt;code&amp;gt;map&amp;lt;/code&amp;gt; e `mapLeft&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_tratamento_de_erros_com_getorelse&amp;quot;&amp;gt;Tratamento de erros com `getOrElse&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_ações_laterais_com_onleft_e_onright&amp;quot;&amp;gt;Ações laterais com &amp;lt;code&amp;gt;onLeft&amp;lt;/code&amp;gt; e `onRight&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_encadeamento_de_operações_com_flatmap&amp;quot;&amp;gt;Encadeamento de operações com `flatMap&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_transformação_bidirecional_com_bimap&amp;quot;&amp;gt;Transformação bidirecional com `bimap&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_inversão_dos_lados_com_swap&amp;quot;&amp;gt;Inversão dos lados com `swap&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_utilização_de_tap_e_tapleft&amp;quot;&amp;gt;Utilização de &amp;lt;code&amp;gt;tap&amp;lt;/code&amp;gt; e &amp;lt;code&amp;gt;tapLeft&amp;lt;/code&amp;gt; :&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_utilização_de_recover&amp;quot;&amp;gt;Utilização de &amp;lt;code&amp;gt;recover&amp;lt;/code&amp;gt; :&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_casos_de_uso_práticos&amp;quot;&amp;gt;Casos de uso práticos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_para_ir_além&amp;quot;&amp;gt;Para ir além&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;_Público-alvo : _ Desenvolvedores Kotlin de nível intermediário a avançado.&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;No mundo da programação funcional em Kotlin, a biblioteca Arrow oferece ferramentas poderosas para lidar com erros e casos alternativos. Entre essas ferramentas, a mônada`Either`destaca-se pela sua capacidade de representar dois estados possíveis: sucesso (Right) ou fracasso (Left). Mas como navegar eficientemente entre esses dois estados? É isto que vamos explorar neste artigo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_que_é_a_mónada_either&amp;quot;&amp;gt;O que é a mónada Either ?&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A mônada`Either`é uma estrutura de dados que representa duas possibilidades mutualmente exclusivas. Em Kotlin com Arrow, ela é frequentemente usada para lidar com os casos de sucesso e de falha de uma operação, oferecendo uma alternativa elegante às exceções tradicionais.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_por_que_usar_either&amp;quot;&amp;gt;Por que usar Either?&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Gerenciamento explícito de erros: Obriga o desenvolvedor a considerar os casos de erro.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Composição funcional: facilita o encadeamento de operações que podem falhar.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Type‑safety : Os erros são tipados, o que ajuda a manipulá-los de maneira mais precisa.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Sem exceções: Evite efeitos colaterais e interrupções inesperadas do fluxo de execução.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_contexto&amp;quot;&amp;gt;Contexto&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Imagine que você está desenvolvendo uma API REST comhttps://spring.io/projects/spring-boot/[Spring Boot,windows=ler-mais-tarde] et &amp;lt;a href=&amp;quot;https://kotlinlang.org/&amp;quot;&amp;gt;Kotlin&amp;lt;/a&amp;gt;, usando a bibliotecahttps://arrow-kt.io/[Arrow,janelas=ler-mais-tarde]. Você tem uma função`findOneUserByEmail`que retorna um`Either&amp;amp;lt;Throwable, User&amp;amp;gt;`. Como você pode tratar esse resultado de forma elegante e funcional?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Temos uma classe User :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@file:Suppress(
    &amp;quot;RemoveRedundantQualifierName&amp;quot;,
    &amp;quot;MemberVisibilityCanBePrivate&amp;quot;,
    &amp;quot;SqlNoDataSourceInspection&amp;quot;
)

package webapp.users

import arrow.core.Either
import arrow.core.left
import arrow.core.right
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.databind.ObjectMapper
import jakarta.validation.constraints.NotNull
import jakarta.validation.constraints.Pattern
import jakarta.validation.constraints.Size
import org.springframework.beans.factory.getBean
import org.springframework.context.ApplicationContext
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate
import org.springframework.r2dbc.core.DatabaseClient
import org.springframework.r2dbc.core.awaitOne
import org.springframework.r2dbc.core.awaitRowsUpdated
import webapp.core.property.ANONYMOUS_USER
import webapp.core.property.EMPTY_STRING
import webapp.core.utils.AppUtils.cleanField
import webapp.users.EntityModel.Companion.ID_MEMBER
import webapp.users.User.UserDao.Attributes.EMAIL_ATTR
import webapp.users.User.UserDao.Attributes.ID_ATTR
import webapp.users.User.UserDao.Attributes.LANG_KEY_ATTR
import webapp.users.User.UserDao.Attributes.LOGIN_ATTR
import webapp.users.User.UserDao.Attributes.PASSWORD_ATTR
import webapp.users.User.UserDao.Attributes.VERSION_ATTR
import webapp.users.User.UserDao.Constraints.LOGIN_REGEX
import webapp.users.User.UserDao.Fields.EMAIL_FIELD
import webapp.users.User.UserDao.Fields.ID_FIELD
import webapp.users.User.UserDao.Fields.LANG_KEY_FIELD
import webapp.users.User.UserDao.Fields.LOGIN_FIELD
import webapp.users.User.UserDao.Fields.PASSWORD_FIELD
import webapp.users.User.UserDao.Fields.VERSION_FIELD
import webapp.users.User.UserDao.Relations.INSERT
import webapp.users.security.Role
import webapp.users.security.Role.RoleDao
import webapp.users.security.UserRole.UserRoleDao
import java.util.*
import jakarta.validation.constraints.Email as EmailConstraint

data class User(
    override val id: UUID? = null,

    @field:NotNull
    @field:Pattern(regexp = LOGIN_REGEX)
    @field:Size(min = 1, max = 50)
    val login: String,

    @JsonIgnore
    @field:NotNull
    @field:Size(min = 60, max = 60)
    val password: String = EMPTY_STRING,

    @field:EmailConstraint
    @field:Size(min = 5, max = 254)
    val email: String = EMPTY_STRING,

    @JsonIgnore
    val roles: MutableSet&amp;amp;lt;Role&amp;amp;gt; = mutableSetOf(Role(ANONYMOUS_USER)),

    @field:Size(min = 2, max = 10)
    val langKey: String = EMPTY_STRING,

    @JsonIgnore
    val version: Long = -1,
) : EntityModel&amp;amp;lt;UUID&amp;amp;gt;() {

    companion object {
        @JvmStatic
        fun main(args: Array&amp;amp;lt;String&amp;amp;gt;) = println(UserDao.Relations.sqlScript)
    }

    object UserDao {
        object Constraints {
            // Regex for acceptable logins
            const val LOGIN_REGEX =
                &amp;quot;^(?&amp;amp;gt;[a-zA-Z0-9!$&amp;amp;amp;*+=?^_`{|}~.-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)|(?&amp;amp;gt;[_.@A-Za-z0-9-]+)$&amp;quot;
            const val PASSWORD_MIN: Int = 4
            const val PASSWORD_MAX: Int = 16
            const val IMAGE_URL_DEFAULT = &amp;quot;https://placehold.it/50x50&amp;quot;
            const val PHONE_REGEX = &amp;quot;^(\\+|00)?[1-9]\\d{0,49}\$&amp;quot;
        }

        object Members {
            const val PASSWORD_MEMBER = &amp;quot;password&amp;quot;
            const val ROLES_MEMBER = &amp;quot;roles&amp;quot;
        }

        object Fields {
            const val ID_FIELD = &amp;quot;`id`&amp;quot;
            const val LOGIN_FIELD = &amp;quot;`login`&amp;quot;
            const val PASSWORD_FIELD = &amp;quot;`password`&amp;quot;
            const val EMAIL_FIELD = &amp;quot;`email`&amp;quot;
            const val LANG_KEY_FIELD = &amp;quot;`lang_key`&amp;quot;
            const val VERSION_FIELD = &amp;quot;`version`&amp;quot;
        }

        object Attributes {
            val ID_ATTR = ID_FIELD.cleanField()
            val LOGIN_ATTR = LOGIN_FIELD.cleanField()
            val PASSWORD_ATTR = PASSWORD_FIELD.cleanField()
            val EMAIL_ATTR = EMAIL_FIELD.cleanField()
            const val LANG_KEY_ATTR = &amp;quot;langKey&amp;quot;
            val VERSION_ATTR = VERSION_FIELD.cleanField()
        }

        object Relations {
            const val TABLE_NAME = &amp;quot;`user`&amp;quot;
            const val SQL_SCRIPT = &amp;quot;&amp;quot;&amp;quot;
            CREATE TABLE IF NOT EXISTS $TABLE_NAME (
                $ID_FIELD                     UUID default random_uuid() PRIMARY KEY,
                $LOGIN_FIELD                  VARCHAR,
                $PASSWORD_FIELD               VARCHAR,
                $EMAIL_FIELD                  VARCHAR,
                $LANG_KEY_FIELD               VARCHAR,
                $VERSION_FIELD                bigint
            );
            CREATE UNIQUE INDEX IF NOT EXISTS `uniq_idx_user_login`
            ON $TABLE_NAME ($LOGIN_FIELD);
            CREATE UNIQUE INDEX IF NOT EXISTS `uniq_idx_user_email`
            ON $TABLE_NAME ($EMAIL_FIELD);
&amp;quot;&amp;quot;&amp;quot;

            @Suppress(&amp;quot;SqlDialectInspection&amp;quot;)
            const val INSERT = &amp;quot;&amp;quot;&amp;quot;
            insert into $TABLE_NAME (
                $LOGIN_FIELD, $EMAIL_FIELD,
                $PASSWORD_FIELD, $LANG_KEY_FIELD,
                $VERSION_FIELD
            ) values ( :login, :email, :password, :langKey, :version)&amp;quot;&amp;quot;&amp;quot;

            @JvmStatic
            val sqlScript: String
                get() = setOf(
                    UserDao.Relations.SQL_SCRIPT,
                    RoleDao.Relations.SQL_SCRIPT,
                    UserRoleDao.Relations.SQL_SCRIPT
                ).joinToString(&amp;quot;&amp;quot;)
                    .trimMargin()
        }

        object Dao {
            val Pair&amp;amp;lt;User, ApplicationContext&amp;amp;gt;.toJson: String
                get() = second.getBean&amp;amp;lt;ObjectMapper&amp;amp;gt;().writeValueAsString(first)

            suspend fun Pair&amp;amp;lt;User, ApplicationContext&amp;amp;gt;.save(): Either&amp;amp;lt;Throwable, Long&amp;amp;gt; = try {
                second.getBean&amp;amp;lt;R2dbcEntityTemplate&amp;amp;gt;()
                    .databaseClient
                    .sql(INSERT)
                    .bind(LOGIN_ATTR, first.login)
                    .bind(EMAIL_ATTR, first.email)
                    .bind(PASSWORD_ATTR, first.password)
                    .bind(LANG_KEY_ATTR, first.langKey)
                    .bind(VERSION_ATTR, first.version)
                    .fetch()
                    .awaitRowsUpdated()
                    .right()
            } catch (e: Throwable) {
                e.left()
            }


            suspend fun ApplicationContext.findOneUserByEmail(
                email: String
            ): Either&amp;amp;lt;Throwable, User&amp;amp;gt; = try {
                getBean&amp;amp;lt;DatabaseClient&amp;amp;gt;()
                    .sql(&amp;quot;SELECT * FROM `user` WHERE LOWER(email) = LOWER(:email)&amp;quot;)
                    .bind(&amp;quot;email&amp;quot;, email)
                    .fetch()
                    .awaitOne()
                    .let { row -&amp;amp;gt;
                        User(
                            id = row[ID_ATTR] as UUID?,
                            login = row[LOGIN_ATTR] as String,
                            password = row[PASSWORD_ATTR] as String,
                            email = row[EMAIL_ATTR] as String,
                            langKey = row[LANG_KEY_ATTR] as String,
                            version = row[VERSION_ATTR] as Long
                        )
                    }.right()
            } catch (e: Throwable) {
                e.left()
            }
        }
    }

    /** Account REST API URIs */
    object UserRestApis {
        const val API_AUTHORITY = &amp;quot;/api/authorities&amp;quot;
        const val API_USERS = &amp;quot;/api/users&amp;quot;
        const val API_SIGNUP = &amp;quot;/signup&amp;quot;
        const val API_SIGNUP_PATH = &amp;quot;$API_USERS$API_SIGNUP&amp;quot;
        const val API_ACTIVATE = &amp;quot;/activate&amp;quot;
        const val API_ACTIVATE_PATH = &amp;quot;$API_USERS$API_ACTIVATE?key=&amp;quot;
        const val API_ACTIVATE_PARAM = &amp;quot;{activationKey}&amp;quot;
        const val API_ACTIVATE_KEY = &amp;quot;key&amp;quot;
        const val API_RESET_INIT = &amp;quot;/reset-password/init&amp;quot;
        const val API_RESET_FINISH = &amp;quot;/reset-password/finish&amp;quot;
        const val API_CHANGE = &amp;quot;/change-password&amp;quot;
        const val API_CHANGE_PATH = &amp;quot;$API_USERS$API_CHANGE&amp;quot;
    }
}

// Abstract entity model with Generic ID, which can be of any type
abstract class EntityModel&amp;amp;lt;T&amp;amp;gt;(
    open val id: T? = null
) {
    companion object {
        const val ID_MEMBER = &amp;quot;id&amp;quot;
    }
}

// Generic extension function that allows the ID to be applied to any EntityModel type
inline fun &amp;amp;lt;reified T : EntityModel&amp;amp;lt;ID&amp;amp;gt;, ID&amp;amp;gt; T.withId(id: ID): T {
    // Use reflection to create a copy with the passed ID
    return this::class.constructors.first { it.parameters.any { param -&amp;amp;gt; param.name == ID_MEMBER } }
        .call(id, *this::class.constructors.first().parameters.drop(1).map { param -&amp;amp;gt;
            this::class.members.first { member -&amp;amp;gt; member.name == param.name }.call(this)
        }.toTypedArray())
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_as_diferentes_abordagens&amp;quot;&amp;gt;As diferentes abordagens&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_abordagem_clássica_com_when&amp;quot;&amp;gt;A abordagem clássica com `when&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val user: User by lazy { userFactory(USER) }

val result: Either&amp;amp;lt;Throwable, User&amp;amp;gt; = context.findOneUserByEmail(user.email)

when (result) {
    is Either.Left -&amp;amp;gt; {
        val error = result.value
        println(&amp;quot;Erreur : ${error.message}&amp;quot;)
    }
    is Either.Right -&amp;amp;gt; {
        val user = result.value
        println(&amp;quot;Utilisateur trouvé : ${user.login}&amp;quot;)
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este método, embora simples e legível, não aproveita plenamente as capacidades funcionais do Arrow.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_uso_de_fold_para_uma_abordagem_mais_concisa&amp;quot;&amp;gt;Uso de &amp;lt;code&amp;gt;fold&amp;lt;/code&amp;gt; para uma abordagem mais concisa&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;result.fold(
{ error -&amp;amp;gt; println(&amp;quot;Erreur : ${error.message}&amp;quot;) },
{ user -&amp;amp;gt; println(&amp;quot;Utilisateur trouvé : ${user.login}&amp;quot;) }
)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`fold`permite definir ações para os dois casos (Left e Right) de forma concisa e elegante.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_transformação_com_map_e_mapleft&amp;quot;&amp;gt;Transformação com &amp;lt;code&amp;gt;map&amp;lt;/code&amp;gt; e `mapLeft&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val processedResult = result
    .map { user -&amp;amp;gt; &amp;quot;Utilisateur trouvé : ${user.login}&amp;quot; }
    .mapLeft { error -&amp;amp;gt; &amp;quot;Erreur : ${error.message}&amp;quot; }

println(processedResult.merge())&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta abordagem permite transformar os valores contidos em Either, preservando sua estrutura, ideal para cadeias de processamento mais complexas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_tratamento_de_erros_com_getorelse&amp;quot;&amp;gt;Tratamento de erros com `getOrElse&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val user = result.getOrElse { error -&amp;amp;gt;
    println(&amp;quot;Erreur : ${error.message}&amp;quot;)
    User(login = &amp;quot;default&amp;quot;, email = &amp;quot;default@example.com&amp;quot;) // utilisateur par défaut
}
println(&amp;quot;Login : ${user.login}&amp;quot;)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`getOrElse`oferece um tratamento elegante de erros ao permitir fornecer um valor padrão.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_ações_laterais_com_onleft_e_onright&amp;quot;&amp;gt;Ações laterais com &amp;lt;code&amp;gt;onLeft&amp;lt;/code&amp;gt; e `onRight&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;result.onLeft { error -&amp;amp;gt; println(&amp;quot;Erreur : ${error.message}&amp;quot;) }
.onRight { user -&amp;amp;gt; println(&amp;quot;Utilisateur trouvé : ${user.login}&amp;quot;) }&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esses métodos permitem executar ações em cada lado sem modificar o Either, perfeito para o registro ou os efeitos secundários leves.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_encadeamento_de_operações_com_flatmap&amp;quot;&amp;gt;Encadeamento de operações com `flatMap&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;fun findUser(email: String): Either&amp;amp;lt;Throwable, User&amp;amp;gt; = // ... implémentation

fun getUserPermissions(user: User): Either&amp;amp;lt;Throwable, List&amp;amp;lt;String&amp;amp;gt;&amp;amp;gt; = // ... implémentation

val userPermissions = findUser(&amp;quot;user@example.com&amp;quot;)
    .flatMap { user -&amp;amp;gt; getUserPermissions(user) }&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;flatMap`é útil para encadear operações que retornam elas próprias`Either&amp;lt;/code&amp;gt;, evitando assim os`Either`aninhados.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_transformação_bidirecional_com_bimap&amp;quot;&amp;gt;Transformação bidirecional com `bimap&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val result: Either&amp;amp;lt;Throwable, User&amp;amp;gt; = // ... obtention du résultat
val processedResult = result.bimap(
    { error -&amp;amp;gt; &amp;quot;Erreur: ${error.message}&amp;quot; },
    { user -&amp;amp;gt; &amp;quot;Utilisateur: ${user.login}&amp;quot; }
)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;`bimap`permite transformar tanto o lado esquerdo quanto o lado direito em uma única operação.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_inversão_dos_lados_com_swap&amp;quot;&amp;gt;Inversão dos lados com `swap&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val result: Either&amp;amp;lt;Throwable, User&amp;amp;gt; = // ... obtention du résultat
val swapped = result.swap()&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;swap`é útil quando você quiser inverter os lados de um`Either&amp;lt;/code&amp;gt;, por exemplo para adaptar a interface de uma função para outra.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_utilização_de_tap_e_tapleft&amp;quot;&amp;gt;Utilização de &amp;lt;code&amp;gt;tap&amp;lt;/code&amp;gt; e &amp;lt;code&amp;gt;tapLeft&amp;lt;/code&amp;gt; :&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;result.tap { user -&amp;amp;gt; println(&amp;quot;Utilisateur trouvé : ${user.login}&amp;quot;) }
.tapLeft { error -&amp;amp;gt; println(&amp;quot;Erreur : ${error.message}&amp;quot;) }&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Similar a`onLeft` et &amp;lt;code&amp;gt;onRight&amp;lt;/code&amp;gt;, mas esses métodos retornam o Either original, o que é útil para o encadeamento de operações.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_utilização_de_recover&amp;quot;&amp;gt;Utilização de &amp;lt;code&amp;gt;recover&amp;lt;/code&amp;gt; :&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val recoveredUser = result.recover { error -&amp;amp;gt;
    println(&amp;quot;Erreur récupérée : ${error.message}&amp;quot;)
    User(login = &amp;quot;recovered&amp;quot;, email = &amp;quot;recovered@example.com&amp;quot;)
}
println(&amp;quot;Login : ${recoveredUser.login}&amp;quot;)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este método permite transformar um Either.Left em Either.Right fornecendo um valor de substituição.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_casos_de_uso_práticos&amp;quot;&amp;gt;Casos de uso práticos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Utilize`fold`para operações simples que exigem um tratamento para cada caso. - Prefiram`map` et &amp;lt;code&amp;gt;mapLeft`para transformações de dados sem mudar a estrutura da`Either&amp;lt;/code&amp;gt;. - Opte por`flatMap`durante o encadeamento de operações que podem falhar. Use`recover`para fornecer um valor padrão em caso de erro. Escolha`onLeft` et &amp;lt;code&amp;gt;onRight&amp;lt;/code&amp;gt; (ou &amp;lt;code&amp;gt;tap&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;tapLeft&amp;lt;/code&amp;gt;) para efeitos colaterais como logging. Use`bimap`para transformar os dois lados em uma única operação. - Aplique`swap`quando você precisar adaptar a interface de uma função para outra.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Cada uma dessas abordagens tem suas vantagens de acordo com o contexto de uso. Os métodos como`fold`, &amp;lt;code&amp;gt;map&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;mapLeft&amp;lt;/code&amp;gt;, et `recover`são particularmente úteis quando você deseja encadear várias operações ou transformar os dados de forma funcional.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A mônade`Either`O d&amp;amp;#8217;Arrow oferece uma flexibilidade notável para lidar com os casos de sucesso e erro em suas aplicações Kotlin. Ao dominar essas diferentes abordagens, você será capaz de escrever um código mais robusto, mais legível e mais funcional.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No seu próximo projeto, não hesite em explorar essas técnicas para tirar o máximo proveito da programação funcional com Kotlin e Arrow!&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_para_ir_além&amp;quot;&amp;gt;Para ir além&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Documentação oficial do Arrow :https://arrow-kt.io/docs/apidocs/arrow-core/arrow.core/-either/[Seta Ou] - Kotlin Coroutines com Arrow :https://arrow-kt.io/docs/fx/[Flecha Fx Corrotinas]&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não se esqueça de compartilhar suas experiências e suas técnicas favoritas para trabalhar com`Either`nos comentários abaixo !&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Guia prático: pesquisa de arquivos no Linux</title>
            <link >https://cheroliv.com//blog/2024/0074_seach_file_linux_post.html</link>
            <pubDate>Mon, 23 Sep 2024 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2024/0074_seach_file_linux_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_utilização_do_comando_find&amp;quot;&amp;gt;Utilização do comando `find&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_opções_adicionais_para_find&amp;quot;&amp;gt;Opções adicionais para `find&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_utilização_do_comando_locate&amp;quot;&amp;gt;Utilização do comando `locate&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_uso_do_comando_grep_para_pesquisar_texto_em_arquivos&amp;quot;&amp;gt;Uso do comando &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; para pesquisar texto em arquivos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;A problemática : Procurar ficheiros num sistema operativo Linux através do terminal.&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Neste artigo, vamos explorar como pesquisar um arquivo no Ubuntu usando um terminal Zsh. Zsh (Z shell) é um interpretador de comandos poderoso e amplamente utilizado por sua flexibilidade e recursos avançados.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Ubuntu, uma distribuição Linux popular, oferece várias ferramentas de linha de comando para pesquisar arquivos. Veremos como usar comandos tais como`find`, &amp;lt;code&amp;gt;locate&amp;lt;/code&amp;gt; et `grep`para localizar arquivos rapidamente e eficientemente no terminal.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_utilização_do_comando_find&amp;quot;&amp;gt;Utilização do comando `find&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O comando`find`permite pesquisar arquivos em um diretório específico e seus subdiretórios. Sua sintaxe básica é a seguinte:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;find [chemin] -name [nom_du_fichier]&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está um exemplo prático onde estamos pesquisando por um arquivo chamado`example.txt`no diretório`/home` :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;find /home -name &amp;quot;example.txt&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este comando vai percorrer todos os subdiretórios de`/home`e exibir o caminho completo do arquivo se for encontrado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Manual online do comando`find`:https://man.archlinux.org/man/find.1.fr/[manual encontrar,janelas=ler-mais-tarde]&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_opções_adicionais_para_find&amp;quot;&amp;gt;Opções adicionais para `find&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O comando`find`é muito poderosa e dispõe de muitas opções. Aqui estão alguns exemplos úteis:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Pesquisar por tipo de arquivo&amp;lt;/strong&amp;gt;: Para pesquisar apenas arquivos, use`-type f`, e para pesquisar diretórios, use`-type d`. - &amp;lt;strong&amp;gt;Pesquisar por tamanho&amp;lt;/strong&amp;gt;Você também pode filtrar os arquivos de acordo com o tamanho usando a opção`-size`. Par exemple, pour rechercher des fichiers de plus de 100 Mo, utilisez :&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;find /home -size +100M&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_utilização_do_comando_locate&amp;quot;&amp;gt;Utilização do comando `locate&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O comando`locate`é mais rápido que`find`Porque ela utiliza um banco de dados pré-indexado dos arquivos presentes no sistema. Você deve primeiro atualizar este banco de dados com o seguinte comando :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sudo updatedb&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em seguida, você pode procurar um arquivo assim:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;locate example.txt&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Eu usohttps://fr.wikipedia.org/wiki/Z_Shell[zsh]comhttps://github.com/ohmyzsh/ohmyzsh/wiki[oh-my-zsh], parece que o pedido`locate`não é acessível a partir de`zsh`mas possível desde`bash`, para lidar com isso, o argumento`-c`da encomenda`bash`permite executar um comando`bash`desde outro tipo de terminal.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui estou procurando os arquivos que terminam com o padrão`001.pdf`no diretório do usuário&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;bash -c &amp;quot;locate ~/*001.pdf&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Manual online do comando`locate` : &amp;lt;a href=&amp;quot;https://man.archlinux.org/man/locate.1.fr/&amp;quot;&amp;gt;man localizar&amp;lt;/a&amp;gt;*&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_uso_do_comando_grep_para_pesquisar_texto_em_arquivos&amp;quot;&amp;gt;Uso do comando &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; para pesquisar texto em arquivos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se você quiser pesquisar um texto específico dentro de um arquivo, você pode usar`grep`. Por exemplo, para pesquisar a ocorrência da palavra &amp;quot;Ubuntu&amp;quot; em todos os arquivos`.txt`do diretório atual, use :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;grep &amp;quot;Ubuntu&amp;quot; *.txt&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A pesquisa de arquivos no Ubuntu por meio de um terminal Zsh pode ser feita usando várias ferramentas poderosas.`find`é flexível e oferece uma multitude de opções, enquanto`locate`permite uma pesquisa rápida usando uma base de dados pré-construída. Finalmente,`grep`permite encontrar ocorrências de texto dentro dos arquivos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esses comandos oferecem uma gama completa de ferramentas para tornar suas pesquisas de arquivos mais eficientes e adaptadas às suas necessidades, e cada um desses comandos possui um manual visível com o comando`man`&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Memo linux</title>
            <link >https://cheroliv.com//blog/2021/0030_memo_linux_post.html</link>
            <pubDate>Mon, 23 Sep 2024 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2021/0030_memo_linux_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_o_teclado&amp;quot;&amp;gt;O teclado :&amp;lt;/a&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_a_tecla_ctrl&amp;quot;&amp;gt;&amp;lt;em&amp;gt;a tecla &amp;amp;lt;Ctrl&amp;amp;gt;&amp;lt;/em&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_permissãoautorização&amp;quot;&amp;gt;Permissão(autorização)&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_pesquisa_de_arquivos&amp;quot;&amp;gt;Pesquisa de arquivos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_o_teclado&amp;quot;&amp;gt;O teclado :&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_a_tecla_ctrl&amp;quot;&amp;gt;&amp;lt;em&amp;gt;a tecla &amp;amp;lt;Ctrl&amp;amp;gt;&amp;lt;/em&amp;gt;&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://korben.info/les-raccourcis-clavier-pour-bash-terminal-linux-et-macos.html&amp;quot;&amp;gt;link&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;a href=&amp;quot;https://blogmotion.fr/systeme/astuces-bash-linux-16175&amp;quot;&amp;gt;outro link&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_permissãoautorização&amp;quot;&amp;gt;Permissão(autorização)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;CHMOD é utilizado para alterar as permissões de um ficheiro.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;PERMISSÃO      COMANDO&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;U   G   W rwx rwx rwx     chmod 777 filename rwx rwx r-x     chmod 775 filename rwx r-x r-x     chmod 755 filename rw- rw- r--     chmod 664 filename rw- r-- r--     chmod 644 filename&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;U = utilizador G = Grupo W = Mundo&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;r = Readable w = gravável x = executável - = sem permissão&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Outra forma de ver as permissões:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;400     lido por proprietário 040     lido pelo grupo 004     lido por qualquer pessoa (outra) 200 escrito pelo proprietário 020     escrever por grupo 002     escrever por qualquer pessoa 100     executar pelo proprietário 010     executar por grupo 001     execute by anybody&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para obter uma combinação, basta somá‑los.&amp;lt;br&amp;gt;
Por exemplo, para ser lido, escrito, executado pelo proprietário,&amp;lt;br&amp;gt;
lido, executado, por grupo e executado por qualquer pessoa,&amp;lt;br&amp;gt;
você adicionaria 400+200+100+040+010+001 para dar 751.&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_pesquisa_de_arquivos&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;/blog/2024/0074_seach_file_linux_post.html&amp;quot;&amp;gt;Pesquisa de arquivos&amp;lt;/a&amp;gt;&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Expor uma porta do subsistema Linux para o host Windows</title>
            <link >https://cheroliv.com//blog/2024/0072_Exposer_un_port_linux_subsystem_post.adoc.html</link>
            <pubDate>Thu, 1 Feb 2024 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2024/0072_Exposer_un_port_linux_subsystem_post.adoc.html</guid>
            <description></description>
        </item><item>
            <title>Instalar o Pandoc</title>
            <link >https://cheroliv.com//blog/2024/0070_installer_pandoc_post.html</link>
            <pubDate>Tue, 30 Jan 2024 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2024/0070_installer_pandoc_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;&amp;lt;em&amp;gt;Tempo de leitura : 3 min&amp;lt;/em&amp;gt; Pandoc um conversor universal de documento&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Pandoc é um software de processamento de texto open source que pode ser usado para converter documentos entre diferentes formatos. Existem várias maneiras de instalar o Pandoc.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_instalação_a_partir_da_página_de_download&amp;quot;&amp;gt;Instalação a partir da página de download&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para instalar o Pandoc a partir da página de download, siga estas etapas:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Acesse a página de download do Pandoc :https://pandoc.org/installing.html/[&amp;lt;a href=&amp;quot;https://pandoc.org/installing.html&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://pandoc.org/installing.html&amp;lt;/a&amp;gt;]&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Faça o download do instalador do pacote para o seu sistema operacional.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Execute o instalador para instalar Pandoc.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Instalação a partir de um arquivo zip&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para instalar Pandoc a partir de um arquivo zip, siga os passos abaixo:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Faça o download do arquivo zip contendo os binários do Pandoc e a documentação.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Descompacte o arquivo zip.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Mova os binários para um diretório da sua escolha.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Instalação usando Chocolatey&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para instalar o Pandoc usando o Chocolatey, siga estes passos:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Abra um prompt de comando.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Execute o comando seguinte:&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-powershell&amp;quot; data-lang=&amp;quot;powershell&amp;quot;&amp;gt;choco install pandoc&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Instalação usando o winget&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para instalar o Pandoc usando o winget, siga estas etapas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Abra um prompt de comando.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Execute o seguinte comando :&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-powershell&amp;quot; data-lang=&amp;quot;powershell&amp;quot;&amp;gt;winget install --source winget --exact --id JohnMacFarlane.Pandoc&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Recomendações&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Por padrão, o Pandoc cria PDFs usando LaTeX. É recomendado instalar o LaTeX via MiKTeX. Com a opção`--pdf-engine`, você pode, porém, especificar outros programas para esta tarefa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Advertência&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O uso de vários métodos de instalação pode resultar em duas instalações distintas do Pandoc. É recomendado desinstalar corretamente o Pandoc antes de passar para um método de instalação alternativo.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Instalação no Linux via WSL&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Verifique se a versão do pandoc no seu gerenciador de pacotes não está obsoleta. Pandoc está nos repositórios do Debian, Ubuntu, Slackware, Arch, Fedora, NixOS, openSUSE, gentoo e Void.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para obter a última versão, oferecemos um pacote binário para a arquitetura amd64 na página de download.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O executável está vinculado estaticamente e não possui dependências dinâmicas nem dependências de arquivos de dados externos. Nota: devido ao link estático, o binário pandoc deste pacote não pode usar os filtros lua que exigem módulos lua externos escritos em C.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Um tarball e um instalador deb são fornecidos. Para instalar o deb :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sudo dpkg -i $DEB&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo no momento da escrita:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;wget https://github.com/jgm/pandoc/releases/download/3.1.11.1/pandoc-3.1.11.1-1-amd64.deb&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;sudo dpkg -i pandoc-3.1.11.1-1-amd64.deb&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;où `$DEB`é o caminho para o deb baixado Isso instalará o executável`pandoc`e a página do manual.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se você utiliza uma distribuição baseada em RPM, talvez consiga instalar o .deb da nossa página de download usando o alien.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em qualquer distribuição, você pode instalar a partir do tarball em`$DEST`(por exemplo,&amp;lt;code&amp;gt;/usr/local/&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;$HOME/.local&amp;lt;/code&amp;gt;) ao fazer :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;tar xvzf $TGZ --strip-components 1 -C $DEST&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;où `$TGZ`é o caminho para o tarball zip baixado. Para as versões do Pandoc anteriores a 2.0, que não fornecem um tarball, experimente em vez disso :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;ar p $DEB data.tar.gz | tar xvz --strip-components 2 -C $DEST&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você também pode instalar a partir do código-fonte, usando as instruções abaixo sob`Compilation à partir de la source`. Note que a maioria das distribuições tem o Haskell Platform nos seus repositórios de pacotes. Por exemplo, no Debian/Ubuntu, você pode instalá-lo com&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;apt-get install haskell-platform&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Por padrão, o Pandoc cria PDFs usando LaTeX. Recomendamos que instale o TeX Live via o seu gestor de pacotes. (No Debian/Ubuntu,&amp;lt;code&amp;gt;apt-get install texlive&amp;lt;/code&amp;gt;) Com a opção`--pdf-engine`, você pode, no entanto, especificar outros programas para esta tarefa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Implementação de kotlin.Triple em JavaScript, TypeScript e Python</title>
            <link >https://cheroliv.com//blog/2023/0069_triple_python_js_ts_post.html</link>
            <pubDate>Mon, 25 Dec 2023 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2023/0069_triple_python_js_ts_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;&amp;lt;em&amp;gt;Tempo de leitura : 2 min&amp;lt;/em&amp;gt; Criar uma implementação completa da classe kotlin.Triple em JavaScript, TypeScript e Python utilizando a programação funcional.&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Nota :&amp;lt;/strong&amp;gt;Os tipos`Maybe`, &amp;lt;code&amp;gt;Either&amp;lt;/code&amp;gt;, `Result`não fazem parte da linguagem de base em JavaScript ou TypeScript, portanto precisamos implementá-los nós mesmos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_javascript&amp;quot;&amp;gt;JavaScript&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;//javascript
// Maybe type
const Maybe = (value) =&amp;amp;gt; ({
  map: (fn) =&amp;amp;gt; (value !== null &amp;amp;amp;&amp;amp;amp; value !== undefined ? Maybe(fn(value)) : Maybe(null)),
  flatMap: (fn) =&amp;amp;gt; (value !== null &amp;amp;amp;&amp;amp;amp; value !== undefined ? fn(value) : Maybe(null)),
  getOrElse: (defaultValue) =&amp;amp;gt; (value !== null &amp;amp;amp;&amp;amp;amp; value !== undefined ? value : defaultValue),
});

// Either type
const Either = {
  Left: (value) =&amp;amp;gt; ({
    map: (fn) =&amp;amp;gt; Either.Left(value),
    flatMap: (fn) =&amp;amp;gt; Either.Left(value),
    getOrElse: (defaultValue) =&amp;amp;gt; defaultValue,
    isLeft: true,
    isRight: false,
  }),
  Right: (value) =&amp;amp;gt; ({
    map: (fn) =&amp;amp;gt; Either.Right(fn(value)),
    flatMap: (fn) =&amp;amp;gt; fn(value),
    getOrElse: (defaultValue) =&amp;amp;gt; value,
    isLeft: false,
    isRight: true,
  }),
};

// Triple class
class Triple {
  constructor(a, b, c) {
    this.a = a;
    this.b = b;
    this.c = c;
  }

  map(fn) {
    return new Triple(fn(this.a), fn(this.b), fn(this.c));
  }

  static of(a, b, c) {
    return new Triple(a, b, c);
  }

  // Example method using Maybe and Either
  divideBy(other) {
    return Maybe(this.b)
      .flatMap((numerator) =&amp;amp;gt;
        Maybe(other)
          .flatMap((denominator) =&amp;amp;gt;
            denominator !== 0
              ? Either.Right(numerator / denominator)
              : Either.Left(&amp;#39;Division by zero&amp;#39;)
          )
      )
      .getOrElse(Either.Left(&amp;#39;Invalid input&amp;#39;));
  }
}

// Example usage
const triple = Triple.of(1, 2, 3);
const result = triple.map((x) =&amp;amp;gt; x * 2);
console.log(result); // Triple { a: 2, b: 4, c: 6 }

const divisionResult = triple.divideBy(2);
console.log(divisionResult); // Either.Right { value: 1 }

const invalidDivisionResult = triple.divideBy(0);
console.log(invalidDivisionResult); // Either.Left { value: &amp;#39;Division by zero&amp;#39; }&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_typescript&amp;quot;&amp;gt;TypeScript&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-typescript&amp;quot; data-lang=&amp;quot;typescript&amp;quot;&amp;gt;//Typescript
// Maybe type
// noinspection JSAnnotator

type Maybe&amp;amp;lt;T&amp;amp;gt; = {
  map&amp;amp;lt;U&amp;amp;gt;(fn: (value: T) =&amp;amp;gt; U): Maybe&amp;amp;lt;U&amp;amp;gt;;
  flatMap&amp;amp;lt;U&amp;amp;gt;(fn: (value: T) =&amp;amp;gt; Maybe&amp;amp;lt;U&amp;amp;gt;): Maybe&amp;amp;lt;U&amp;amp;gt;;
  getOrElse(defaultValue: T): T;
};

const just = &amp;amp;lt;T&amp;amp;gt;(value: T): Maybe&amp;amp;lt;T&amp;amp;gt; =&amp;amp;gt; ({
  map: &amp;amp;lt;U&amp;amp;gt;(fn: (value: T) =&amp;amp;gt; U) =&amp;amp;gt; just(fn(value)),
  flatMap: &amp;amp;lt;U&amp;amp;gt;(fn: (value: T) =&amp;amp;gt; Maybe&amp;amp;lt;U&amp;amp;gt;) =&amp;amp;gt; fn(value),
  getOrElse: (_: T) =&amp;amp;gt; value,
});

const nothing = &amp;amp;lt;T&amp;amp;gt;(): Maybe&amp;amp;lt;T&amp;amp;gt; =&amp;amp;gt; ({
  map: &amp;amp;lt;U&amp;amp;gt;(_: (value: T) =&amp;amp;gt; U) =&amp;amp;gt; nothing&amp;amp;lt;U&amp;amp;gt;(),
  flatMap: &amp;amp;lt;U&amp;amp;gt;(_: (value: T) =&amp;amp;gt; Maybe&amp;amp;lt;U&amp;amp;gt;) =&amp;amp;gt; nothing&amp;amp;lt;U&amp;amp;gt;(),
  getOrElse: (defaultValue: T) =&amp;amp;gt; defaultValue,
});

// Either type
type Either&amp;amp;lt;L, R&amp;amp;gt; = {
  map&amp;amp;lt;U&amp;amp;gt;(fn: (value: R) =&amp;amp;gt; U): Either&amp;amp;lt;L, U&amp;amp;gt;;
  flatMap&amp;amp;lt;U&amp;amp;gt;(fn: (value: R) =&amp;amp;gt; Either&amp;amp;lt;L, U&amp;amp;gt;): Either&amp;amp;lt;L, U&amp;amp;gt;;
  getOrElse(defaultValue: R): R;
  isLeft: boolean;
  isRight: boolean;
};

const left = &amp;amp;lt;L, R&amp;amp;gt;(value: L): Either&amp;amp;lt;L, R&amp;amp;gt; =&amp;amp;gt; ({
  map: &amp;amp;lt;U&amp;amp;gt;(_: (value: R) =&amp;amp;gt; U) =&amp;amp;gt; left&amp;amp;lt;L, U&amp;amp;gt;(value),
  flatMap: &amp;amp;lt;U&amp;amp;gt;(_: (value: R) =&amp;amp;gt; Either&amp;amp;lt;L, U&amp;amp;gt;) =&amp;amp;gt; left&amp;amp;lt;L, U&amp;amp;gt;(value),
  getOrElse: (defaultValue: R) =&amp;amp;gt; defaultValue,
  isLeft: true,
  isRight: false,
});

const right = &amp;amp;lt;L, R&amp;amp;gt;(value: R): Either&amp;amp;lt;L, R&amp;amp;gt; =&amp;amp;gt; ({
  map: &amp;amp;lt;U&amp;amp;gt;(fn: (value: R) =&amp;amp;gt; U) =&amp;amp;gt; right&amp;amp;lt;L, U&amp;amp;gt;(fn(value)),
  flatMap: &amp;amp;lt;U&amp;amp;gt;(fn: (value: R) =&amp;amp;gt; Either&amp;amp;lt;L, U&amp;amp;gt;) =&amp;amp;gt; fn(value),
  getOrElse: (_: R) =&amp;amp;gt; value,
  isLeft: false,
  isRight: true,
});

// Triple class
class Triple&amp;amp;lt;T&amp;amp;gt; {
  constructor(public a: T, public b: T, public c: T) {}

  map&amp;amp;lt;U&amp;amp;gt;(fn: (value: T) =&amp;amp;gt; U): Triple&amp;amp;lt;U&amp;amp;gt; {
    return new Triple&amp;amp;lt;U&amp;amp;gt;(fn(this.a), fn(this.b), fn(this.c));
  }

  static of&amp;amp;lt;T&amp;amp;gt;(a: T, b: T, c: T): Triple&amp;amp;lt;T&amp;amp;gt; {
    return new Triple&amp;amp;lt;T&amp;amp;gt;(a, b, c);
  }

  // Example method using Maybe and Either
  divideBy(other: T): Either&amp;amp;lt;string, number&amp;amp;gt; {
    return just(this.b)
      .flatMap((numerator) =&amp;amp;gt;
        just(other)
          .flatMap((denominator) =&amp;amp;gt;
            denominator !== 0 ? right(numerator / denominator) : left(&amp;#39;Division by zero&amp;#39;)
          )
      )
      .getOrElse(left(&amp;#39;Invalid input&amp;#39;));
  }
}

// Example usage
const triple = Triple.of(1, 2, 3);
const result = triple.map((x) =&amp;amp;gt; x * 2);
console.log(result); // Triple { a: 2, b: 4, c: 6 }

const divisionResult = triple.divideBy(2);
console.log(divisionResult); // Either.Right { value: 1 }

const invalidDivisionResult = triple.divideBy(0);
console.log(invalidDivisionResult); // Either.Left { value: &amp;#39;Division by zero&amp;#39; }&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_python_pydantic_pymonad&amp;quot;&amp;gt;Python, pydantic, pymonad&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para criar uma implementação em Python usando`pydantic`para a validação dos tipos e`pymonad`para as mônadas :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Instalar as dependências :&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;pip install pydantic pymonad&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-python&amp;quot; data-lang=&amp;quot;python&amp;quot;&amp;gt;from pymonad.either import Either, Left, Right
from pydantic import BaseModel, ValidationError, validator

# Définition de la classe Triple avec Pydantic
class Triple(BaseModel):
    a: int
    b: int
    c: int

    @validator(&amp;#39;b&amp;#39;)
    def validate_b(cls, b, values):
        if b == 0:
            raise ValueError(&amp;#39;Division by zero is not allowed&amp;#39;)
        return b

    # Méthode utilisant Either pour la gestion des erreurs
    def divide_by(self, other):
        def division(numerator, denominator):
            return Right(numerator / denominator)

        def handle_error(error):
            return Left(str(error))

        return Either(lambda: division(self.b, other)).or_else(handle_error)

# Exemple d&amp;#39;utilisation
try:
    triple = Triple(a=1, b=2, c=3)
    result = triple.divide_by(2)

    if result.is_right:
        print(f&amp;#39;Division result: {result.value}&amp;#39;)
    else:
        print(f&amp;#39;Error: {result.value}&amp;#39;)

except ValidationError as e:
    print(f&amp;#39;Validation Error: {e}&amp;#39;)
except Exception as e:
    print(f&amp;#39;An unexpected error occurred: {e}&amp;#39;)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Neste exemplo:&amp;lt;/div&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;pydantic`para definir a classe`Triple`com campos tipados. O método`divide_by`utiliza a mônada`Either&amp;lt;/code&amp;gt; de `pymonad`para lidar com os erros potenciais resultantes da divisão por zero - Validação personalizada para garantir que o valor de`b`não é igual a zero. Se for igual a zero, uma exceção é lançada, e`pymonad`captura essa exceção para retornar um resultado`Left`indicando o erro.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Introdução ao Google Apps Script</title>
            <link >https://cheroliv.com//blog/2023/0068_introduction_google_apps_script_post.html</link>
            <pubDate>Fri, 22 Dec 2023 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2023/0068_introduction_google_apps_script_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_sumário&amp;quot;&amp;gt;Sumário&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;tempo de leitura: 30 min&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#Introduction&amp;quot;&amp;gt;[Introduction]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#Estrutura básica no Google Apps Script&amp;quot;&amp;gt;[Estrutura básica no Google Apps Script]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#_tipos_e_variáveis_no_google_apps_script&amp;quot;&amp;gt;Tipos e Variáveis no Google Apps Script&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#Constantes&amp;quot;&amp;gt;Variáveis e Letras no Google Apps Script&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#_operações_e_operadores_no_google_apps_script&amp;quot;&amp;gt;Operações e Operadores no Google Apps Script&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#Var&amp;quot;&amp;gt;Let e Const no Google Apps Script&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#Estruturas de Controle no Google Apps Script&amp;quot;&amp;gt;[Estruturas de Controle no Google Apps Script]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#Laços no Google Apps Script&amp;quot;&amp;gt;[Laços no Google Apps Script]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#Objetos no Google Apps Script&amp;quot;&amp;gt;[Objetos no Google Apps Script]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#_funções_no_google_apps_script&amp;quot;&amp;gt;Funções no Google Apps Script&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#_parâmetros_e_escopos_em_google_apps_script&amp;quot;&amp;gt;Parâmetros e Escopos em Google Apps Script&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#Operadores de Resto e de Espalhamento em Google Apps Script&amp;quot;&amp;gt;[Operadores de Resto e de Espalhamento em Google Apps Script]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Atribuição por Decomposição em Google Apps Script&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#_coleções_e_programação_declarativa_no_google_apps_script&amp;quot;&amp;gt;Coleções e Programação Declarativa no Google Apps Script&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_introdução&amp;quot;&amp;gt;Introdução&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Google Apps Script (GAS) é uma plataforma de desenvolvimento de scripts associada a diversos serviços do Google, como Google Sheets, Google Docs e Google Forms. Ela permite automatizar tarefas, adicionar funcionalidades personalizadas e integrar serviços do Google.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Neste memorando, vamos explorar os principais conceitos do Google Apps Script usando exemplos concretos. Abordaremos noções como variáveis, laços, funções e muito mais, adaptando-os ao contexto específico do GAS.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_estrutura_básica_em_google_apps_script&amp;quot;&amp;gt;Estrutura básica em Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para começar, vamos examinar a estrutura básica de um script Google Apps.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Structure de base en Google Apps Script
 */
function exempleStructureGAS() {
    // Votre code ici
    Logger.log(&amp;quot;Hello, Google Apps Script!&amp;quot;);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_tipos_e_variáveis_no_google_apps_script&amp;quot;&amp;gt;Tipos e Variáveis no Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Google Apps Script utiliza JavaScript como linguagem de programação. Os tipos de dados e as variáveis funcionam de forma semelhante ao JavaScript padrão.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Types et Variables en Google Apps Script
 */
function exempleTypesVariablesGAS() {
    // Votre code ici
    var nombre = 42;
    var texte = &amp;quot;Bonjour, Google Apps Script!&amp;quot;;
    Logger.log(nombre);
    Logger.log(texte);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_operações_e_operadores_no_google_apps_script&amp;quot;&amp;gt;Operações e Operadores no Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;As operações e operadores no Google Apps Script são os mesmos que em JavaScript.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Opérations et Opérateurs en Google Apps Script
 */
function exempleOperationsGAS() {
    // Votre code ici
    var x = 10;
    var y = 5;
    var resultat = x + y;
    Logger.log(resultat);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_var_let_e_const_no_google_apps_script&amp;quot;&amp;gt;Var, Let e Const no Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No GAS, os conceitos de`var`, &amp;lt;code&amp;gt;let&amp;lt;/code&amp;gt;, et `const`são usados para declarar variáveis. No entanto, é essencial compreender suas diferenças e suas implicações.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Var, Let et Const en Google Apps Script
 */
function exempleVarLetConstGAS() {
    // Votre code ici
    // Utilisation de var
    var x = 10;
    if (true) {
        var x = 20; // La variable x est modifiée globalement
    }
    Logger.log(x);

    // Utilisation de let
    let y = 30;
    if (true) {
        let y = 40; // La variable y est limitée au bloc if
    }
    Logger.log(y);

    // Utilisation de const
    const z = 50;
    // z = 60; // Impossible de réassigner une constante
    Logger.log(z);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_estruturas_de_controle_em_google_apps_script&amp;quot;&amp;gt;Estruturas de Controle em Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;As estruturas de controle tais como`if`, &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt; et `for`são utilizadas para gerenciar o fluxo de execução no Google Apps Script.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Structures de Contrôle en Google Apps Script
 */
function exempleStructuresControleGAS() {
    // Votre code ici
    var condition = true;

    if (condition) {
        Logger.log(&amp;quot;La condition est vraie.&amp;quot;);
    } else {
        Logger.log(&amp;quot;La condition est fausse.&amp;quot;);
    }

    var compteur = 0;
    while (compteur &amp;amp;lt; 5) {
        Logger.log(compteur);
        compteur++;
    }

    for (var i = 0; i &amp;amp;lt; 3; i++) {
        Logger.log(i);
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_parâmetros_e_escopos_em_google_apps_script&amp;quot;&amp;gt;Parâmetros e Escopos em Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os parâmetros e os escopos desempenham um papel crucial no desenvolvimento de scripts em GAS. Entenda como eles funcionam para evitar comportamentos inesperados.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Paramètres et Scopes en Google Apps Script
 */
function exempleParametresScopesGAS(parametre) {
    // Votre code ici
    var variableGlobale = &amp;quot;Je suis global&amp;quot;;

    function afficherParametre() {
        Logger.log(parametre);
    }

    afficherParametre();
    Logger.log(variableGlobale);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_constantes_variáveis_e_letras_em_google_apps_script&amp;quot;&amp;gt;Constantes, Variáveis e Letras em Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Entender a diferença entre`const`, &amp;lt;code&amp;gt;var&amp;lt;/code&amp;gt;, et `let`é essencial para uma utilização eficaz das variáveis no Google Apps Script.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Constantes, Variables, et Lettres en Google Apps Script
 */
function exempleConstantesVariablesLettresGAS() {
    // Votre code ici
    const constante = &amp;quot;Je ne change pas&amp;quot;;
    Logger.log(constante);

    var variable = &amp;quot;Je peux changer&amp;quot;;
    Logger.log(variable);

    let lettre = &amp;quot;Je peux aussi changer, mais seulement dans mon bloc&amp;quot;;
    Logger.log(lettre);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_laços_em_google_apps_script&amp;quot;&amp;gt;Laços em Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Laços, tais como`for` et &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;, são essenciais para iterar sobre elementos e executar operações repetitivas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Boucles en Google Apps Script
 */
function exempleBouclesGAS() {
    // Votre code ici
    for (var i = 0; i &amp;amp;lt; 3; i++) {
        Logger.log(i);
    }

    var condition = true;
    var compteur = 0;

    while (condition) {
        Logger.log(&amp;quot;Tour de boucle&amp;quot;);
        compteur++;
        if (compteur === 3) {
            condition = false;
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_objetos_em_google_apps_script&amp;quot;&amp;gt;Objetos em Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os objetos são usados para estruturar os dados. No GAS, muitos objetos integrados facilitam a interação com os serviços do Google.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Objets en Google Apps Script
 */
function exempleObjetsGAS() {
    // Votre code ici
    var feuille = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
    var cellule = feuille.getRange(&amp;quot;A1&amp;quot;);
    cellule.setValue(&amp;quot;Nouvelle valeur&amp;quot;);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_constantes_variáveis_e_letras_em_google_apps_script_2&amp;quot;&amp;gt;Constantes, Variáveis e Letras em Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Entender a diferença entre`const`, &amp;lt;code&amp;gt;var&amp;lt;/code&amp;gt;, et `let`é essencial para uma utilização eficaz das variáveis no Google Apps Script.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Constantes, Variables, et Lettres en Google Apps Script
 */
function exempleConstantesVariablesLettresGAS() {
    // Votre code ici
    const constante = &amp;quot;Je ne change pas&amp;quot;;
    Logger.log(constante);

    var variable = &amp;quot;Je peux changer&amp;quot;;
    Logger.log(variable);

    let lettre = &amp;quot;Je peux aussi changer, mais seulement dans mon bloc&amp;quot;;
    Logger.log(lettre);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_funções_no_google_apps_script&amp;quot;&amp;gt;Funções no Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;As funções são elementos fundamentais na programação. No GAS, elas podem ser declaradas e chamadas de diferentes maneiras.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Fonctions en Google Apps Script
 */
function exempleFonctionsGAS() {
    // Votre code ici
    function additionner(a, b) {
        return a + b;
    }

    var resultat = additionner(2, 3);
    Logger.log(resultat);

    // Fonction anonyme
    var multiplier = function (x, y) {
        return x * y;
    };

    Logger.log(multiplier(4, 5));
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_operadores_spread_e_rest_em_google_apps_script&amp;quot;&amp;gt;Operadores Spread e Rest em Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os operadores de descanso (&amp;lt;code&amp;gt;&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/code&amp;gt;) e de propagação (&amp;lt;code&amp;gt;&amp;amp;#8230;&amp;amp;#8203;&amp;lt;/code&amp;gt;) são úteis para manipular os arrays e os objetos de forma concisa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Opérateurs de Repos et de Propagation en Google Apps Script
 */
function exempleOperateursReposPropagationGAS() {
    // Votre code ici
    // Opérateur de repos (...) pour les tableaux
    var nombres = [1, 2, 3, 4, 5];
    var [...copieNombres] = nombres;
    Logger.log(copieNombres);

    // Opérateur de propagation (...) pour les objets
    var objOriginal = { x: 1, y: 2 };
    var objClone = { ...objOriginal, z: 3 };
    Logger.log(objClone);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_atribuição_por_decomposição_em_google_apps_script&amp;quot;&amp;gt;Atribuição por Decomposição em Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Atribuição por desestruturação é um recurso poderoso que permite extrair valores de objetos e arrays de forma concisa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Affectation par Décomposition en Google Apps Script
 */
function exempleAffectationDecompositionGAS() {
    // Votre code ici
    var coordonnees = [3, 4];
    var [x, y] = coordonnees;
    Logger.log(x);
    Logger.log(y);

    var utilisateur = { nom: &amp;quot;John&amp;quot;, age: 30 };
    var { nom, age } = utilisateur;
    Logger.log(nom);
    Logger.log(age);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_coleções_e_programação_declarativa_no_google_apps_script&amp;quot;&amp;gt;Coleções e Programação Declarativa no Google Apps Script&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Google Apps Script suporta estruturas de dados como arrays, e os conceitos de programação funcional e iteração declarativa (&amp;amp;gt;&amp;amp;lt;impérative), tais como`map`, &amp;lt;code&amp;gt;reduce&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;forEach&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Collections et Programmation Déclarative en Google Apps Script
 */
function exempleCollectionsFonctionnelleGAS() {
    // Votre code ici
    // Exemple de tableau (collection)
    var nombres = [1, 2, 3, 4, 5];

    // Utilisation de forEach
    nombres.forEach(function (nombre) {
        Logger.log(nombre);
    });

    // Utilisation de map
    var carresNombres = nombres.map(function (nombre) {
        return nombre * nombre;
    });
    Logger.log(carresNombres);

    // Utilisation de reduce
    var somme = nombres.reduce(function (acc, nombre) {
        return acc + nombre;
    }, 0);
    Logger.log(somme);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_conclusão&amp;quot;&amp;gt;Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este memo cobriu os conceitos fundamentais do Google Apps Script ilustrando-os com exemplos de código concretos. Use esses conhecimentos para automatizar suas tarefas diárias, personalizar seus documentos e aproveitar plenamente as funcionalidades do Google Apps Script.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Formação Express sobre o Script e a Automação do Google Form</title>
            <link >https://cheroliv.com//blog/2023/0067_formation_express_sur_google_forms_script_post.html</link>
            <pubDate>Wed, 20 Dec 2023 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2023/0067_formation_express_sur_google_forms_script_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Duração: 30 minutos&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_objetivos&amp;quot;&amp;gt;objetivos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Entender os fundamentos do Google Apps Script nos Formulários - Aprender a acessar o editor de script e executar uma instrução no gatilho&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_temporização&amp;quot;&amp;gt;temporização&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Introdução ao Google Form Script&amp;lt;/strong&amp;gt;(5 min)&amp;lt;/p&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Visão geral rápida do Script do Google Form - Apresentação das funcionalidades principais&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Criação de um Formulário Google e de um Script associado&amp;lt;/strong&amp;gt;(5 min)&amp;lt;/p&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Criação de um formulário simples com Google Forms Introdução ao editor de script associado&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Executar uma instrução&amp;lt;/strong&amp;gt;(10 min)&amp;lt;/p&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Configuração do gatilho &amp;quot;on start - Configuração do gatilho &amp;quot;on submit - Escrita de um script para Executar uma instrução - Testes rápidos para verificar o bom funcionamento no jornal&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Análise dos Resultados e Boas Práticas&amp;lt;/strong&amp;gt;(10 min)&amp;lt;/p&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Verificação dos resultados da execução de uma instrução - Discussão sobre um exemplo de script e como a automação pode economizar tempo&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_pré_requisito&amp;quot;&amp;gt;Pré-requisito&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Ter uma conta do Google e acesso ao Google Forms e ao Google Sheets&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_material&amp;quot;&amp;gt;Material&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Computador com um navegador web e uma conexão à internet&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_observação&amp;quot;&amp;gt;Observação&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Esta formação express oferece uma introdução rápida à automação com o Google Form Script. Para uma compreensão mais aprofundada, formações mais longas são recomendadas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_1_introdução_ao_script_do_google_forms&amp;quot;&amp;gt;1 - Introdução ao Script do Google Forms&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_slide_1_visão_geral_rápida_do_google_form_script&amp;quot;&amp;gt;Slide 1: Visão geral rápida do Google Form Script&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Google Form Script é uma plataforma de script integrada ao Google Forms. - Ela permite adicionar funcionalidades personalizadas e automatizar tarefas.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_slide_2_apresentação_das_funcionalidades_principales&amp;quot;&amp;gt;Slide 2: Apresentação das funcionalidades principales&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Script Editor : O editor de script integrado para escrever scripts do Google Apps Script. - Gatilhos: Eventos como &amp;lt;code&amp;gt;on submit&amp;lt;/code&amp;gt; que ativam a execução dos scripts.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_slide_3_vantagens_do_uso_do_google_form_script&amp;quot;&amp;gt;Slide 3: Vantagens do uso do Google Form Script&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Personalização: Crie formulários personalizados com funcionalidades únicas. - Automatização: Automatize ações em resposta às respostas dos formulários. - Integração: Integre scripts com outros serviços do Google para uma experiência mais rica.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_nota_para_o_apresentador&amp;quot;&amp;gt;Nota para o apresentador :&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Essas funcionalidades oferecem um potencial considerável para personalizar e automatizar os formulários. A continuação desta formação explorará em maior detalhe a criação de scripts e automações específicas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://developers.google.com/apps-script/add-ons/editors/forms?hl=pt&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://developers.google.com/apps-script/add-ons/editors/forms?hl=pt&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://developers.google.com/apps-script/reference/forms?hl=fr&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://developers.google.com/apps-script/reference/forms?hl=fr&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://developers.google.com/apps-script/reference/forms/form-app?hl=fr&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://developers.google.com/apps-script/reference/forms/form-app?hl=fr&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_criação_de_um_formulário_google_e_um_script_associado_5_min&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Criação de um Formulário Google e um Script associado&amp;lt;/strong&amp;gt; (5 min)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Nota Python</title>
            <link >https://cheroliv.com//blog/2023/0066_memo_python_post.html</link>
            <pubDate>Sun, 19 Nov 2023 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2023/0066_memo_python_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_instalar_python_no_windows&amp;quot;&amp;gt;Instalar python no windows&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://ninite.com/pythonx3/&amp;quot;&amp;gt;python no windows&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://python-docs.readthedocs.io/en/latest/starting/install3/win.html&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://python-docs.readthedocs.io/en/latest/starting/install3/win.html&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://python-docs.readthedocs.io/en/latest/dev/virtualenvs.html#virtualenvironments-ref&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://python-docs.readthedocs.io/en/latest/dev/virtualenvs.html#virtualenvironments-ref&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_executando_python&amp;quot;&amp;gt;Executando Python&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Referência :&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;cd mon_projet

python -m venv mon_venv

source mon_venv/bin/activate

# Sur windows
mon_venv\Scripts\activate

deactivate&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_requisito_de_instalação&amp;quot;&amp;gt;requisito de instalação&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;pip install -r requirements.txt&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_iniciação&amp;quot;&amp;gt;iniciação&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://python.doctor/page-apprendre-programmation-orientee-objet-poo-classes-python-cours-debutants&amp;quot;&amp;gt;Programação Orientada a Objetos em Python&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_padrões_de_projeto_do_gof_em_python&amp;quot;&amp;gt;Padrões de Projeto do GoF em Python&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/whikwon/python-patterns/&amp;quot;&amp;gt;implementação 1&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/Sean-Bradley/Design-Patterns-In-Python&amp;quot;&amp;gt;implementação 2&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://sbcode.net/python/&amp;quot;&amp;gt;livro completo&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os design patterns do GoF são um conjunto de 23 design patterns que foram descritos no livro &amp;quot;Design Patterns: Elements of Reusable Object-Oriented Software&amp;quot;.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;caption class=&amp;quot;title&amp;quot;&amp;gt;Table 1. Esta é uma lista dos padrões de projeto do GoF&amp;lt;/caption&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0833%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 2.0849%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nome&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Descrição&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Método Factory&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Crie objetos sem especificar a classe exata a ser instanciada.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Abstract Factory&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Crie famílias de objetos relacionados ou dependentes sem especificar sua classe concreta.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Construtor&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Separa a construção de um objeto complexo da sua representação de modo que o mesmo processo de construção possa criar diferentes representações.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Protótipo&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Cria novos objetos ao copiar um protótipo existente.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Singleton&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Restringe a instanciação de uma classe a um único objeto.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Adaptar&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Converte a interface de uma classe em outra interface que o cliente espera.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Ponte&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Desacople uma abstração de sua implementação para que os dois possam variar independentemente.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Composto&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Compõe objetos em estruturas de árvore para representar hierarquias de parte-todo.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Decorador&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Anexe dinamicamente responsabilidades adicionais a um objeto.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;fachada&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Fornece uma interface unificada a um conjunto de interfaces em um subsistema.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Peso leve&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Reduz o custo de criação e uso de objetos leves.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Proxy&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Fornece um substituto ou um espaço reservado para outro objeto para controlar o acesso a ele.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Cadeia de Responsabilidade&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Permite que mais de um objeto trate uma solicitação.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;comando&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Encapsula uma solicitação em um objeto, permitindo assim parametrizar clientes com diferentes solicitações, filas ou logs, e apoiar operações anuláveis.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Intérprete&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Dá uma representação da gramática de uma língua e usa essa representação para interpretar frases nessa língua.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Iterator&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Fornece um meio de percorrer sequencialmente os elementos de um objeto agregado sem expor sua representação subjacente.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Mediador&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Permite reduzir as dependências complexas entre os objetos fazendo-os comunicar apenas por meio de um objeto mediador.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Memento&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Fornece a capacidade de restaurar um objeto ao seu estado anterior (sem violar o encapsulamento).&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Observe&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Define uma dependência um-para-muitos entre objetos de forma que quando um objeto muda de estado, todos os seus dependentes são notificados e atualizados automaticamente.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Estado&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Permite que um objeto altere seu comportamento quando seu estado interno muda.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Estratégia&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Define uma família de algoritmos, encapsula cada um deles e os torna intercambiáveis.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Método Modelo&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Define o esqueleto de um algoritmo em um método, delegando algumas etapas às subclasses.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Visitante&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Permite definir uma nova operação a ser realizada numa estrutura de objetos, sem alterar as classes dos objetos nos quais ela opera.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_programação_funcional&amp;quot;&amp;gt;Programação funcional&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 16.6666%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 27.7777%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 55.5557%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Biblioteca&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;URL&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Descrição&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;PyMonad&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/jasondelaat/pymonad&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://github.com/jasondelaat/pymonad&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;PyMonad é uma implementação de estruturas de dados monádicas em Python baseada em linguagens de programação como Haskell e F# com implementações para os tipos de monads mais comumente usados em programação funcional.https://www.miguelfarrajota.com/2021/06/monads-in-python-with-pymonad/[artigo]&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;PyFunctional&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/EntilZha/PyFunctional/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://github.com/EntilZha/PyFunctional/&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;PyFunctional é uma biblioteca Python para programação funcional que fornece ferramentas para trabalhar com funções puras e iteráveis.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Toolz&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://toolz.readthedocs.io/en/latest/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://toolz.readthedocs.io/en/latest/&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Toolz é uma biblioteca Python para programação funcional que fornece ferramentas para trabalhar com funções puras e iteráveis.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Fn.py&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/kachayev/fn.py&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://github.com/kachayev/fn.py&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Fn.py é uma biblioteca Python para programação funcional que fornece ferramentas para trabalhar com funções puras e iteráveis.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;coco&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://coconut-lang.org/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://coconut-lang.org/&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Coconut adiciona várias funcionalidades ao Python para programação funcional, incluindo o pattern matching.https://stackoverflow.com/questions/11909681/are-there-pattern-matching-functions-in-python-like-this[conversa sof]&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_efeitos_colaterais&amp;quot;&amp;gt;efeitos colaterais&amp;lt;/h3&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_laços&amp;quot;&amp;gt;laços&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-python&amp;quot; data-lang=&amp;quot;python&amp;quot;&amp;gt;# impérative way
result = []
for i in inputs:
    x = f(g(i))
    result.append(x)

# idiomatic way
result = map(comp(f,g), inputs)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Desenvolvedor web</title>
            <link >https://cheroliv.com//blog/2023/0065_training_web_dev_post.html</link>
            <pubDate>Thu, 24 Aug 2023 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2023/0065_training_web_dev_post.html</guid>
            <description></description>
        </item><item>
            <title>Jogos de cartas colecionáveis (TCGs)</title>
            <link >https://cheroliv.com//blog/2023/0064_training_trading_card_games_post.html</link>
            <pubDate>Mon, 21 Aug 2023 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2023/0064_training_trading_card_games_post.html</guid>
            <description></description>
        </item><item>
            <title>Memo Typescript</title>
            <link >https://cheroliv.com//blog/2023/0063_memo_ts_post.html</link>
            <pubDate>Sat, 19 Aug 2023 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2023/0063_memo_ts_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;repositório do GitHub:https://github.com/cheroliv/ts-codebase[ts-codebase, window=&amp;quot;_blank]&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;toc_ts&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução TypeScript&amp;lt;/span&amp;gt;&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#ts_types&amp;quot;&amp;gt;Os tipos em typescript&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#funções&amp;quot;&amp;gt;Funções&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#data_struct&amp;quot;&amp;gt;Estrutura de dados&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#enums_tuples&amp;quot;&amp;gt;Enums e tuplas&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#void_never&amp;quot;&amp;gt;Void e Never&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#class_interface&amp;quot;&amp;gt;Classes e interfaces&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#tsconfig&amp;quot;&amp;gt;O arquivo tsconfig.json&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#módulos&amp;quot;&amp;gt;[módulos]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#promessas&amp;quot;&amp;gt;Promessas&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#async-await&amp;quot;&amp;gt;async/await&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#genéricos&amp;quot;&amp;gt;[genéricos]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;index.html&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html lang=&amp;quot;fr&amp;quot;&amp;amp;gt;

&amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;amp;gt;
    &amp;amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;ie=edge&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;title&amp;amp;gt;Document&amp;amp;lt;/title&amp;amp;gt;
&amp;amp;lt;/head&amp;amp;gt;

&amp;amp;lt;body&amp;amp;gt;
    Intro typescript
    &amp;amp;lt;script src=&amp;quot;01_types.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
    &amp;amp;lt;script src=&amp;quot;02_data_structure.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
    &amp;amp;lt;script src=&amp;quot;03_enums_tuples.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
&amp;amp;lt;/body&amp;amp;gt;

&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;ts_types&amp;quot;&amp;gt;Os tipos em typescript&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;01_types.ts&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-typescript&amp;quot; data-lang=&amp;quot;typescript&amp;quot;&amp;gt;/**
 * Les types en typescript
 */

//quand le type n&amp;#39;est pas defini des le debut,
// alors il est réassignable
// c&amp;#39;est le type Any qui est attribué par défaut
let my_var;
my_var = &amp;quot;username&amp;quot;;
console.assert(my_var === &amp;quot;username&amp;quot;);
my_var = 11;
console.assert(my_var === 11);
my_var = true;
console.assert(my_var === true);

let age = 1;
console.assert(age === 1);
age = 2;
// age=&amp;quot;trois&amp;quot; c&amp;#39;est impossible
// car l&amp;#39;inference de type a attribué le type number
console.assert(age === 2);

// Déclaration explicite de type
let pi: Number = 3.14;
console.assert(typeof pi === &amp;quot;number&amp;quot;);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_ts&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução TypeScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;funções&amp;quot;&amp;gt;Funções&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;As funções são blocos de código reutilizáveis.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo de cota:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-typescript&amp;quot; data-lang=&amp;quot;typescript&amp;quot;&amp;gt;const add = (a: number, b: number): number =&amp;amp;gt; a + b;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_ts&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução TypeScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;data_struct&amp;quot;&amp;gt;Estrutura de dados&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;02_data_structure.ts&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-typescript&amp;quot; data-lang=&amp;quot;typescript&amp;quot;&amp;gt;/***
 * Structure de données:
 * Avec le type any on peut changer
 * le type de la variable à tout instruction
 * Lorsque l&amp;#39;on fixe le type
 * alors il n&amp;#39;est plus possible de le changer
 * lors d&amp;#39;une assignation
 */

// Arrays
let buddies: any[] = [
    &amp;quot;cheroliv&amp;quot;,
    &amp;quot;imrandeh&amp;quot;,
    &amp;quot;issoudeh&amp;quot;,
    &amp;quot;soumi92&amp;quot;
]

console.assert(buddies[0] === &amp;quot;cheroliv&amp;quot;);
console.assert(buddies[1] === &amp;quot;imrandeh&amp;quot;);
console.assert(buddies[2] === &amp;quot;issoudeh&amp;quot;);
console.assert(buddies[3] === &amp;quot;soumi92&amp;quot;);
// Déclaré en type any, l&amp;#39;inference de type
// a bien réaligné en type string
console.assert(typeof buddies[0] === &amp;quot;string&amp;quot;);
console.assert(typeof buddies[1] === &amp;quot;string&amp;quot;);
console.assert(typeof buddies[2] === &amp;quot;string&amp;quot;);
console.assert(typeof buddies[3] === &amp;quot;string&amp;quot;);



let writers: (String | String | String | Number)[][] = [
    [&amp;quot;Chrétien&amp;quot;, &amp;quot;de Troyes&amp;quot;, &amp;quot;fr&amp;quot;, 12],
    [&amp;quot;François&amp;quot;, &amp;quot;Rabelais&amp;quot;, &amp;quot;fr&amp;quot;, 16],
    [&amp;quot;René&amp;quot;, &amp;quot;Descartes&amp;quot;, &amp;quot;fr&amp;quot;, 17],
    [&amp;quot;Jean-Jacques&amp;quot;, &amp;quot;Rousseau&amp;quot;, &amp;quot;fr&amp;quot;, 18],
    [&amp;quot;Georg&amp;quot;, &amp;quot;Hegel&amp;quot;, &amp;quot;de&amp;quot;, 19],
    [&amp;quot;Karl&amp;quot;, &amp;quot;Marx&amp;quot;, &amp;quot;de&amp;quot;, 19],
    [&amp;quot;Friedrich&amp;quot;, &amp;quot;Engels&amp;quot;, &amp;quot;de&amp;quot;, 19],
    [&amp;quot;Victor&amp;quot;, &amp;quot;Hugo&amp;quot;, &amp;quot;fr&amp;quot;, 19],
    [&amp;quot;Paul&amp;quot;, &amp;quot;Verlaine&amp;quot;, &amp;quot;fr&amp;quot;, 19],
    [&amp;quot;Arthur&amp;quot;, &amp;quot;Rimbaud&amp;quot;, &amp;quot;fr&amp;quot;, 19],
    [&amp;quot;Gérard&amp;quot;, &amp;quot;de Nerval&amp;quot;, &amp;quot;fr&amp;quot;, 19],
    [&amp;quot;Georg&amp;quot;, &amp;quot;Lukacs&amp;quot;, &amp;quot;hu&amp;quot;, 20],
    [&amp;quot;Franz&amp;quot;, &amp;quot;Kafka&amp;quot;, &amp;quot;hu&amp;quot;, 20],
    [&amp;quot;Antonio&amp;quot;, &amp;quot;Gramsci&amp;quot;, &amp;quot;it&amp;quot;, 20],
    [&amp;quot;Domenico&amp;quot;,&amp;quot;Losurdo&amp;quot;,&amp;quot;it&amp;quot;,20],
];

// console.table(writers);


// Objects
let author: {
    first_name: String,
    last_name: String,
    lang: String,
    century: Number
} = {
    // slice(-1) renvoie le dernier élèment
    first_name: writers.slice(-1)[0][0] as String,
    last_name: writers.slice(-1)[0][1] as String,
    lang: writers.slice(-1)[0][2] as String,
    century: writers.slice(-1)[0][3] as Number,
}


// assertion sur la valeur en accés par encapsulation(dot)
console.assert(author.first_name === &amp;quot;Antonio&amp;quot;)
console.assert(author.last_name === &amp;quot;Gramsci&amp;quot;)
console.assert(author.lang === &amp;quot;it&amp;quot;)
console.assert(author.century === 20);

// assertion sur le type en accès par index
console.assert(typeof author[&amp;quot;first_name&amp;quot;] === &amp;quot;string&amp;quot;)
console.assert(typeof author[&amp;quot;last_name&amp;quot;] === &amp;quot;string&amp;quot;)
console.assert(typeof author[&amp;quot;lang&amp;quot;] === &amp;quot;string&amp;quot;)
console.assert(typeof author[&amp;quot;century&amp;quot;] === &amp;quot;number&amp;quot;);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_ts&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução TypeScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;enums_tuples&amp;quot;&amp;gt;Enums e tuplas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;03_enums_tuples.ts&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-typescript&amp;quot; data-lang=&amp;quot;typescript&amp;quot;&amp;gt;/**
 * Enums et Tuples
 *
 * Enum: il existe les enums numérique
 * et les enums chaine de caracteres.
 *
 * Tuple: similaire aux arrays mais ne peut
 * contenir qu&amp;#39;une valeur de type spécifié.
 *
 */

//Enum numérique
// l&amp;#39;index par de debut défaut est 0
// ici on le place a 1
enum Week {
    Monday = 1,
    Tuesday = 2,
    Wednesday = 3,
    Thursday = 4,
    Friday = 5,
    Saturday = 6,
    Sunday = 7,
};

console.assert(Week.Monday == 1);
console.assert(Week.Tuesday == 2);
console.assert(Week.Wednesday == 3);
console.assert(Week.Thursday == 4);
console.assert(Week.Friday == 5);
console.assert(Week.Saturday == 6);
console.assert(Week.Sunday == 7);

console.assert(Week[1] === &amp;quot;Monday&amp;quot;);
console.assert(Week[2] === &amp;quot;Tuesday&amp;quot;);
console.assert(Week[3] === &amp;quot;Wednesday&amp;quot;);
console.assert(Week[4] === &amp;quot;Thursday&amp;quot;);
console.assert(Week[5] === &amp;quot;Friday&amp;quot;);
console.assert(Week[6] === &amp;quot;Saturday&amp;quot;);
console.assert(Week[7] === &amp;quot;Sunday&amp;quot;);

let motd_arr_fr: String[] = [
    &amp;quot;Associé à la Lune&amp;quot;,
    &amp;quot;Du dieu Tiw, associé à Mars&amp;quot;,
    &amp;quot;Du dieu germanique Odin&amp;quot;,
    &amp;quot;Du dieu germanique du tonnerre Thor&amp;quot;,
    &amp;quot;De la déesse germanique Frigga associée à Vénus&amp;quot;,
    &amp;quot;Associé à Saturne&amp;quot;,
    &amp;quot;Associé au Soleil&amp;quot;,
];

let motd_arr_en: String[] = [
    &amp;quot;associated with the Moon&amp;quot;,
    &amp;quot;from the god Tiw, associated with Mars&amp;quot;,
    &amp;quot;from Germanic god Odin&amp;quot;,
    &amp;quot;from Germanic god of thunder Thor&amp;quot;,
    &amp;quot;from Germanic goddess Frigga associated with Venus&amp;quot;,
    &amp;quot;associated with Saturn&amp;quot;,
    &amp;quot;associated with the Sun&amp;quot;,
];

// Un tuple (triple)
let monday_triple_fr: [
    Number,
    String,
    String
] = [
        Week.Monday,
        Week[Week.Monday],
        motd_arr_fr[0],
    ];


console.assert(monday_triple_fr[0] === 1);
console.assert(monday_triple_fr[1] === &amp;quot;Monday&amp;quot;);
console.assert(monday_triple_fr[2] === &amp;quot;Associé à la Lune&amp;quot;);


let monday_triple_en: [
    Number,
    String,
    String
] = [
        Week.Monday,
        Week[Week.Monday],
        motd_arr_en[0],
    ];

console.assert(monday_triple_en[0] === 1);
console.assert(monday_triple_en[1] === &amp;quot;Monday&amp;quot;);
console.assert(monday_triple_en[2] === &amp;quot;associated with the Moon&amp;quot;);


// On se fait un type pour ajouter le nom de la langue
type Meaning_of_the_day = {
    lang: String,
    meaning: (Week | String)[][],
};

//fonction d&amp;#39;affichage du type Meaning_of_the_day
const display_motd = (motd: Meaning_of_the_day) =&amp;amp;gt; {
    motd.meaning.forEach(day =&amp;amp;gt;
        console.table(`${Week[day[0] as Week]}: ${day[1]}.(${motd.lang})`)
    )
};

//fonction d&amp;#39;assertion sur le type Meaning_of_the_day
// afin de verifier la concordance du contenu avec
// motd_arr_#lang#
const test_motd = (
    motd: Meaning_of_the_day,
    motd_arr: String[]
) =&amp;amp;gt; {
    console.assert(motd.lang.length === 2)
    for (const [i, value] of motd.meaning.entries()) {
        console.assert(value[1] === motd_arr[i]);
    }
};


let motd_fr: Meaning_of_the_day = {
    lang: &amp;quot;fr&amp;quot;,
    meaning: [
        [Week.Monday, motd_arr_fr[0]],
        [Week.Tuesday, motd_arr_fr[1]],
        [Week.Wednesday, motd_arr_fr[2]],
        [Week.Thursday, motd_arr_fr[3]],
        [Week.Friday, motd_arr_fr[4]],
        [Week.Saturday, motd_arr_fr[5]],
        [Week.Sunday, motd_arr_fr[6]],
    ],
}

console.log(&amp;quot;---------&amp;quot;);
console.log(&amp;quot;display_motd(motd_fr):&amp;quot;);
display_motd(motd_fr);
test_motd(motd_fr, motd_arr_fr)


let motd_en: Meaning_of_the_day = {
    lang: &amp;quot;en&amp;quot;,
    meaning: [
        [Week.Monday, motd_arr_en[0]],
        [Week.Tuesday, motd_arr_en[1]],
        [Week.Wednesday, motd_arr_en[2]],
        [Week.Thursday, motd_arr_en[3]],
        [Week.Friday, motd_arr_en[4]],
        [Week.Saturday, motd_arr_en[5]],
        [Week.Sunday, motd_arr_en[6]],
    ],
}

console.log(&amp;quot;---------&amp;quot;);
console.log(&amp;quot;display_motd(motd_en):&amp;quot;);
display_motd(motd_en);
console.log(&amp;quot;---------&amp;quot;);
test_motd(motd_en, motd_arr_en);

// Un tuple (triple) utilisant le type
// Meaning_of_the_day pour peupler le meaning
let motd_triple: [
    Number,
    String,
    String
] = [
        Week.Monday,
        Week[Week.Monday],
        motd_fr.meaning[0][1] as String
    ];

console.assert(motd_triple[0] === 1);
console.assert(motd_triple[1] === &amp;quot;Monday&amp;quot;);
console.assert(motd_triple[2] === &amp;quot;Associé à la Lune&amp;quot;);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_ts&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução TypeScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;void_never&amp;quot;&amp;gt;Void e Never&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;04_void_never_types.ts&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-typescript&amp;quot; data-lang=&amp;quot;typescript&amp;quot;&amp;gt;// Le type void est l&amp;#39;opposé du type any
// c&amp;#39;est l&amp;#39;absence de type.
function verify_writers_length(): void {
    console.assert(writers.length &amp;amp;gt; 0);
}

verify_writers_length();

const verify_writers_length_arrow = (): void =&amp;amp;gt;
    console.assert(writers.length &amp;amp;gt; 0);

verify_writers_length_arrow();

//Type of et never
let value = 30;
console.assert(typeof value === &amp;quot;number&amp;quot;);

//never: qqchs qui n&amp;#39;arrive jamais
function foo(x: String | Number): Boolean {
    if (typeof x === &amp;quot;string&amp;quot;) {
        return true;
    } else if (typeof x === &amp;quot;number&amp;quot;) {
        return false;
    }
    return fail(&amp;quot;Error!&amp;quot;)
}

function fail(message: String): never {
    throw new Error(message as string);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_ts&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução TypeScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;class_interface&amp;quot;&amp;gt;Classes e interfaces&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;04_void_never_types.ts&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-typescript&amp;quot; data-lang=&amp;quot;typescript&amp;quot;&amp;gt;/**
 * Les classes:
 * members,
 * visibility,
 * nommage,
 * accesseurs
 * scope identifier: static,private,protected
 *
 * convention de nommage :
 * Les membres private sont préfixés avec un _ *
 */

class Member {
    username: String = &amp;quot;&amp;quot;;
    email: String = &amp;quot;&amp;quot;;
    private _password: String = &amp;quot;&amp;quot;;
    readonly signup_date: Date = new Date(Date.now());

    constructor(username: String, email: String) {
        this.username = username;
        this.email = email;
    }

    get password(): String {
        return this._password;
    }

    set password(new_password: String) {
        this._password = new_password;
    }

    /**
     * Une fonction static qui initilise un member,
     * à partir d&amp;#39;un author
     */
    static fromAuthor(author: {
        first_name: String,
        last_name: String,
        lang: String,
        century: Number
    }): Member {
        return new Member(
            `${author.first_name}.${author.last_name}`,
            `${author.first_name}.${author.last_name}@acme.com`,
        );
    }
};


let domenico = Member.fromAuthor(author);
domenico.password = &amp;quot;test&amp;quot;;

console.assert(domenico.email === &amp;quot;Domenico.Losurdo@acme.com&amp;quot;);
console.assert(domenico.password === &amp;quot;test&amp;quot;);


// Héritage
enum Forms {
    Undefined = 0,
    Polygones = 1,
    Circle = 2,
    Straight = 3,
    Segment = 4,
};

class Form {
    type: Forms = Forms.Undefined;
};

class Rectangle extends Form {
    h: Number = 0;
    w: Number = 0;
};

class Square extends Rectangle {
    side: Number = 0;
};


/**
 * Interface donne un contrat à un type.
 * Les interfaces permettent de définir la structure d&amp;#39;un objet.
 */

interface IPerson {
    username: String;
    email: String;
    readonly signup_date: Date;
}

const user1: IPerson = {
    username: Member.fromAuthor(author)[&amp;quot;username&amp;quot;],
    email: Member.fromAuthor(author)[&amp;quot;email&amp;quot;],
    signup_date: Member.fromAuthor(author)[&amp;quot;signup_date&amp;quot;],
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_ts&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução TypeScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;tsconfig&amp;quot;&amp;gt;O arquivo tsconfig.json&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;tsconfig.json&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-json&amp;quot; data-lang=&amp;quot;json&amp;quot;&amp;gt;{
  /* Visit https://aka.ms/tsconfig to read more about this file */
  &amp;quot;compileOnSave&amp;quot;: true,

  &amp;quot;compilerOptions&amp;quot;: {
    &amp;quot;target&amp;quot;: &amp;quot;es2016&amp;quot;,
    &amp;quot;module&amp;quot;: &amp;quot;CommonJS&amp;quot;,
    &amp;quot;noEmitOnError&amp;quot;: true,
    &amp;quot;strict&amp;quot;: true,
    &amp;quot;noImplicitAny&amp;quot;: true,
    &amp;quot;esModuleInterop&amp;quot;: true
  }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_ts&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução TypeScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;modules&amp;quot;&amp;gt;Módulos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os módulos permitem organizar o código em vários arquivos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo de classificação :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-typescript&amp;quot; data-lang=&amp;quot;typescript&amp;quot;&amp;gt;// Dans un fichier math.ts
export const add = (a: number, b: number): number =&amp;amp;gt; a + b;

// Dans un autre fichier
import { add } from &amp;#39;./math&amp;#39;;
let result = add(2, 3);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_ts&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução TypeScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;promessas&amp;quot;&amp;gt;Promessas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;As promessas são usadas para realizar operações assíncronas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo de cota :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-typescript&amp;quot; data-lang=&amp;quot;typescript&amp;quot;&amp;gt;const promise = new Promise&amp;amp;lt;string&amp;amp;gt;((resolve) =&amp;amp;gt; {
    // Code asynchrone ici
    resolve(&amp;quot;Succès&amp;quot;);
});

promise.then((result) =&amp;amp;gt; {
    console.log(result);
});&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_ts&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução TypeScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;async-await&amp;quot;&amp;gt;async/await&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O async/await simplifica o gerenciamento das promessas.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo de cota :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-typescript&amp;quot; data-lang=&amp;quot;typescript&amp;quot;&amp;gt;const fetchData = async (): Promise&amp;amp;lt;void&amp;amp;gt; =&amp;amp;gt; {
    const result = await someAsyncFunction();
    console.log(result);
};&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_ts&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução TypeScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;generiques&amp;quot;&amp;gt;Genéricos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os genéricos permitem criar componentes reutilizáveis com tipos dinâmicos.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo de classificação :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-typescript&amp;quot; data-lang=&amp;quot;typescript&amp;quot;&amp;gt;const identity = &amp;amp;lt;T&amp;amp;gt;(arg: T): T =&amp;amp;gt; arg;

const output = identity&amp;amp;lt;string&amp;amp;gt;(&amp;quot;hello&amp;quot;);
console.log(output);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Memorando CSS</title>
            <link >https://cheroliv.com//blog/2023/0062_memo_css_post.html</link>
            <pubDate>Mon, 14 Aug 2023 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2023/0062_memo_css_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;toc&amp;quot;&amp;gt;Sumário&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#good_links&amp;quot;&amp;gt;Links bons&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#repo&amp;quot;&amp;gt;Repositório e exemplo de código&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_início&amp;quot;&amp;gt;[toc_início]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;good_links&amp;quot;&amp;gt;Links bons&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc&amp;quot;&amp;gt;Sumário&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;repo&amp;quot;&amp;gt;Repositório e exemplo de código&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;repositório github:https://github.com/cheroliv/css-codebase[css-codebase, window = &amp;quot;_blank]&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;toc_début&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Início CSS&amp;lt;/span&amp;gt;&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#início_intro&amp;quot;&amp;gt;[início_intro]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#início_texts&amp;quot;&amp;gt;[início_texts]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#início_background&amp;quot;&amp;gt;[início_background]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#início_boxes&amp;quot;&amp;gt;[início_boxes]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#início_flexbox&amp;quot;&amp;gt;Flexbox&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#início_grid&amp;quot;&amp;gt;[início_grid]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#início_abs_pos&amp;quot;&amp;gt;[início_abs_pos]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#início_responsivo&amp;quot;&amp;gt;[início_responsivo]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#início_conclusão&amp;quot;&amp;gt;Depuração &amp;amp;amp; Conclusão&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc&amp;quot;&amp;gt;Sumário&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;début_intro&amp;quot;&amp;gt;Introdução &amp;amp;amp; Configuração&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Intro baseado neste vídeo:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;videoblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;iframe src=&amp;quot;https://www.youtube.com/embed/iSWjmVcfQGg?rel=0&amp;quot; frameborder=&amp;quot;0&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;repositório GitHub:https://github.com/cheroliv/css-codebase/début_css/[début css, window = &amp;quot;_blank]&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;fonte do memo:&amp;lt;br&amp;gt;
index.html&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html lang=&amp;quot;fr&amp;quot;&amp;amp;gt;
  &amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;IE=edge&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;title&amp;amp;gt;Cours CSS&amp;amp;lt;/title&amp;amp;gt;

    &amp;amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;style.css&amp;quot; /&amp;amp;gt;
  &amp;amp;lt;/head&amp;amp;gt;
  &amp;amp;lt;body&amp;amp;gt;
    &amp;amp;lt;header&amp;amp;gt;
      &amp;amp;lt;h1&amp;amp;gt;Les bases de CSS&amp;amp;lt;/h1&amp;amp;gt;
    &amp;amp;lt;/header&amp;amp;gt;

    &amp;amp;lt;main&amp;amp;gt;
      &amp;amp;lt;div class=&amp;quot;flexbox&amp;quot;&amp;amp;gt;
        &amp;amp;lt;h2&amp;amp;gt;Flexbox&amp;amp;lt;/h2&amp;amp;gt;
        &amp;amp;lt;ul&amp;amp;gt;
          &amp;amp;lt;li&amp;amp;gt;boite 1&amp;amp;lt;/li&amp;amp;gt;
          &amp;amp;lt;li&amp;amp;gt;boite 2&amp;amp;lt;/li&amp;amp;gt;
          &amp;amp;lt;li&amp;amp;gt;boite 3&amp;amp;lt;/li&amp;amp;gt;
        &amp;amp;lt;/ul&amp;amp;gt;
      &amp;amp;lt;/div&amp;amp;gt;

      &amp;amp;lt;div class=&amp;quot;grid&amp;quot;&amp;amp;gt;
        &amp;amp;lt;h2&amp;amp;gt;Grid&amp;amp;lt;/h2&amp;amp;gt;
        &amp;amp;lt;div class=&amp;quot;grid-container&amp;quot;&amp;amp;gt;
          &amp;amp;lt;img src=&amp;quot;./assets/img/css-logo.png&amp;quot; alt=&amp;quot;logo css&amp;quot; /&amp;amp;gt;

          &amp;amp;lt;form action=&amp;quot;&amp;quot;&amp;amp;gt;
            &amp;amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;firstname&amp;quot; placeholder=&amp;quot;Prénom&amp;quot; /&amp;amp;gt;
            &amp;amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;surname&amp;quot; placeholder=&amp;quot;Nom&amp;quot; /&amp;amp;gt;
            &amp;amp;lt;textarea
              cols=&amp;quot;30&amp;quot;
              rows=&amp;quot;10&amp;quot;
              placeholder=&amp;quot;Ici votre message&amp;quot;
            &amp;amp;gt;&amp;amp;lt;/textarea&amp;amp;gt;
            &amp;amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Valider&amp;quot; id=&amp;quot;btn-submit&amp;quot; /&amp;amp;gt;
          &amp;amp;lt;/form&amp;amp;gt;
        &amp;amp;lt;/div&amp;amp;gt;
      &amp;amp;lt;/div&amp;amp;gt;

      &amp;amp;lt;div class=&amp;quot;absolute&amp;quot;&amp;amp;gt;
        &amp;amp;lt;h2&amp;amp;gt;Absolute&amp;amp;lt;/h2&amp;amp;gt;
        &amp;amp;lt;span id=&amp;quot;circle1&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;
        &amp;amp;lt;span id=&amp;quot;circle2&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;
      &amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;/main&amp;amp;gt;
  &amp;amp;lt;/body&amp;amp;gt;
&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Vincular o arquivo &amp;lt;code&amp;gt;style.css&amp;lt;/code&amp;gt; à página html:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;/head&amp;amp;gt;
  &amp;amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;style.css&amp;quot; /&amp;amp;gt;
&amp;amp;lt;/head&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;toc_inicio&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;début_texts&amp;quot;&amp;gt;Os textos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;/* import remote de font sur l&amp;#39;ensemble de la feuille*/
@import url(&amp;quot;https://fonts.googleapis.com/css2?family=Oswald:wght@500&amp;amp;amp;display=swap&amp;quot;);

/* import local de font */
@font-face {
  font-family: &amp;quot;DMSerif&amp;quot;;
  src: url(./assets/fonts/DMSerifDisplay-Regular.ttf);
}

/*
body est le parent de toutes les balises,
qu&amp;#39;il contient, donc je le déclare avant,
pour la logique de précédence de parent
*/
body {
  /* si DMSerif n&amp;#39;est pas trouvé alors il se rabat sur Verdana*/
  font-family: &amp;quot;DMSerif&amp;quot;, Verdana;
}

/* va styliser toutes les balises h1,
celles de body sont surchargés,
par la déclaration présente */
h1 {
  text-transform: uppercase;
  /* espacement entre les lettres en pixel*/
  letter-spacing: 3px;
  /* alignement du texte*/
  text-align: center;
  /* Les tailles de polices doivent être en REM  (rem = root em).
  Le REM se base pas sur l&amp;#39;élément parent pour obtenir sa taille mais sur l&amp;#39;élément racine. Ainsi 1rem prendra sa valeur de la font-size de votre document (body ou html).*/
  font-size: 2.5rem;
  /* ombrage du texte */
  text-shadow:
  3px /* offset-x */
  3px /* offset-y */
  8px /* blur-radius */
  #00000042/* color */;
  /* couleur du texte */
  color: #ab0ef4;
  /*attibuer les polices(font): liste, ordonnée par priorité, de polices à utiliser pour mettre en forme le texte de l&amp;#39;élément ciblé.*/
  font-family: &amp;quot;Oswald&amp;quot;, sans-serif;
  /* surligner le text*/
  text-decoration: underline;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_início&amp;quot;&amp;gt;[toc_início]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;début_background&amp;quot;&amp;gt;Contexto&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;@import url(&amp;quot;https://fonts.googleapis.com/css2?family=Oswald:wght@500&amp;amp;amp;display=swap&amp;quot;);

@font-face {
  font-family: &amp;quot;DMSerif&amp;quot;;
  src: url(./assets/fonts/DMSerifDisplay-Regular.ttf);
}

body {
  font-family: &amp;quot;DMSerif&amp;quot;, Verdana;
  /* image comme background*/
  background: url(./assets/img/bg.jpg) center/cover;
  /* 100VH = 100% de la taille de l&amp;#39;écran (viewport height)
  min afin de ne pas bloquer la possibilité d&amp;#39;avoir un plus grand */
  min-height: 100vh;
}

h1 {
  text-transform: uppercase;
  letter-spacing: 3px;
  text-align: center;
  font-size: 2.5rem;
  text-shadow: 3px 3px 8px #00000042;
  color: #ab0ef4;
  font-family: &amp;quot;Oswald&amp;quot;, sans-serif;
  text-decoration: underline;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#sumário_início&amp;quot;&amp;gt;[sumário_início]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;début_boxes&amp;quot;&amp;gt;As caixas (boxes)&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;@import url(&amp;quot;https://fonts.googleapis.com/css2?family=Oswald:wght@500&amp;amp;amp;display=swap&amp;quot;);

@font-face {
  font-family: &amp;quot;DMSerif&amp;quot;;
  src: url(./assets/fonts/DMSerifDisplay-Regular.ttf);
}

/* *: létoile donne du style a tous les éléments.
Ensuite remettre un à un les parametres par defaut afin d&amp;#39;eviter les stylisation inattendu.*/
* {
  /* surcharge de base tous les elements à padding 0*/
  margin: 0;
  /* surcharge de base tous les elements à padding 0*/
  padding: 0;
}

body {
  font-family: &amp;quot;DMSerif&amp;quot;, Verdana;
  /* image comme background*/
  background: url(./assets/img/bg.jpg) center/cover;
  min-height: 100vh;
}

h1 {
  text-transform: uppercase;
  letter-spacing: 3px;
  text-align: center;
  font-size: 2.5rem;
  text-shadow: 3px 3px 8px #00000042;
  color: #ab0ef4;
  font-family: &amp;quot;Oswald&amp;quot;, sans-serif;
  text-decoration: underline;
}

main {
  background: rgba(245, 245, 245, 0.9);
  min-height: 500px;
  /* quelque soit la taille de l&amp;#39;écran,
  la boite fait toujours 90% de la taille de la page,
  les 10% restant sont une marge à droite,
  pour avoir 5% de chaque coté on ajoute une margin, tel que [0, auto]*/
  width: 90%;
  /* haut bas prend 0, et  gauche droite prend auto(center cad meme de chaque cotés)*/
  margin: 0 auto;
  /* on ajoute des bords a notre boite*/
  border: 2px solid rgb(0, 140, 255);
  /* on arrondis les coins de la boite*/
  border-radius: 20px 20px 0 0;
  /* ombrage sur la boite*/
  box-shadow: 0px 0px 20px 4px #81cfc6;
  /* marge interrieure de la boite sur tous les cotés*/
  padding: 15px;
}

h2 {
  /* afin de recuprer le comportement par defaut qui a ete surchargé par l&amp;#39;operateur étoile*/
  margin: 0;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html lang=&amp;quot;fr&amp;quot;&amp;amp;gt;
  &amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;IE=edge&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;title&amp;amp;gt;Cours CSS&amp;amp;lt;/title&amp;amp;gt;

    &amp;amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;style.css&amp;quot; /&amp;amp;gt;
  &amp;amp;lt;/head&amp;amp;gt;
  &amp;amp;lt;body&amp;amp;gt;
    &amp;amp;lt;header&amp;amp;gt;
      &amp;amp;lt;h1&amp;amp;gt;Les bases de CSS&amp;amp;lt;/h1&amp;amp;gt;
    &amp;amp;lt;/header&amp;amp;gt;

    &amp;amp;lt;main&amp;amp;gt;
      &amp;amp;lt;div class=&amp;quot;flexbox&amp;quot;&amp;amp;gt;
        &amp;amp;lt;h2&amp;amp;gt;Flexbox&amp;amp;lt;/h2&amp;amp;gt;
        &amp;amp;lt;ul&amp;amp;gt;
          &amp;amp;lt;li&amp;amp;gt;boite 1&amp;amp;lt;/li&amp;amp;gt;
          &amp;amp;lt;li&amp;amp;gt;boite 2&amp;amp;lt;/li&amp;amp;gt;
          &amp;amp;lt;li&amp;amp;gt;boite 3&amp;amp;lt;/li&amp;amp;gt;
        &amp;amp;lt;/ul&amp;amp;gt;
      &amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;/main&amp;amp;gt;
  &amp;amp;lt;/body&amp;amp;gt;
&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_início&amp;quot;&amp;gt;[toc_início]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;início_flexbox&amp;quot;&amp;gt;Flexbox&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;@import url(&amp;quot;https://fonts.googleapis.com/css2?family=Oswald:wght@500&amp;amp;amp;display=swap&amp;quot;);

@font-face {
  font-family: &amp;quot;DMSerif&amp;quot;;
  src: url(./assets/fonts/DMSerifDisplay-Regular.ttf);
}

body {
  font-family: &amp;quot;DMSerif&amp;quot;, Verdana;
  background: url(./assets/img/bg.jpg) center/cover;
  min-height: 100vh;
}

h1 {
  text-transform: uppercase;
  letter-spacing: 3px;
  text-align: center;
  font-size: 2.5rem;
  text-shadow: 3px 3px 8px #00000042;
  color: #ab0ef4;
  font-family: &amp;quot;Oswald&amp;quot;, sans-serif;
  text-decoration: underline;
}

main {
  background: rgba(245, 245, 245, 0.9);
  min-height: 500px;
  width: 90%;
  margin: 0 auto;
  border: 2px solid rgb(0, 140, 255);
  border-radius: 20px 20px 0 0;
  box-shadow: 0px 0px 20px 4px #81cfc6;
  padding: 15px;
}

h2 {
  margin: 0;
}

/* le point permet d&amp;#39;acceder à la classe d&amp;#39;un composant*/
.flexbox {
  border: 2px solid skyblue;
  border-radius: 10px;
  padding: 10px;
  margin-top: 20px;
  min-height: 150px;
}

/* FLEXBOX */
/* Sert à répartir équitablement des éléments sur la page */
.flexbox ul {
  padding: 0;
  /* aligne les éléments enfants de ul(les li)*/
  display: flex;
  /* reparti convenablement(équitablement les li sur la page) */
  justify-content: space-around;
}
/* ce style concerne les li de la classe,
flexbox des balises qui y font referencent*/
.flexbox li {
  /* suprime les boules des éléments de la list li*/
  list-style: none;
  height: 160px;
  width: 160px;
  margin: 10px;
  background: turquoise;
  /* Centrer un unique élément verticalement et horizontalement */
  display: flex;
  justify-content: center;
  align-items: center;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html lang=&amp;quot;fr&amp;quot;&amp;amp;gt;
  &amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;IE=edge&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;title&amp;amp;gt;Cours CSS&amp;amp;lt;/title&amp;amp;gt;

    &amp;amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;style.css&amp;quot; /&amp;amp;gt;
  &amp;amp;lt;/head&amp;amp;gt;
  &amp;amp;lt;body&amp;amp;gt;
    &amp;amp;lt;header&amp;amp;gt;
      &amp;amp;lt;h1&amp;amp;gt;Les bases de CSS&amp;amp;lt;/h1&amp;amp;gt;
    &amp;amp;lt;/header&amp;amp;gt;

    &amp;amp;lt;main&amp;amp;gt;
      &amp;amp;lt;div class=&amp;quot;flexbox&amp;quot;&amp;amp;gt;
        &amp;amp;lt;h2&amp;amp;gt;Flexbox&amp;amp;lt;/h2&amp;amp;gt;
        &amp;amp;lt;ul&amp;amp;gt;
          &amp;amp;lt;li&amp;amp;gt;boite 1&amp;amp;lt;/li&amp;amp;gt;
          &amp;amp;lt;li&amp;amp;gt;boite 2&amp;amp;lt;/li&amp;amp;gt;
          &amp;amp;lt;li&amp;amp;gt;boite 3&amp;amp;lt;/li&amp;amp;gt;
        &amp;amp;lt;/ul&amp;amp;gt;
      &amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;/main&amp;amp;gt;
  &amp;amp;lt;/body&amp;amp;gt;
&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_início&amp;quot;&amp;gt;[toc_início]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;début_grid&amp;quot;&amp;gt;Grade&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para distribuir elementos de maneira um pouco mais complexa do que com flexbox, com um sistema de grade, entre outros.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;@import url(&amp;quot;https://fonts.googleapis.com/css2?family=Oswald:wght@500&amp;amp;amp;display=swap&amp;quot;);

@font-face {
  font-family: &amp;quot;DMSerif&amp;quot;;
  src: url(./assets/fonts/DMSerifDisplay-Regular.ttf);
}

body {
  font-family: &amp;quot;DMSerif&amp;quot;, Verdana;
  background: url(./assets/img/bg.jpg) center/cover;
  min-height: 100vh;
}

h1 {
  text-transform: uppercase;
  letter-spacing: 3px;
  text-align: center;
  font-size: 2.5rem;
  text-shadow: 3px 3px 8px #00000042;
  color: #ab0ef4;
  font-family: &amp;quot;Oswald&amp;quot;, sans-serif;
  text-decoration: underline;
}

main {
  background: rgba(245, 245, 245, 0.9);
  min-height: 500px;
  width: 90%;
  margin: 0 auto;
  border: 2px solid rgb(0, 140, 255);
  border-radius: 20px 20px 0 0;
  box-shadow: 0px 0px 20px 4px #81cfc6;
  padding: 15px;
}

h2 {
  margin: 0;
}

.flexbox,
.grid {
  border: 2px solid skyblue;
  border-radius: 10px;
  padding: 10px;
  margin-top: 20px;
  min-height: 150px;
}

.flexbox ul {
  padding: 0;
  display: flex;
  justify-content: space-around;
}

.flexbox li {
  list-style: none;
  height: 160px;
  width: 160px;
  margin: 10px;
  background: turquoise;
  display: flex;
  justify-content: center;
  align-items: center;
}

/* GRID */
.grid-container {
  display: grid;
  /* séparatuion de la grille en deux(à ses deux enfants),
  avec à gauche 30% pour l&amp;#39;image
  et à droite 70% pour le formulaire*/
  grid-template-columns: 30% 70%;
}

/* sizing de l&amp;#39;image css-logo
contenu dans la classe grid
qui est dans le parent grid-container*/
.grid img {
  /* l&amp;#39;image remplt 80% de part de grille*/
  width: 80%;
  margin: 20px auto;
  display: block;
}

form {
  display: grid;
  /* deux colonnes de chacune 1 fraction*/
  grid-template-columns: 1fr 1fr;
  /* trois rangés de chacune 1 fraction*/
    grid-template-rows: 1fr 1fr 1fr;
    /* structuration sous forme de template
    de notre repartition des composants
    enfants de la forme*/
  grid-template-areas:
    &amp;quot;i1 i2&amp;quot;/* i1: input1, i2:input2*/
    &amp;quot;ta ta&amp;quot;/*ta: text area */
    &amp;quot;vi bt&amp;quot;/*vi:, bt: bouton */;
}

/* on donne du style sur deux éléments
en meme temps en les listant
avant les parentheses*/
input,
textarea {
  margin: 5px;
  border: 1px solid darkblue;
  padding: 10px;
  font-size: 1.1rem;
  font-family: &amp;quot;DMSerif&amp;quot;;
  border-radius: 5px;
}

/* donner du style uniquement la textearea*/
textarea {
  /* renseigner le nommage pour le grid template area*/
  grid-area: ta;
  height: 40px;
  /* empeche le cassage du style de la page
  en empechant le resizing de la textarea
  du formulaire*/
  resize: none;
}
/* pour pointer un id d&amp;#39;élément html
on utilise la notation préfixe # */
#btn-submit {
  /* renseigner le nommage*/
  grid-area: bt;
  /* change le le symbole de curseur
  souris quand il survole sur le bouton*/
  cursor: pointer;
  background: cyan;
  transition: 0.2s;
}

/* au survole(hover) de la souris
le bouton change de comportement*/
#btn-submit:hover {
  background: darkblue;
  /* couleur de text du bouton devient blanc*/
  color: white;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html lang=&amp;quot;fr&amp;quot;&amp;amp;gt;

&amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;IE=edge&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;title&amp;amp;gt;Cours CSS&amp;amp;lt;/title&amp;amp;gt;

    &amp;amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;style.css&amp;quot; /&amp;amp;gt;
&amp;amp;lt;/head&amp;amp;gt;

&amp;amp;lt;body&amp;amp;gt;
    &amp;amp;lt;header&amp;amp;gt;
        &amp;amp;lt;h1&amp;amp;gt;Les bases de CSS&amp;amp;lt;/h1&amp;amp;gt;
    &amp;amp;lt;/header&amp;amp;gt;

    &amp;amp;lt;main&amp;amp;gt;
        &amp;amp;lt;div class=&amp;quot;flexbox&amp;quot;&amp;amp;gt;
            &amp;amp;lt;h2&amp;amp;gt;Flexbox&amp;amp;lt;/h2&amp;amp;gt;
            &amp;amp;lt;ul&amp;amp;gt;
                &amp;amp;lt;li&amp;amp;gt;boite 1&amp;amp;lt;/li&amp;amp;gt;
                &amp;amp;lt;li&amp;amp;gt;boite 2&amp;amp;lt;/li&amp;amp;gt;
                &amp;amp;lt;li&amp;amp;gt;boite 3&amp;amp;lt;/li&amp;amp;gt;
            &amp;amp;lt;/ul&amp;amp;gt;
        &amp;amp;lt;/div&amp;amp;gt;

        &amp;amp;lt;div class=&amp;quot;grid&amp;quot;&amp;amp;gt;
            &amp;amp;lt;h2&amp;amp;gt;Grid&amp;amp;lt;/h2&amp;amp;gt;
            &amp;amp;lt;div class=&amp;quot;grid-container&amp;quot;&amp;amp;gt;
                &amp;amp;lt;img src=&amp;quot;./assets/img/css-logo.png&amp;quot; alt=&amp;quot;logo css&amp;quot; /&amp;amp;gt;

                &amp;amp;lt;form action=&amp;quot;&amp;quot;&amp;amp;gt;
                    &amp;amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;firstname&amp;quot; placeholder=&amp;quot;Prénom&amp;quot; /&amp;amp;gt;
                    &amp;amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;surname&amp;quot; placeholder=&amp;quot;Nom&amp;quot; /&amp;amp;gt;
                    &amp;amp;lt;textarea cols=&amp;quot;30&amp;quot; rows=&amp;quot;10&amp;quot; placeholder=&amp;quot;Ici votre message&amp;quot;&amp;amp;gt;&amp;amp;lt;/textarea&amp;amp;gt;
                    &amp;amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Valider&amp;quot; id=&amp;quot;btn-submit&amp;quot; /&amp;amp;gt;
                &amp;amp;lt;/form&amp;amp;gt;
            &amp;amp;lt;/div&amp;amp;gt;
        &amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;/main&amp;amp;gt;
&amp;amp;lt;/body&amp;amp;gt;

&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_início&amp;quot;&amp;gt;[toc_início]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;début_abs_pos&amp;quot;&amp;gt;Posição absoluta&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;@import url(&amp;quot;https://fonts.googleapis.com/css2?family=Oswald:wght@500&amp;amp;amp;display=swap&amp;quot;);

@font-face {
  font-family: &amp;quot;DMSerif&amp;quot;;
  src: url(./assets/fonts/DMSerifDisplay-Regular.ttf);
}

body {
  font-family: &amp;quot;DMSerif&amp;quot;, Verdana;
  background: url(./assets/img/bg.jpg) center/cover;
  min-height: 100vh;
}

h1 {
  text-transform: uppercase;
  letter-spacing: 3px;
  text-align: center;
  font-size: 2.5rem;
  text-shadow: 3px 3px 8px #00000042;
  color: #ab0ef4;
  font-family: &amp;quot;Oswald&amp;quot;, sans-serif;
  text-decoration: underline;
}

main {
  background: rgba(245, 245, 245, 0.9);
  min-height: 500px;
  width: 90%;
  margin: 0 auto;
  border: 2px solid rgb(0, 140, 255);
  border-radius: 20px 20px 0 0;
  box-shadow: 0px 0px 20px 4px #81cfc6;
  padding: 15px;
}

h2 {
  margin: 0;
}

.flexbox,
.grid,
.absolute {
  border: 2px solid skyblue;
  border-radius: 10px;
  padding: 10px;
  margin-top: 20px;
  min-height: 150px;
}

.flexbox ul {
  padding: 0;
  display: flex;
  justify-content: space-around;
}

.flexbox li {
  list-style: none;
  height: 160px;
  width: 160px;
  margin: 10px;
  background: turquoise;
  display: flex;
  justify-content: center;
  align-items: center;
}

.grid-container {
  display: grid;
  grid-template-columns: 30% 70%;
}

.grid img {
  width: 80%;
  margin: 20px auto;
  display: block;
}

form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
  grid-template-areas: &amp;quot;i1 i2&amp;quot; &amp;quot;ta ta&amp;quot; &amp;quot;vi bt&amp;quot;;
}

input,
textarea {
  margin: 5px;
  border: 1px solid darkblue;
  padding: 10px;
  font-size: 1.1rem;
  font-family: &amp;quot;DMSerif&amp;quot;;
  border-radius: 5px;
}

textarea {
  grid-area: ta;
  height: 40px;
  resize: none;
}

#btn-submit {
  grid-area: bt;
  cursor: pointer;
  background: cyan;
  transition: 0.2s;
}

#btn-submit:hover {
  background: darkblue;
  color: white;
}

/* ABSOLUTE  */
/* Sans élément en Relative, de base, l&amp;#39;élément en absolute l&amp;#39;est par rapport au Body  */
/* Il faut mettre une position relative au parent pour contraindre l&amp;#39;élément en absolute dans ses frontières  */
.absolute {
  /* position relative par rapport à son parent,
  donc contenu dans les frontieres de son parents*/
  position: relative;
}


#circle1 {
  height: 80px;
  width: 80px;
  background: skyblue;
  position: absolute;
  border-radius: 150px;
  top: -20px;
  right: -20px;
}

#circle2 {
  height: 40px;
  width: 40px;
  border-radius: 150px;
  background: teal;
  position: absolute;
  left: 50%;
  transform:
  /*traslate de 50% de la taille du cercle,
  pour etre centré par rapport au centre du cercle,
  plutot que sur son bord gauche par défaut.*/
  translateX(-50%);
  top: 100px;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html lang=&amp;quot;fr&amp;quot;&amp;amp;gt;

&amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;IE=edge&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;title&amp;amp;gt;Cours CSS&amp;amp;lt;/title&amp;amp;gt;

    &amp;amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;style.css&amp;quot; /&amp;amp;gt;
&amp;amp;lt;/head&amp;amp;gt;

&amp;amp;lt;body&amp;amp;gt;
    &amp;amp;lt;header&amp;amp;gt;
        &amp;amp;lt;h1&amp;amp;gt;Les bases de CSS&amp;amp;lt;/h1&amp;amp;gt;
    &amp;amp;lt;/header&amp;amp;gt;

    &amp;amp;lt;main&amp;amp;gt;
        &amp;amp;lt;div class=&amp;quot;flexbox&amp;quot;&amp;amp;gt;
            &amp;amp;lt;h2&amp;amp;gt;Flexbox&amp;amp;lt;/h2&amp;amp;gt;
            &amp;amp;lt;ul&amp;amp;gt;
                &amp;amp;lt;li&amp;amp;gt;boite 1&amp;amp;lt;/li&amp;amp;gt;
                &amp;amp;lt;li&amp;amp;gt;boite 2&amp;amp;lt;/li&amp;amp;gt;
                &amp;amp;lt;li&amp;amp;gt;boite 3&amp;amp;lt;/li&amp;amp;gt;
            &amp;amp;lt;/ul&amp;amp;gt;
        &amp;amp;lt;/div&amp;amp;gt;

        &amp;amp;lt;div class=&amp;quot;grid&amp;quot;&amp;amp;gt;
            &amp;amp;lt;h2&amp;amp;gt;Grid&amp;amp;lt;/h2&amp;amp;gt;
            &amp;amp;lt;div class=&amp;quot;grid-container&amp;quot;&amp;amp;gt;
                &amp;amp;lt;img src=&amp;quot;./assets/img/css-logo.png&amp;quot; alt=&amp;quot;logo css&amp;quot; /&amp;amp;gt;

                &amp;amp;lt;form action=&amp;quot;&amp;quot;&amp;amp;gt;
                    &amp;amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;firstname&amp;quot; placeholder=&amp;quot;Prénom&amp;quot; /&amp;amp;gt;
                    &amp;amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;surname&amp;quot; placeholder=&amp;quot;Nom&amp;quot; /&amp;amp;gt;
                    &amp;amp;lt;textarea cols=&amp;quot;30&amp;quot; rows=&amp;quot;10&amp;quot; placeholder=&amp;quot;Ici votre message&amp;quot;&amp;amp;gt;&amp;amp;lt;/textarea&amp;amp;gt;
                    &amp;amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Valider&amp;quot; id=&amp;quot;btn-submit&amp;quot; /&amp;amp;gt;
                &amp;amp;lt;/form&amp;amp;gt;
            &amp;amp;lt;/div&amp;amp;gt;
        &amp;amp;lt;/div&amp;amp;gt;

        &amp;amp;lt;div class=&amp;quot;absolute&amp;quot;&amp;amp;gt;
            &amp;amp;lt;h2&amp;amp;gt;Absolute&amp;amp;lt;/h2&amp;amp;gt;
            &amp;amp;lt;span id=&amp;quot;circle1&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;
            &amp;amp;lt;span id=&amp;quot;circle2&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;
        &amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;/main&amp;amp;gt;
&amp;amp;lt;/body&amp;amp;gt;

&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_início&amp;quot;&amp;gt;[toc_início]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;début_responsive&amp;quot;&amp;gt;O responsivo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;@import url(&amp;quot;https://fonts.googleapis.com/css2?family=Oswald:wght@500&amp;amp;amp;display=swap&amp;quot;);

@font-face {
  font-family: &amp;quot;DMSerif&amp;quot;;
  src: url(./assets/fonts/DMSerifDisplay-Regular.ttf);
}

body {
  font-family: &amp;quot;DMSerif&amp;quot;, Verdana;
  background: url(./assets/img/bg.jpg) center/cover;
  min-height: 100vh;
}

h1 {
  text-transform: uppercase;
  letter-spacing: 3px;
  text-align: center;
  /* pour etre plus responsive on peut passer la taille en vw(view port width) pour qu&amp;#39;elle s&amp;#39;adapte à la taille de l&amp;#39;ecran*/
  font-size: 2.5rem;
  text-shadow: 3px 3px 8px #00000042;
  color: #ab0ef4;
  font-family: &amp;quot;Oswald&amp;quot;, sans-serif;
  text-decoration: underline;
}

main {
  background: rgba(245, 245, 245, 0.9);
  min-height: 500px;
  width: 90%;
  margin: 0 auto;
  border: 2px solid rgb(0, 140, 255);
  border-radius: 20px 20px 0 0;
  box-shadow: 0px 0px 20px 4px #81cfc6;
  padding: 15px;
}

h2 {
  margin: 0;
}

.flexbox,
.grid,
.absolute {
  border: 2px solid skyblue;
  border-radius: 10px;
  padding: 10px;
  margin-top: 20px;
  min-height: 150px;
}

.flexbox ul {
  padding: 0;
  display: flex;
  justify-content: space-around;
}

.flexbox li {
  list-style: none;
  height: 160px;
  width: 160px;
  margin: 10px;
  background: turquoise;
  display: flex;
  justify-content: center;
  align-items: center;
}

.grid-container {
  display: grid;
  grid-template-columns: 30% 70%;
}

.grid img {
  width: 80%;
  margin: 20px auto;
  /* permet en media query de la centrer en petit écran*/
  display: block;
}

form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
  grid-template-areas: &amp;quot;i1 i2&amp;quot; &amp;quot;ta ta&amp;quot; &amp;quot;vi bt&amp;quot;;
}

input,
textarea {
  margin: 5px;
  border: 1px solid darkblue;
  padding: 10px;
  font-size: 1.1rem;
  font-family: &amp;quot;DMSerif&amp;quot;;
  border-radius: 5px;
}

textarea {
  grid-area: ta;
  height: 40px;
  resize: none;
}

#btn-submit {
  grid-area: bt;
  cursor: pointer;
  background: cyan;
  transition: 0.2s;
}

#btn-submit:hover {
  background: darkblue;
  color: white;
}

.absolute {
  position: relative;
}


#circle1 {
  height: 80px;
  width: 80px;
  background: skyblue;
  position: absolute;
  border-radius: 150px;
  top: -20px;
  right: -20px;
}

#circle2 {
  height: 40px;
  width: 40px;
  border-radius: 150px;
  background: teal;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  top: 100px;
}

/* RESPONSIVE:
a partir des tailles d&amp;#39;ecran permet de surcharger les reglages definis plus haut
*/
@media screen and (max-width: 900px) {
  .grid-container {
    display: block;
  }
  .grid-container img {
    width: 40%;
  }
}

@media screen and (max-width: 610px) {
  .flexbox ul {
    flex-direction: column;
    align-items: center;
  }

  form {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
    grid-template-areas:
      &amp;quot;i1&amp;quot;
      &amp;quot;i2&amp;quot;
      &amp;quot;ta&amp;quot;
      &amp;quot;bt&amp;quot;;
  }
  input,
  textarea {
    font-size: 0.8rem;
  }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html lang=&amp;quot;fr&amp;quot;&amp;amp;gt;

&amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;IE=edge&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;title&amp;amp;gt;Cours CSS&amp;amp;lt;/title&amp;amp;gt;

    &amp;amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;style.css&amp;quot; /&amp;amp;gt;
&amp;amp;lt;/head&amp;amp;gt;

&amp;amp;lt;body&amp;amp;gt;
    &amp;amp;lt;header&amp;amp;gt;
        &amp;amp;lt;h1&amp;amp;gt;Les bases de CSS&amp;amp;lt;/h1&amp;amp;gt;
    &amp;amp;lt;/header&amp;amp;gt;

    &amp;amp;lt;main&amp;amp;gt;
        &amp;amp;lt;div class=&amp;quot;flexbox&amp;quot;&amp;amp;gt;
            &amp;amp;lt;h2&amp;amp;gt;Flexbox&amp;amp;lt;/h2&amp;amp;gt;
            &amp;amp;lt;ul&amp;amp;gt;
                &amp;amp;lt;li&amp;amp;gt;boite 1&amp;amp;lt;/li&amp;amp;gt;
                &amp;amp;lt;li&amp;amp;gt;boite 2&amp;amp;lt;/li&amp;amp;gt;
                &amp;amp;lt;li&amp;amp;gt;boite 3&amp;amp;lt;/li&amp;amp;gt;
            &amp;amp;lt;/ul&amp;amp;gt;
        &amp;amp;lt;/div&amp;amp;gt;

        &amp;amp;lt;div class=&amp;quot;grid&amp;quot;&amp;amp;gt;
            &amp;amp;lt;h2&amp;amp;gt;Grid&amp;amp;lt;/h2&amp;amp;gt;
            &amp;amp;lt;div class=&amp;quot;grid-container&amp;quot;&amp;amp;gt;
                &amp;amp;lt;img src=&amp;quot;./assets/img/css-logo.png&amp;quot; alt=&amp;quot;logo css&amp;quot; /&amp;amp;gt;

                &amp;amp;lt;form action=&amp;quot;&amp;quot;&amp;amp;gt;
                    &amp;amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;firstname&amp;quot; placeholder=&amp;quot;Prénom&amp;quot; /&amp;amp;gt;
                    &amp;amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;surname&amp;quot; placeholder=&amp;quot;Nom&amp;quot; /&amp;amp;gt;
                    &amp;amp;lt;textarea cols=&amp;quot;30&amp;quot; rows=&amp;quot;10&amp;quot; placeholder=&amp;quot;Ici votre message&amp;quot;&amp;amp;gt;&amp;amp;lt;/textarea&amp;amp;gt;
                    &amp;amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Valider&amp;quot; id=&amp;quot;btn-submit&amp;quot; /&amp;amp;gt;
                &amp;amp;lt;/form&amp;amp;gt;
            &amp;amp;lt;/div&amp;amp;gt;
        &amp;amp;lt;/div&amp;amp;gt;

        &amp;amp;lt;div class=&amp;quot;absolute&amp;quot;&amp;amp;gt;
            &amp;amp;lt;h2&amp;amp;gt;Absolute&amp;amp;lt;/h2&amp;amp;gt;
            &amp;amp;lt;span id=&amp;quot;circle1&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;
            &amp;amp;lt;span id=&amp;quot;circle2&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;
        &amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;/main&amp;amp;gt;
&amp;amp;lt;/body&amp;amp;gt;

&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_início&amp;quot;&amp;gt;[toc_início]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;início_conclusão&amp;quot;&amp;gt;Depuração &amp;amp;amp; Conclusão&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-css&amp;quot; data-lang=&amp;quot;css&amp;quot;&amp;gt;/* all elements:
supprime les comportements par défaut*/
* {
  /* les marges à zéro*/
  margin: 0;
  /* les parge internes(padding) à zéro*/
  padding: 0;
  /* tous les bords avec un cadre rouge de 2px.
  qui est ou et qui fait quoi...*/
  border: 2px solid red;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;toc_início&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Memo HTML</title>
            <link >https://cheroliv.com//blog/2023/0061_memo_html_post.html</link>
            <pubDate>Sun, 13 Aug 2023 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2023/0061_memo_html_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_bons_links&amp;quot;&amp;gt;bons links&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_repositório_e_exemplo_de_código&amp;quot;&amp;gt;Repositório e exemplo de código&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_bons_links&amp;quot;&amp;gt;bons links&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;html&amp;lt;/div&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://htmlcheatsheet.com&amp;quot; target=&amp;quot;&amp;quot;_blank&amp;quot;&amp;gt;htmlcheatsheet.com&amp;lt;/a&amp;gt;: folha de dicas de HTML&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;font&amp;lt;/div&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://fontawesome.com&amp;quot;&amp;gt;fontawesome.com&amp;lt;/a&amp;gt;: biblioteca de ícones&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://www.toptal.com/designers/htmlarrows/symbols/&amp;quot;&amp;gt;toptal.com/designers/htmlarrows/symbols&amp;lt;/a&amp;gt;: Ícones nativos em HTML&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;caracteres especiais e escapes&amp;lt;/div&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://www.sonarsource.com/blog/encoding-differentials-why-charset-matters/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://www.sonarsource.com/blog/encoding-differentials-why-charset-matters/&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://stackoverflow.com/a/3614344/13104550&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://stackoverflow.com/a/3614344/13104550&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://html.spec.whatwg.org/multipage/named-characters.html&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://html.spec.whatwg.org/multipage/named-characters.html&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc&amp;quot;&amp;gt;[toc]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_repositório_e_exemplo_de_código&amp;quot;&amp;gt;Repositório e exemplo de código&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;repositório do GitHub:https://github.com/cheroliv/html-codebase[html-codebase, window = &amp;quot;_blank]&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;memo fonte:&amp;lt;br&amp;gt;
index.html&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlightjs highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html hljs&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html lang=&amp;quot;fr&amp;quot;&amp;amp;gt;

&amp;amp;lt;head&amp;amp;gt;
  &amp;amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;amp;gt;
  &amp;amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;amp;gt;
  &amp;amp;lt;!-- Le titre de la page  --&amp;amp;gt;
  &amp;amp;lt;title&amp;amp;gt;Cours HTML&amp;amp;lt;/title&amp;amp;gt;
  &amp;amp;lt;!-- Icone de l&amp;#39;onglet --&amp;amp;gt;
  &amp;amp;lt;link rel=&amp;quot;shortcut icon&amp;quot; href=&amp;quot;html-logo.png&amp;quot; /&amp;amp;gt;
  &amp;amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;https://use.fontawesome.com/releases/v5.8.2/css/all.css&amp;quot; /&amp;amp;gt;
&amp;amp;lt;/head&amp;amp;gt;

&amp;amp;lt;body&amp;amp;gt;
  &amp;amp;lt;header&amp;amp;gt;
    &amp;amp;lt;h1&amp;amp;gt;Texte - Titre H1&amp;amp;lt;/h1&amp;amp;gt;
    &amp;amp;lt;p&amp;amp;gt;
      &amp;amp;lt;em&amp;amp;gt;em pour mettre en italique&amp;amp;lt;/em&amp;amp;gt;,
      &amp;amp;lt;strong&amp;amp;gt;strong pour mettre en gras&amp;amp;lt;/strong&amp;amp;gt;
      &amp;amp;lt;span&amp;amp;gt;L&amp;#39;élement en span ne revient pas à la ligne&amp;amp;lt;/span&amp;amp;gt;.
    &amp;amp;lt;/p&amp;amp;gt;
    &amp;amp;lt;p&amp;amp;gt;
      Lorem ipsum dolor sit amet consectetur adipisicing elit. Exercitationem
      cumque ratione veniam officiis nulla a debitis at facilis sed
      dignissimos.
    &amp;amp;lt;/p&amp;amp;gt;
  &amp;amp;lt;/header&amp;amp;gt;

  &amp;amp;lt;section&amp;amp;gt;
    &amp;amp;lt;div&amp;amp;gt;
      &amp;amp;lt;h2&amp;amp;gt;Photo - Titre H2&amp;amp;lt;/h2&amp;amp;gt;
      &amp;amp;lt;img src=&amp;quot;./img-1.jpg&amp;quot; alt=&amp;quot;image-arbre&amp;quot; height=&amp;quot;200&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;/div&amp;amp;gt;

    &amp;amp;lt;div&amp;amp;gt;
      &amp;amp;lt;h3&amp;amp;gt;Liste - Titre H3&amp;amp;lt;/h3&amp;amp;gt;
      &amp;amp;lt;ul&amp;amp;gt;
        &amp;amp;lt;li&amp;amp;gt;UL = unordered list&amp;amp;lt;/li&amp;amp;gt;
        &amp;amp;lt;li&amp;amp;gt;UL = unordered list&amp;amp;lt;/li&amp;amp;gt;
        &amp;amp;lt;li&amp;amp;gt;UL = unordered list&amp;amp;lt;/li&amp;amp;gt;
      &amp;amp;lt;/ul&amp;amp;gt;
      &amp;amp;lt;ol&amp;amp;gt;
        &amp;amp;lt;li&amp;amp;gt;OL = ordered list&amp;amp;lt;/li&amp;amp;gt;
        &amp;amp;lt;li&amp;amp;gt;OL = ordered list&amp;amp;lt;/li&amp;amp;gt;
        &amp;amp;lt;li&amp;amp;gt;OL = ordered list&amp;amp;lt;/li&amp;amp;gt;
      &amp;amp;lt;/ol&amp;amp;gt;
    &amp;amp;lt;/div&amp;amp;gt;

    &amp;amp;lt;div&amp;amp;gt;
      &amp;amp;lt;h4&amp;amp;gt;Tableaux - Titre H4&amp;amp;lt;/h4&amp;amp;gt;
      &amp;amp;lt;table border=&amp;quot;4&amp;quot; cellpadding=&amp;quot;10&amp;quot; cellspacing=&amp;quot;4&amp;quot; style=&amp;quot;text-align: center&amp;quot;&amp;amp;gt;
        &amp;amp;lt;thead&amp;amp;gt;
          &amp;amp;lt;tr&amp;amp;gt;
            &amp;amp;lt;th&amp;amp;gt;Col 1&amp;amp;lt;/th&amp;amp;gt;
            &amp;amp;lt;th&amp;amp;gt;Col 2&amp;amp;lt;/th&amp;amp;gt;
            &amp;amp;lt;th&amp;amp;gt;Col 3&amp;amp;lt;/th&amp;amp;gt;
          &amp;amp;lt;/tr&amp;amp;gt;
        &amp;amp;lt;/thead&amp;amp;gt;
        &amp;amp;lt;tbody&amp;amp;gt;
          &amp;amp;lt;tr&amp;amp;gt;
            &amp;amp;lt;td rowspan=&amp;quot;2&amp;quot;&amp;amp;gt;Row 1 Cell 1&amp;amp;lt;/td&amp;amp;gt;
            &amp;amp;lt;td&amp;amp;gt;Row 1 Cell 2&amp;amp;lt;/td&amp;amp;gt;
            &amp;amp;lt;td&amp;amp;gt;Row 1 Cell 3&amp;amp;lt;/td&amp;amp;gt;
          &amp;amp;lt;/tr&amp;amp;gt;
          &amp;amp;lt;tr&amp;amp;gt;
            &amp;amp;lt;td&amp;amp;gt;Row 2 Cell 2&amp;amp;lt;/td&amp;amp;gt;
            &amp;amp;lt;td&amp;amp;gt;Row 2 Cell 3&amp;amp;lt;/td&amp;amp;gt;
          &amp;amp;lt;/tr&amp;amp;gt;
          &amp;amp;lt;tr&amp;amp;gt;
            &amp;amp;lt;td colspan=&amp;quot;3&amp;quot;&amp;amp;gt;Row 3 Cell 1&amp;amp;lt;/td&amp;amp;gt;
          &amp;amp;lt;/tr&amp;amp;gt;
        &amp;amp;lt;/tbody&amp;amp;gt;
      &amp;amp;lt;/table&amp;amp;gt;
    &amp;amp;lt;/div&amp;amp;gt;

    &amp;amp;lt;div&amp;amp;gt;
      &amp;amp;lt;h5&amp;amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;amp;amp;#9814;&amp;amp;lt;/span&amp;amp;gt; Liens - Titre H5&amp;amp;lt;/h5&amp;amp;gt;
      &amp;amp;lt;a href=&amp;quot;https://htmlcheatsheet.com/&amp;quot; target=&amp;quot;_blank&amp;quot;&amp;amp;gt;HTML Cheatsheet&amp;amp;lt;/a&amp;amp;gt;
      &amp;amp;lt;br /&amp;amp;gt;
      &amp;amp;lt;a href=&amp;quot;https://www.toptal.com/designers/htmlarrows/symbols/&amp;quot; target=&amp;quot;_blank&amp;quot;&amp;amp;gt;Icones natifs en HTML&amp;amp;lt;/a&amp;amp;gt;
    &amp;amp;lt;/div&amp;amp;gt;

    &amp;amp;lt;div&amp;amp;gt;
      &amp;amp;lt;h6&amp;amp;gt;&amp;amp;lt;i class=&amp;quot;fas fa-video&amp;quot;&amp;amp;gt;&amp;amp;lt;/i&amp;amp;gt; Vidéo - Titre H6&amp;amp;lt;/h6&amp;amp;gt;
      &amp;amp;lt;video src=&amp;quot;video.mp4&amp;quot; height=&amp;quot;150&amp;quot; autoplay loop muted&amp;amp;gt;&amp;amp;lt;/video&amp;amp;gt;
    &amp;amp;lt;/div&amp;amp;gt;
  &amp;amp;lt;/section&amp;amp;gt;
  &amp;amp;lt;br /&amp;amp;gt;
  &amp;amp;lt;!-- Formulaires en HTML --&amp;amp;gt;
  &amp;amp;lt;section&amp;amp;gt;
    &amp;amp;lt;h2&amp;amp;gt;Formulaire&amp;amp;lt;/h2&amp;amp;gt;
    &amp;amp;lt;form action=&amp;quot;/action.php&amp;quot; method=&amp;quot;post&amp;quot;&amp;amp;gt;
      &amp;amp;lt;label for=&amp;quot;name&amp;quot;&amp;amp;gt;Nom&amp;amp;lt;/label&amp;amp;gt;
      &amp;amp;lt;input id=&amp;quot;name&amp;quot; type=&amp;quot;text&amp;quot; placeholder=&amp;quot;Entrez votre nom&amp;quot; /&amp;amp;gt;&amp;amp;lt;br /&amp;amp;gt;

      &amp;amp;lt;label for=&amp;quot;number&amp;quot;&amp;amp;gt;Entrez votre age&amp;amp;lt;/label&amp;amp;gt;
      &amp;amp;lt;input type=&amp;quot;number&amp;quot; id=&amp;quot;number-age&amp;quot; value=&amp;quot;15&amp;quot; oninput=&amp;quot;document.getElementById(&amp;#39;range-age&amp;#39;).value=this.value&amp;quot;&amp;amp;gt;
      &amp;amp;lt;input type=&amp;quot;range&amp;quot; id=&amp;quot;range-age&amp;quot; min=&amp;quot;0&amp;quot; max=&amp;quot;100&amp;quot;
        oninput=&amp;quot;document.getElementById(&amp;#39;number-age&amp;#39;).value=this.value&amp;quot;&amp;amp;gt;&amp;amp;lt;br /&amp;amp;gt;

      &amp;amp;lt;!-- Input select --&amp;amp;gt;
      &amp;amp;lt;label for=&amp;quot;gender&amp;quot;&amp;amp;gt;Genre&amp;amp;lt;/label&amp;amp;gt;
      &amp;amp;lt;select id=&amp;quot;gender&amp;quot;&amp;amp;gt;
        &amp;amp;lt;option selected=&amp;quot;selected&amp;quot; value=&amp;quot;Homme&amp;quot;&amp;amp;gt;Homme&amp;amp;lt;/option&amp;amp;gt;
        &amp;amp;lt;option value=&amp;quot;Femme&amp;quot;&amp;amp;gt;Femme&amp;amp;lt;/option&amp;amp;gt;
      &amp;amp;lt;/select&amp;amp;gt;&amp;amp;lt;br /&amp;amp;gt;

      &amp;amp;lt;!-- Input radio --&amp;amp;gt;
      &amp;amp;lt;div&amp;amp;gt;
        &amp;amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;type&amp;quot; id=&amp;quot;human&amp;quot; checked /&amp;amp;gt;
        &amp;amp;lt;label for=&amp;quot;human&amp;quot;&amp;amp;gt;Humain&amp;amp;lt;/label&amp;amp;gt;

        &amp;amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;type&amp;quot; id=&amp;quot;dog&amp;quot; /&amp;amp;gt;
        &amp;amp;lt;label for=&amp;quot;dog&amp;quot;&amp;amp;gt;Chien&amp;amp;lt;/label&amp;amp;gt;

        &amp;amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;type&amp;quot; id=&amp;quot;cat&amp;quot; /&amp;amp;gt;
        &amp;amp;lt;label for=&amp;quot;cat&amp;quot;&amp;amp;gt;Chat&amp;amp;lt;/label&amp;amp;gt;
      &amp;amp;lt;/div&amp;amp;gt;

      &amp;amp;lt;textarea cols=&amp;quot;20&amp;quot; rows=&amp;quot;5&amp;quot; placeholder=&amp;quot;Votre message...&amp;quot;&amp;amp;gt;&amp;amp;lt;/textarea&amp;amp;gt;&amp;amp;lt;br /&amp;amp;gt;

      &amp;amp;lt;!-- Input Checkbox --&amp;amp;gt;
      &amp;amp;lt;label&amp;amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; /&amp;amp;gt;Accepter les CGV&amp;amp;lt;/label&amp;amp;gt; &amp;amp;lt;br /&amp;amp;gt;
      &amp;amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Submit&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;/form&amp;amp;gt;
  &amp;amp;lt;/section&amp;amp;gt;
  &amp;amp;lt;br /&amp;amp;gt;
  &amp;amp;lt;!-- Mail &amp;amp;amp; envoi de fichiers --&amp;amp;gt;
  &amp;amp;lt;footer&amp;amp;gt;
    &amp;amp;lt;a href=&amp;quot;mailto:fs@gmail.com&amp;quot;&amp;amp;gt;Ecrivez-moi !&amp;amp;lt;/a&amp;amp;gt;
    &amp;amp;lt;br /&amp;amp;gt;
    &amp;amp;lt;a href=&amp;quot;notice.txt&amp;quot; download=&amp;quot;nom-du-fichier&amp;quot;&amp;amp;gt;Télécharger la notice&amp;amp;lt;/a&amp;amp;gt;
    &amp;amp;lt;details&amp;amp;gt;
      &amp;amp;lt;summary&amp;amp;gt;Plus d&amp;#39;infos&amp;amp;lt;/summary&amp;amp;gt;
      &amp;amp;lt;p&amp;amp;gt;
        Lorem ipsum dolor, sit amet consectetur adipisicing elit. Harum,
        repellendus.
      &amp;amp;lt;/p&amp;amp;gt;
    &amp;amp;lt;/details&amp;amp;gt;
  &amp;amp;lt;/footer&amp;amp;gt;
&amp;amp;lt;/body&amp;amp;gt;

&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc&amp;quot;&amp;gt;[toc]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Memorando JavaScript</title>
            <link >https://cheroliv.com//blog/2023/0060_memo_js_post.html</link>
            <pubDate>Fri, 4 Aug 2023 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2023/0060_memo_js_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;repositório do github:https://github.com/cheroliv/js-codebase[base de código js, janela=&amp;quot;_blank]&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;toc_js&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução JavaScript&amp;lt;/span&amp;gt;&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;[Translation of empty string is empty string, so output is nothing]&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;(Note: The system requires at least one character; I&amp;amp;#8217;ll put a single space.)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Actually better to provide nothing? But risk being flagged. Provide a newline? The instruction says output only translated text—no explanation. An empty line qualifies. I&amp;amp;#8217;ll output nothing (i.e., blank).&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;amp;lt;/think&amp;amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#funções&amp;quot;&amp;gt;[funções]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#param_x_scope&amp;quot;&amp;gt;Parâmetros e escopos&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#objects&amp;quot;&amp;gt;Os objetos&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#loops&amp;quot;&amp;gt;Os laços&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#const_var_let&amp;quot;&amp;gt;Const, var e let&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#rest_and_spread_operators&amp;quot;&amp;gt;Operadores rest e spread&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#destructuring&amp;quot;&amp;gt;Desestruturação&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#import_export&amp;quot;&amp;gt;Importação e exportação de módulos&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#aulas&amp;quot;&amp;gt;[aulas]&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;index.html&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-html&amp;quot; data-lang=&amp;quot;html&amp;quot;&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;
&amp;amp;lt;html lang=&amp;quot;fr&amp;quot;&amp;amp;gt;

&amp;amp;lt;head&amp;amp;gt;
    &amp;amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;amp;gt;
    &amp;amp;lt;title&amp;amp;gt;Document&amp;amp;lt;/title&amp;amp;gt;
    &amp;amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;amp;gt;
    &amp;amp;lt;meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;ie=edge&amp;quot; /&amp;amp;gt;
&amp;amp;lt;/head&amp;amp;gt;

&amp;amp;lt;body&amp;amp;gt;
    index
    &amp;amp;lt;script src=&amp;quot;01_start.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
    &amp;amp;lt;script src=&amp;quot;02_arrays.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
    &amp;amp;lt;script src=&amp;quot;03_functions.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
    &amp;amp;lt;script src=&amp;quot;04_param_x_scope.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
    &amp;amp;lt;script src=&amp;quot;05_objects.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
    &amp;amp;lt;script src=&amp;quot;06_loops.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
    &amp;amp;lt;script src=&amp;quot;07_const_var_let.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
    &amp;amp;lt;script src=&amp;quot;08_rest_and_spread_operators.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
    &amp;amp;lt;script src=&amp;quot;09_destructuring.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
    &amp;amp;lt;script src=&amp;quot;10_module_import_export.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt;
&amp;amp;lt;/body&amp;amp;gt;

&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_js&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução JavaScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_variáveis_operadores_aritméticos_e_cadeias_de_caracteres&amp;quot;&amp;gt;Variáveis, operadores aritméticos e cadeias de caracteres&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;01_start.js&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Variables
 */
//une variable stupide
var my_variable = &amp;quot;dummy variable&amp;quot;;
console.log(my_variable);

//assertion
console.assert(my_variable === &amp;quot;dummy variable&amp;quot;);

var username = &amp;quot;cheroliv&amp;quot;;
console.log(username);
console.assert(username === &amp;quot;cheroliv&amp;quot;);

//concaténation de string
console.assert(username === &amp;quot;cher&amp;quot; + &amp;quot;oliv&amp;quot;);

/**
 *  Opérateur arithmétique: + - * / %
 */
//integer
var x = 10;
var y = 5;
var z = 20;

console.log(x + y);
console.assert(x + y === 15);

console.log(x + y - z);
console.assert(x + y - z === -5);


console.log(x - y);
console.assert(x - y === 5);


console.log(x * y);
console.assert(x * y === 50);

console.log(x / y);
console.assert(x / y === 2);


console.log(&amp;quot;---------&amp;quot;);

x = 20;
y = 10;

console.log(x + y);
console.assert(x + y === 30);

console.log(x + y - z);
console.assert(x + y - z === 10);


console.log(x - y);
console.assert(x - y === 10);

console.log(x * y);
console.assert(x * y === 200);

console.log(x / z);
console.assert(x / z === 1);


//Littéraux de gabarits pour string avec backstick `
//Template literals (Template strings)
console.assert(`typeof z: ${typeof z}` === &amp;quot;typeof z: number&amp;quot;);

console.log(`x is type of : ${typeof x}`);
console.log(`y is type of : ${typeof y}`);
console.log(`z is type of : ${typeof z}`);

//typeof: donne le type de la variable
console.assert(typeof x === &amp;quot;number&amp;quot;);
console.assert(typeof y === &amp;quot;number&amp;quot;);
console.assert(typeof z === &amp;quot;number&amp;quot;);


console.log(&amp;quot;---------&amp;quot;);
/**
 * Incrémenation/décrémentation
 */
//incrémantation/décrémentation préfixe
console.log(++x) //incrémenté puis affiché: 21
console.log(x) //la valeur incrémenté: 21

//incrémantation/décrémentation suffixe
console.log(y++) //affiche la valeur sans incrémenation puis incrémente: 10
console.log(y) //la valeur incrémenté: 11

//décrémentation
x--;
y--;
console.assert(x === 20);
console.assert(y === 10);


/**
 * String: chaine de caractères
 */
console.log(`username: ${username}`);

//taille de la string
console.log(`username.length: ${username.length}`);
console.assert(username.length === 8);
console.assert(username.length === &amp;quot;cheroliv&amp;quot;.length);
console.log(&amp;quot;---------&amp;quot;);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_js&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução JavaScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;arrays&amp;quot;&amp;gt;Os arrays&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;02_arrays.js&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Les arrays
 */
// auteurs [[&amp;quot;nom &amp;quot;, &amp;quot;prénom&amp;quot;, &amp;quot;pays&amp;quot;]]
var writers = [
    [&amp;quot;Karl&amp;quot;, &amp;quot;Marx&amp;quot;, &amp;quot;de&amp;quot;],
    [&amp;quot;Jean-Jacques&amp;quot;, &amp;quot;Rousseau&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Victor&amp;quot;, &amp;quot;Hugo&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;René&amp;quot;, &amp;quot;Descartes&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Paul&amp;quot;, &amp;quot;Verlaine&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Antonio&amp;quot;, &amp;quot;Gramsci&amp;quot;, &amp;quot;it&amp;quot;],
    [&amp;quot;Georg&amp;quot;, &amp;quot;Lukacs&amp;quot;, &amp;quot;hu&amp;quot;],
    [&amp;quot;Franz&amp;quot;, &amp;quot;Kafka&amp;quot;, &amp;quot;hu&amp;quot;],
    [&amp;quot;Arthur&amp;quot;, &amp;quot;Rimbaud&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Gérard&amp;quot;, &amp;quot;de Nerval&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Chrétien&amp;quot;, &amp;quot;de Troyes&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;François&amp;quot;, &amp;quot;Rabelais&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Georg&amp;quot;, &amp;quot;Hegel&amp;quot;, &amp;quot;de&amp;quot;],
    [&amp;quot;Friedrich&amp;quot;, &amp;quot;Engels&amp;quot;, &amp;quot;de&amp;quot;],
]

console.table(writers);

// accés aux élèments du tableau
console.log(writers[0]);
console.log(`${writers[0][0]}, ${writers[0][1]} (${writers[0][2]})`);
console.assert(&amp;quot;Karl, Marx (de)&amp;quot; === `${writers[0][0]}, ${writers[0][1]} (${writers[0][2]})`);

//sauvegarder la valeur du prénom de Marx
var karl = writers[0][0]

//éditer un élement du tableau, le prénom de Marx
writers[0][0] = &amp;quot;Karlito&amp;quot;

console.log(`${writers[0][0]}, ${writers[0][1]} (${writers[0][2]})`);
console.assert(&amp;quot;Karlito, Marx (de)&amp;quot; === `${writers[0][0]}, ${writers[0][1]} (${writers[0][2]})`);

console.assert(&amp;quot;Karl&amp;quot; === karl);

//remettre la valeur initale du prénom de Marx
writers[0][0] = karl;

console.assert(`${karl}, Marx (de)` === `${writers[0][0]}, ${writers[0][1]} (${writers[0][2]})`);

console.log(&amp;quot;---------&amp;quot;);

/**
 *  Arrays: map/forEach/pop/push/slice/sort/shift/unshift
 */

var numbers = [1, 5, 4, 3, 2];

console.log(&amp;quot;Iterate with forEach&amp;quot;);
// parcourir avec array.forEach
numbers.forEach((it) =&amp;amp;gt; console.log(it));
console.log(&amp;quot;---------&amp;quot;);

console.log(&amp;quot;Iterate with map&amp;quot;);
// parcourir avec array.map
numbers.map((it) =&amp;amp;gt; console.log(it));
console.log(&amp;quot;---------&amp;quot;);

//afficher les éléments sur une ligne
//génère la string avec le formatage des nombres
const numbersString = (numberArray) =&amp;amp;gt; {
    var consoleOutput = new String();
    numberArray.forEach(number =&amp;amp;gt; consoleOutput += `${number}, `);
    return consoleOutput = consoleOutput.substring(0, consoleOutput.length - 2);
};

//affiche la chaine entre crochets dans la console
const displayNumbers = (numbersStr) =&amp;amp;gt; {
    console.log(`[${numbersString(numbersStr)}]`);
};

console.log(&amp;quot;orginal number array&amp;quot;)
displayNumbers(numbers);
console.log(&amp;quot;---------&amp;quot;);


//push: ajoute à la fin
console.log(&amp;quot;push&amp;quot;);
numbers.push(6);
displayNumbers(numbers);
console.log(&amp;quot;---------&amp;quot;);

//pop: suprime le dernier
console.log(&amp;quot;pop&amp;quot;);
numbers.pop();
displayNumbers(numbers);
console.log(&amp;quot;---------&amp;quot;);

//unshift: ajouter le parametre au debut de l&amp;#39;array
console.log(&amp;quot;unshift&amp;quot;);
numbers.unshift(0);
displayNumbers(numbers);
console.log(&amp;quot;---------&amp;quot;);

//shift: supprime le premier element de l&amp;#39;array
console.log(&amp;quot;shift&amp;quot;);
numbers.shift();
displayNumbers(numbers);
console.log(&amp;quot;---------&amp;quot;);

//slice: renvoi l&amp;#39;array entre les positions en argument
console.log(&amp;quot;slice&amp;quot;);
var sliceNumbersResult = numbers.slice(2, 4);
displayNumbers(sliceNumbersResult);
console.log(&amp;quot;---------&amp;quot;);

//sort asc
console.log(&amp;quot;sort asc&amp;quot;);
var ascSort = numbers.sort((a, b) =&amp;amp;gt; a - b);
displayNumbers(ascSort);
console.log(&amp;quot;---------&amp;quot;);

//sort desc
console.log(&amp;quot;sort desc&amp;quot;);
var descSort = numbers.sort((a, b) =&amp;amp;gt; b - a);
displayNumbers(descSort);
console.log(&amp;quot;---------&amp;quot;);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_js&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução JavaScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;functions&amp;quot;&amp;gt;As funções&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;03_functions.js&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 *  Les fonctions
 */

//function style legacy
function sayHelloWorldLegacy() {
    console.log(&amp;quot;Hello world legacy!&amp;quot;);
}
sayHelloWorldLegacy();

function sayHelloLegacy(firstName, lastName, style) {
    console.log(`Hello ${firstName}, ${lastName} (${style})`);
}
sayHelloLegacy(&amp;quot;Cher&amp;quot;, &amp;quot;Oliv&amp;quot;, &amp;quot;legacy&amp;quot;);

console.log(&amp;quot;---------&amp;quot;);

//function style arrow
const sayHelloWorld = () =&amp;amp;gt; console.log(&amp;quot;Hello world!&amp;quot;);
sayHelloWorld();

const sayHello = (firstName, lastName) =&amp;amp;gt;
    console.log(`Hello ${firstName}, ${lastName}`);
sayHello(&amp;quot;Cher&amp;quot;, &amp;quot;Oliv&amp;quot;);

console.log(&amp;quot;---------&amp;quot;);

var authors = [
    [&amp;quot;Chrétien&amp;quot;, &amp;quot;de Troyes&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;François&amp;quot;, &amp;quot;Rabelais&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;René&amp;quot;, &amp;quot;Descartes&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Jean-Jacques&amp;quot;, &amp;quot;Rousseau&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Georg&amp;quot;, &amp;quot;Hegel&amp;quot;, &amp;quot;de&amp;quot;],
    [&amp;quot;Karl&amp;quot;, &amp;quot;Marx&amp;quot;, &amp;quot;de&amp;quot;],
    [&amp;quot;Friedrich&amp;quot;, &amp;quot;Engels&amp;quot;, &amp;quot;de&amp;quot;],
    [&amp;quot;Victor&amp;quot;, &amp;quot;Hugo&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Paul&amp;quot;, &amp;quot;Verlaine&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Antonio&amp;quot;, &amp;quot;Gramsci&amp;quot;, &amp;quot;it&amp;quot;],
    [&amp;quot;Georg&amp;quot;, &amp;quot;Lukacs&amp;quot;, &amp;quot;hu&amp;quot;],
    [&amp;quot;Franz&amp;quot;, &amp;quot;Kafka&amp;quot;, &amp;quot;hu&amp;quot;],
    [&amp;quot;Arthur&amp;quot;, &amp;quot;Rimbaud&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Gérard&amp;quot;, &amp;quot;de Nerval&amp;quot;, &amp;quot;fr&amp;quot;],
]


const displayAuthors = (authorsArray) =&amp;amp;gt; authorsArray.forEach(author =&amp;amp;gt;
    console.log(`${author[0]} ${author[1]}, (${author[2]})`)
);

displayAuthors(authors);

console.log(&amp;quot;---------&amp;quot;);

/**
 * valeur par défaut des parametres d&amp;#39;une fonctions
 */
const add = (a = 0, b = 0) =&amp;amp;gt; a + b;
console.assert(add() === 0)
console.assert(add(1) === 1)
console.assert(add(1, 1) === 2)

console.log(&amp;quot;---------&amp;quot;);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_js&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução JavaScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;param_x_scope&amp;quot;&amp;gt;Parâmetros e escopos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;04_param_x_scope.js&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;//global scope: visible partout, dans et hors functions
var global;

const foo = () =&amp;amp;gt; {
    //local scope: visible uniquement dans la fonction foo
    var bar = &amp;quot;bar&amp;quot;
    console.log(bar)
}

foo();
console.log(&amp;quot;---------&amp;quot;);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_js&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução JavaScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;objects&amp;quot;&amp;gt;Os objetos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;05_objetos.js&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Les objects
 */
var authors = [
    [&amp;quot;Chrétien&amp;quot;, &amp;quot;de Troyes&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;François&amp;quot;, &amp;quot;Rabelais&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;René&amp;quot;, &amp;quot;Descartes&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Jean-Jacques&amp;quot;, &amp;quot;Rousseau&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Georg&amp;quot;, &amp;quot;Hegel&amp;quot;, &amp;quot;de&amp;quot;],
    [&amp;quot;Karl&amp;quot;, &amp;quot;Marx&amp;quot;, &amp;quot;de&amp;quot;],
    [&amp;quot;Friedrich&amp;quot;, &amp;quot;Engels&amp;quot;, &amp;quot;de&amp;quot;],
    [&amp;quot;Victor&amp;quot;, &amp;quot;Hugo&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Paul&amp;quot;, &amp;quot;Verlaine&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Antonio&amp;quot;, &amp;quot;Gramsci&amp;quot;, &amp;quot;it&amp;quot;],
    [&amp;quot;Georg&amp;quot;, &amp;quot;Lukacs&amp;quot;, &amp;quot;hu&amp;quot;],
    [&amp;quot;Franz&amp;quot;, &amp;quot;Kafka&amp;quot;, &amp;quot;hu&amp;quot;],
    [&amp;quot;Arthur&amp;quot;, &amp;quot;Rimbaud&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Gérard&amp;quot;, &amp;quot;de Nerval&amp;quot;, &amp;quot;fr&amp;quot;],
];

var hugo = {
    firstName: authors[7][0],
    lastName: authors[7][1],
    country: authors[7][2],
};

var book = {
    author: hugo,
    title: &amp;quot;Les misérables&amp;quot;
};
console.log(hugo);
console.log(book);

//accéder à un membre de l&amp;#39;objet par point
console.log(hugo.country);

//accéder à un membre de l&amp;#39;objet par crochet
console.log(book[&amp;quot;title&amp;quot;]);

console.log(book.author.lastName);

console.log(book[&amp;quot;author&amp;quot;][&amp;quot;firstName&amp;quot;]);

//ajouter une clé a un objet
hugo.gender = &amp;quot;non binary&amp;quot;;
console.assert(hugo.gender === &amp;quot;non binary&amp;quot;);


//mettre à jour une clé
hugo.gender = &amp;quot;male&amp;quot;;
console.assert(hugo.gender !== &amp;quot;non binary&amp;quot;);
console.assert(hugo[&amp;quot;gender&amp;quot;] === &amp;quot;male&amp;quot;);

//supprimer une clé
delete hugo.gender;


//verfier que l&amp;#39;objet ne contient pas la clé gender
console.assert(!Object.keys(hugo).includes(&amp;quot;gender&amp;quot;));


console.log(&amp;quot;---------&amp;quot;);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_js&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução JavaScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;loops&amp;quot;&amp;gt;Os laços&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;06_loops.js&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Les boucles: Itérer sur arrays et objets.
 */

console.log(&amp;quot;for loop over array&amp;quot;);

var numbers = [12, 10, 8, 6, 4, 2, 0];

for (number of numbers) {
    console.log(number);
}

console.log(&amp;quot;---------&amp;quot;);

console.log(&amp;quot;for loop over object key&amp;quot;);

var obj = { a: 1, b: 2, c: 3, d: 4 };

for (key in obj) {
    console.log(key);
}

console.log(&amp;quot;---------&amp;quot;);

console.log(&amp;quot;for loop over object value&amp;quot;);

for (value in obj) {
    console.log(obj[value]);
}

console.log(&amp;quot;---------&amp;quot;);

console.log(&amp;quot;another for loop over authors array&amp;quot;)

var authors = [
    [&amp;quot;Chrétien&amp;quot;, &amp;quot;de Troyes&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;François&amp;quot;, &amp;quot;Rabelais&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;René&amp;quot;, &amp;quot;Descartes&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Jean-Jacques&amp;quot;, &amp;quot;Rousseau&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Georg&amp;quot;, &amp;quot;Hegel&amp;quot;, &amp;quot;de&amp;quot;],
    [&amp;quot;Karl&amp;quot;, &amp;quot;Marx&amp;quot;, &amp;quot;de&amp;quot;],
    [&amp;quot;Friedrich&amp;quot;, &amp;quot;Engels&amp;quot;, &amp;quot;de&amp;quot;],
    [&amp;quot;Victor&amp;quot;, &amp;quot;Hugo&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Paul&amp;quot;, &amp;quot;Verlaine&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Antonio&amp;quot;, &amp;quot;Gramsci&amp;quot;, &amp;quot;it&amp;quot;],
    [&amp;quot;Georg&amp;quot;, &amp;quot;Lukacs&amp;quot;, &amp;quot;hu&amp;quot;],
    [&amp;quot;Franz&amp;quot;, &amp;quot;Kafka&amp;quot;, &amp;quot;hu&amp;quot;],
    [&amp;quot;Arthur&amp;quot;, &amp;quot;Rimbaud&amp;quot;, &amp;quot;fr&amp;quot;],
    [&amp;quot;Gérard&amp;quot;, &amp;quot;de Nerval&amp;quot;, &amp;quot;fr&amp;quot;],
];

for (const [i, value] of authors.entries()) {
    console.log(`${value[0]}, ${value[1]} (${value[2]})`);
}

console.log(&amp;quot;---------&amp;quot;);

console.log(&amp;quot;while loop over array&amp;quot;)

var i = 0
while (i &amp;amp;lt; authors.length) {
    console.log(`${authors[i][0]}, ${authors[i][1]} (${authors[i][2]})`);
    i++;
}

console.log(&amp;quot;---------&amp;quot;);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_js&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução JavaScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;const_var_let&amp;quot;&amp;gt;Const, var e let&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;07_const_var_let.js&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * const X var X let
 */
//var x du if n&amp;#39;est pas confiné au scope du if
const varTest = () =&amp;amp;gt; {
    var x = 1;
    console.log(x);
    console.assert(x === 1);
    if (true) {
        var x = 2;
        console.log(x);
        console.assert(x === 2);
    }
    console.log(x);
    console.assert(x !== 1);
    console.assert(x === 2);
}
varTest();

console.log(&amp;quot;---------&amp;quot;);

//var x est global à tous les fichier js
//chargés dans la page html
//var x issue du fichier 1_start.js
//ligne 46
console.log(x);
console.assert(x === 20);

console.log(&amp;quot;---------&amp;quot;);

//premier let x est confiné au scope de la fonction
//le let x du if est confiné au scope du if
const letTest = () =&amp;amp;gt; {
    let x = 1;
    console.log(x);
    console.assert(x === 1);
    if (true) {
        let x = 2;
        console.log(x);
        console.assert(x === 2);
    }
    console.log(x);
    console.assert(x === 1);
    console.assert(x !== 2);
}
letTest();

console.log(&amp;quot;---------&amp;quot;);

/**
 * const: déclare un emplacement mémoire non réattribuable
 * c&amp;#39;est modifiable pour un array,
 * mais non réinitialisable;
 * ex:
 * const arr = [25, 27, 29]
 * arr = [5, 7] //impossible
 * arr[0]=20 // possible
 * ex:
 * const n = 1;
 * n = 3; //impossible
 */
const arr = [25, 27, 29]
console.table(arr);
arr[0] = 20 // possible
console.table(arr);

arr.pop()
console.table(arr);
console.log(&amp;quot;---------&amp;quot;);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_js&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução JavaScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;rest_and_spread_operators&amp;quot;&amp;gt;Operadores rest e spread&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;08_rest_and_spread_operators.js&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Opérateur de rest:
 * const add = (...operandes) =&amp;amp;gt; a + b;
 * ou avec parametres avec valeur par défaut
 * const add = (a=0, b=0, ...operandes) =&amp;amp;gt; a + b;
 */

/**
 * spread opérateur:
 * récupère l&amp;#39;ensemble des elements d&amp;#39;une collection,
 * dans un array
 */
const user_ages = [25, 56, 12, 58, 41, 62, 26];

const max_user_age = Math.max(...user_ages);
const min_user_age = Math.min(...user_ages);

console.assert(max_user_age == 62);
console.assert(min_user_age == 12);

console.log(&amp;quot;---------&amp;quot;);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_js&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução JavaScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;destructuring&amp;quot;&amp;gt;Desestruturação&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;09_destructuring.js&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * L&amp;#39;affectation par décomposition (destructuring)
 */

const user = {
    first_name: &amp;quot;Oliv&amp;quot;,
    last_name: &amp;quot;Cher&amp;quot;,
    nick_name: &amp;quot;cheroliv&amp;quot;,
    gender: &amp;quot;male&amp;quot;,
    is_adult: true
};
console.log(user);

//première façon d&amp;#39;initaliser des variables sans destructuration
let first_name_legacy = user.first_name;
let last_name_legacy = user.last_name;
let nick_name_legacy = user.nick_name;
let gender_legacy = user.gender;
let is_adult_legacy = user.is_adult;

console.assert(&amp;quot;Oliv&amp;quot; === first_name_legacy);
console.assert(&amp;quot;Cher&amp;quot; === last_name_legacy);
console.assert(&amp;quot;cheroliv&amp;quot; === nick_name_legacy);
console.assert(&amp;quot;male&amp;quot; === gender_legacy);
console.assert(true === is_adult_legacy);
console.log(&amp;quot;---------&amp;quot;);
/**
 * Initialisation par déstructuration
 */
const {
    first_name,
    last_name,
    nick_name,
    gender,
    is_adult
} = user;

console.assert(user.first_name === first_name);
console.assert(user.last_name === last_name);
console.assert(user.nick_name === nick_name);
console.assert(user.gender === gender);
console.assert(user.is_adult === is_adult);

/**
 * Intialisation par déstructuration des arrays
 */
//cible explicite
const [a1, a2] = [15, 25, 17, 81, 51, 46];
console.assert(a1 === 15);
console.assert(a2 === 25);

//cible avec position par rapport aux index par virgules
//je veux b3 à 46
const [b1, b2, , , , b3] = [15, 25, 17, 81, 51, 46];
console.assert(b1 === 15);
console.assert(b2 === 25);
console.assert(b3 === 46);

//déstructuration avec operateur de rest
// je contruit un autre avec avec les elements
// à partir du premier élèment non destructuré
const [c1, c2, ...sub_arr] = [15, 25, 17, 81, 51, 46];
console.assert(b1 === 15);
console.assert(b2 === 25);
console.assert(sub_arr.length === 4);
console.assert(sub_arr[0] === 17);
console.assert(sub_arr[1] === 81);
console.assert(sub_arr[2] === 51);
console.assert(sub_arr[3] === 46);


// Une boucle pour comparer le resultat attendu
for (const [i, value] of[15, 25, 17, 81, 51, 46].entries()) {
    //on reconstruit le tableau à chaque iteration
    console.assert([c1, c2].concat(sub_arr)[i] === value);
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_js&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução JavaScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;import_export&amp;quot;&amp;gt;Importação e exportação de módulos&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;10_module_import_export.js&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Import et export de module
 *
 * Dans le fichier html
 * au niveau de la balise d&amp;#39;import de script
 * spécifier le type module
 *
 * 1er technique: import nommé des fonctions:
 * import { sum, minus, times, div, rem } from &amp;quot;./dummy_math_functions.js&amp;quot;;
 *
 * 2eme technique: import global des fonction avec l&amp;#39;asterisque(*)
 * en préfixant l&amp;#39;import par un nommage du fichier importé:
 * import * as math from &amp;quot;./dummy_math_functions.js&amp;quot;;
 *
 * 3eme technique: import par alias avec le mot clé &amp;quot;as&amp;quot;
 * import {
 *     sum as add,
 *     minus as substract,
 *     times as multiply,
 *     div as divide,
 *     rem as modulo
 * } from &amp;quot;./dummy_math_functions.js&amp;quot;;
 */

import {
    sum,
    minus,
    times,
    div,
    rem
} from &amp;quot;./dummy_math_functions.js&amp;quot;;

console.assert(sum(1, 1) === 2);
console.assert(minus(4, 2) === 2);
console.assert(times(2, 2) === 4);
console.assert(div(4, 2) === 2);
console.assert(rem(4, 2) === 0);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;dummy_math_functions.js&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;//sum
const sum = (x, y) =&amp;amp;gt; x + y;

// subtraction
const minus = (x, y) =&amp;amp;gt; x - y;

// multiplication
const times = (x, y) =&amp;amp;gt; x * y;

// division
const div = (x, y) =&amp;amp;gt; x / y;

// remainder: reste de la division euclidiene
const rem = (x, y) =&amp;amp;gt; x % y;

// log sum
const sum_log = (x, y) =&amp;amp;gt; console.log(sum(x, y));

// log subtraction
const minus_log = (x, y) =&amp;amp;gt; console.log(minus(x, y));

// log multiplication
const times_log = (x, y) =&amp;amp;gt; console.log(times(x, y));

// log division
const div_log = (x, y) =&amp;amp;gt; console.log(div(x, y));

// log remainder
const rem_log = (x, y) =&amp;amp;gt; console.log(rem(x, y));

export {
    sum,
    minus,
    times,
    div,
    rem,
    sum_log,
    minus_log,
    times_log,
    div_log,
    rem_log
};&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_js&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução JavaScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;classes&amp;quot;&amp;gt;Utilização das classes&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;11_classes.js&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-javascript&amp;quot; data-lang=&amp;quot;javascript&amp;quot;&amp;gt;/**
 * Utilisation des classes
 */
//creation d&amp;#39;un objet
class Account {
    constructor(username) {
        this.username = username;
    }
};

const acc1 = new Account(&amp;quot;cheroliv&amp;quot;);
console.table(acc1);
console.assert(acc1.username === &amp;quot;cheroliv&amp;quot;);

console.log(&amp;quot;---------&amp;quot;);

//création d&amp;#39;un objet et ajout de getter et setter(accesseurs)
class AccountInfo {
    constructor(username) {
        this._username = username;
    }

    get username() {
        return this._username;
    }

    /**
     * @param {(arg0: string) =&amp;amp;gt; void} new_username
     */
    set new_username(new_username) {
        this._username = new_username;
    }
};

const acc_info1 = new AccountInfo(&amp;quot;cheroliv&amp;quot;);
console.table(acc_info1);
//accés au membre _username avec le getter username()
console.assert(acc_info1.username === &amp;quot;cheroliv&amp;quot;);

//update la valeur username de acc_info1 avec le setter
acc_info1.new_username = &amp;quot;imrandeh&amp;quot;;
console.assert(acc_info1.username === &amp;quot;imrandeh&amp;quot;);

console.log(&amp;quot;---------&amp;quot;);&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc_js&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;underline&amp;quot;&amp;gt;Introdução JavaScript&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Memorando de Escopo</title>
            <link >https://cheroliv.com//blog/2022/0054_memo_cadrage_post.html</link>
            <pubDate>Sat, 31 Dec 2022 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2022/0054_memo_cadrage_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_enquadramento&amp;quot;&amp;gt;enquadramento&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_oficina_1_visão&amp;quot;&amp;gt;Oficina 1 : Visão&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_1_o_padrão_qqoqcp_quem_o_que_onde_quando_como_por_que&amp;quot;&amp;gt;1. O padrão “QQOQCP” – Quem, O que, Onde, Quando, Como, Por que&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_2_o_product_vision_board&amp;quot;&amp;gt;2. O Product Vision Board&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;sect4&amp;quot;&amp;gt;
&amp;lt;h5 id=&amp;quot;_grupo_alvo&amp;quot;&amp;gt;Grupo-alvo&amp;lt;/h5&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quem são os utilizadores que utilizam o produto ou a oferta? (direto, indireto)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect4&amp;quot;&amp;gt;
&amp;lt;h5 id=&amp;quot;_necessidades&amp;quot;&amp;gt;Necessidades&amp;lt;/h5&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quais são as 5 necessidades principais dos usuários chave da aplicação?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect4&amp;quot;&amp;gt;
&amp;lt;h5 id=&amp;quot;_produto&amp;quot;&amp;gt;Produto&amp;lt;/h5&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Qual é a minha proposta de valor? Quais são as 5 principais funcionalidades do produto que permitem atender ao necessidades&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect4&amp;quot;&amp;gt;
&amp;lt;h5 id=&amp;quot;_objetivo_de_negócio&amp;quot;&amp;gt;Objetivo de Negócio&amp;lt;/h5&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quais são os critérios de sucesso ?&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect4&amp;quot;&amp;gt;
&amp;lt;h5 id=&amp;quot;_visão&amp;quot;&amp;gt;Visão&amp;lt;/h5&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Quando as 4 colunas estiverem completas, você precisará preencher a parte da visão do Product Vision Board em uma frase completa.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_oficina_2_personaspercursos_do_utilizador&amp;quot;&amp;gt;Oficina 2: Personas/Percursos do utilizador&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Definir para cada persona um percurso do usuário:&amp;lt;/div&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Quem vai usar meu serviço? (Alvo)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;O que ele quer fazer? (But)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Como ele vai fazer? (Caminho do Usuário)&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_oficina_3_zoneamento_storyboard_sitemap&amp;quot;&amp;gt;Oficina 3: Zoneamento, storyboard, sitemap&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_1_zoneamento&amp;quot;&amp;gt;1. Zoneamento&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_2_storyboard&amp;quot;&amp;gt;2. storyboard&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_3_mapa_do_site&amp;quot;&amp;gt;3. Mapa do Site&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_oficina_4_wireframemoodboard&amp;quot;&amp;gt;Oficina 4: Wireframe/Moodboard&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_1_wireframes&amp;quot;&amp;gt;1. Wireframes&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_2_moodboard&amp;quot;&amp;gt;2. Moodboard&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_oficina_5_mapeamento_de_histórias_histórias_de_usuário&amp;quot;&amp;gt;Oficina 5: Mapeamento de Histórias/ Histórias de Usuário&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_story_mapping_priorização_das_us&amp;quot;&amp;gt;Story mapping (priorização das US)&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_como_tipo_de_utilizador_eu_quero_um_objetivo_para_que_uma_razão&amp;quot;&amp;gt;Como &amp;amp;lt;tipo de utilizador&amp;amp;gt;, eu quero &amp;amp;lt;um objetivo&amp;amp;gt; para que &amp;amp;lt;uma razão&amp;amp;gt;.&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_oficina_6_backlog&amp;quot;&amp;gt;Oficina 6: Backlog&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://www.atlassian.com/fr/agile/scrum/backlogs&amp;quot;&amp;gt;acúmulos&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_oficina_7_técnica_e_arquitetura&amp;quot;&amp;gt;Oficina 7: Técnica e Arquitetura&amp;lt;/h3&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Memo Kotlin</title>
            <link >https://cheroliv.com//blog/2022/0053_memo_kotlin_post.html</link>
            <pubDate>Sun, 29 May 2022 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2022/0053_memo_kotlin_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_links_bons&amp;quot;&amp;gt;Links bons&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://play.kotlinlang.org/&amp;quot;&amp;gt;kotlinlang parque de brinquedos&amp;lt;/a&amp;gt;+&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://arrow-kt.io/&amp;quot;&amp;gt;arrow.kt&amp;lt;/a&amp;gt;+&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://arkivanov.github.io/MVIKotlin/&amp;quot;&amp;gt;MVI:Modelo Visão Intenção&amp;lt;/a&amp;gt;+&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/arkivanov/Essenty&amp;quot;&amp;gt;Essenty&amp;lt;/a&amp;gt;+&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://arkivanov.github.io/Decompose/&amp;quot;&amp;gt;Decompõe&amp;lt;/a&amp;gt;+&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_trecho_de_código_variado&amp;quot;&amp;gt;Trecho de código variado&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_map_reduce&amp;quot;&amp;gt;map reduce&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Converter uma lista de bite para uma lista de string e resumir a lista de string&amp;lt;br&amp;gt;
em uma string contendo a concatenação. Como registrar a requisição enviada(requestBodyContent:byte[])&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;log.info(
    requestBodyContent!!.map { it.toInt().toChar().toString() }
        .reduce { request: String, s: String -&amp;amp;gt; request + s }
)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_enum_e_classes_seladas&amp;quot;&amp;gt;enum e classes seladas&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;post:&amp;lt;a href=&amp;quot;0038_training_kotlin_enum_sealed_class_post&amp;quot; target=&amp;quot;&amp;quot;_blank&amp;quot;&amp;gt;training kotlin enum sealed class&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_interface_funcional_e_referência_de_método&amp;quot;&amp;gt;interface funcional e referência de método&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;É possível transformar uma entidade em um modelo de DTO utilizando uma referência de método (estilo funcional - Java 8)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;fun findAllByLoginNot(
        pageable:Pageable,
        login:String)
    :Page&amp;amp;lt;UserDto&amp;amp;gt; {
    return userDao.findAllByLoginNot(
                    pageable,
                    login).map(::fromEntity)
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;umahttps://stackoverflow.com/a/22245383/837404[boa explicação]&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_capturar_a_saída_padrão&amp;quot;&amp;gt;Capturar a saída padrão&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;package functional

import java.io.ByteArrayOutputStream
import java.io.PrintStream
import java.lang.System.out
import java.lang.System.setOut
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals


class BasicsHOF {
    private val standardOut: PrintStream? = out
    private val outputStreamCaptor = ByteArrayOutputStream()

    @BeforeTest
    fun setUp() = setOut(PrintStream(outputStreamCaptor))

    @AfterTest
    fun tearDown() = setOut(standardOut)

    @Test
    fun `three times dope`() {

        3.times { println(&amp;quot;Hello&amp;quot;) }

        assertEquals(
            buildString {
                repeat(3) { append(&amp;quot;Hello\n&amp;quot;) }
                deleteAt(length - 1)
            }, outputStreamCaptor
                .toString()
                .trim()
        )
    }

    fun Int.times(fn: () -&amp;amp;gt; Unit) = (1..this).forEach { _ -&amp;amp;gt; fn() }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_qual_é_a_diferença_em_kotlin_entre_apply_run_let_also_use_e_with&amp;quot;&amp;gt;Qual é a diferença em kotlin entre apply, run, let, also, use e with ?&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Comparação das funções Kotlin e Utilização das Formas Lambda Referência&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_aplicar&amp;quot;&amp;gt;aplicar&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Introdução As funções Kotlin`apply`, &amp;lt;code&amp;gt;run&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;let&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;also&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;use&amp;lt;/code&amp;gt;, et `with`oferecem diferentes maneiras de tratar os objetos. Cada uma tem seus próprios casos de uso e comportamentos. Além disso, as formas lambda reference permitem deixar o código mais legível e reutilizável ao fazer referência a funções lambda existentes.&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;A função`apply`é usada para configurar um objeto durante a sua criação. Ela retorna o objeto no qual ela é chamada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo com referência lambda :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val someObject = SomeClass().apply(::configureObject)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo com lambda :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val someObject = SomeClass().apply {
// configuration des propriétés de someObject
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_run&amp;quot;&amp;gt;run&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A função`run`é utilizada para executar um bloco de código em um objeto e retorna o resultado do bloco de código.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo com referência lambda :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val result = someObject.run(::someFunction)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo com lambda :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val result = someObject.run {
// bloc de code à exécuter sur someObject
// la dernière expression est renvoyée
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_deixe&amp;quot;&amp;gt;deixe&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A função`let`é usada para executar um bloco de código em um objeto e retorna o resultado do bloco de código.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo com lambda referência :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val result = someObject.let(::processObject)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo com lambda :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val result = someObject.let {
// bloc de code à exécuter sur someObject
// la dernière expression est renvoyée
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_também&amp;quot;&amp;gt;também&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A função`also`é usada para executar uma ação adicional em um objeto e retorna o objeto no qual ela é chamada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo com referência lambda :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;someObject.also(::performAdditionalAction)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo com lambda :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;someObject.also {
// action additionnelle sur someObject
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_use&amp;quot;&amp;gt;use&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A função`use`É utilizada para trabalhar com recursos que devem ser fechados após o uso. Ela chama automaticamente a função`close`no final do bloco.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo com referência de lambda :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;someResource.use(::useResource)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo com lambda:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;someResource.use {
// travailler avec la ressource
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_com&amp;quot;&amp;gt;com&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A função`with`É usada para chamar vários métodos em um objeto sem repetir seu nome e retorna o resultado da última expressão.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo com referência lambda :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val result = with(someObject, ::processWithObject)&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Exemplo com lambda:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;val result = with(someObject) {
// appeler des méthodes sur someObject
// la dernière expression est renvoyée
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Usando as formas lambda de referência ou os blocos`{}`, você pode encapsular a lógica em funções separadas, melhorando assim a legibilidade e a reutilização do código.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_função_em_kotlin&amp;quot;&amp;gt;Função em Kotlin&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;O let retorna o objeto com os efeitos colaterais aplicados a ele ou no estado inicial de entrada, dependendo (let) ?&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;A função`let`em Kotlin é usada para realizar operações em um objeto e retornar um resultado diferente. No entanto, é importante observar que a função`let`não modifica o estado inicial do objeto no qual ela é chamada.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Assinatura&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;code&amp;gt;inline fun &amp;amp;lt;T, R&amp;amp;gt; T.let(block: (T) &amp;amp;#8594; R): R&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;uso&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;A função`let`é comumente utilizada para aplicar transformações a um objeto e obter um resultado baseado nessas transformações.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;resultado&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;O valor retornado pela função`let`é o resultado da expressão lambda passada como argumento, geralmente o resultado das operações realizadas sobre o objeto.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 50%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Efeitos colaterais&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Embora a função`let`pode ter efeitos colaterais no objeto quando é usada na expressão lambda, ela não modifica o estado inicial do próprio objeto.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Assim, a função`let`É uma forma elegante de realizar operações em um objeto enquanto se obtém um resultado derivado, preservando também a integridade do objeto original.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Memo JVM</title>
            <link >https://cheroliv.com//blog/2022/0052_memo_jvm_post.html</link>
            <pubDate>Sat, 28 May 2022 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2022/0052_memo_jvm_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;toc&amp;quot;&amp;gt;sumário&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;olist arabic&amp;quot;&amp;gt;
&amp;lt;ol class=&amp;quot;arabic&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#char&amp;quot;&amp;gt;cadeias de caracteres e caracteres&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#math&amp;quot;&amp;gt;números e matemática&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#date&amp;quot;&amp;gt;datas e horas&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#java8date&amp;quot;&amp;gt;datas e horas java8&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#arrCol&amp;quot;&amp;gt;tabelas e coleções&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#sysProp&amp;quot;&amp;gt;System.Properties&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#threads&amp;quot;&amp;gt;Fios&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#regex&amp;quot;&amp;gt;Expressões Regulares&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;char&amp;quot;&amp;gt;cadeias de caracteres e caracteres&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;import java.text.Collator
import java.util.*
import kotlin.test.*

class StringsTest {
    @Test
    fun `chaines de caractères et caractères`() {
        //inférence de type
        val s = &amp;quot;C&amp;#39;est&amp;quot;
        assertEquals(&amp;quot;C&amp;#39;est&amp;quot;, s)

        //concaténation
        val t: String = s + &amp;quot; le moment.&amp;quot;
        assertEquals(&amp;quot;C&amp;#39;est le moment.&amp;quot;, t)

        // StringBuilder
        val t1: String = s + buildString { append(&amp;quot; le moment.&amp;quot;) }
        assertEquals(&amp;quot;C&amp;#39;est le moment.&amp;quot;, t1)

        //interpolation
        val t2 = &amp;quot;$s le moment.&amp;quot;
        assertEquals(&amp;quot;C&amp;#39;est le moment.&amp;quot;, t2)

        //multiligne chaine de caractères
        val t3 = &amp;quot;&amp;quot;&amp;quot;$s le moment.&amp;quot;&amp;quot;&amp;quot;
        assertEquals(&amp;quot;C&amp;#39;est le moment.&amp;quot;, t3)

        //conversion de type
        val t4 = t + &amp;quot; &amp;quot; + 23.4
        assertEquals(&amp;quot;C&amp;#39;est le moment. 23.4&amp;quot;, t4)

        val t5 = &amp;#39;C&amp;#39;.toString()
        assertEquals(&amp;quot;C&amp;quot;, t5)

        //taille de la chaine de caractères
        assertEquals(16, t.length)

        //sous-chaine d&amp;#39;une chaine de caractères

        //retourne une chaine de caractères contenant,
        //les caractères aux positions x à y-1
        //sub = t.substring(x, y)

        //t = &amp;quot;C&amp;#39;est le moment.&amp;quot;
        //retourne les caractères 6 et 7
        var sub = t.substring(6, 8)
        assertEquals(&amp;quot;le&amp;quot;, sub)

        //retourne les caractères 0 à 4
        sub = t.substring(0, 5)
        assertEquals(&amp;quot;C&amp;#39;est&amp;quot;, sub)

        //la longueur d&amp;#39;une sous-chaine est toujours égale (y-x)
        val numChars = sub.length
        assertEquals(5 - 0, numChars)

        //extraction des caractères d&amp;#39;une chaine
        assertEquals(&amp;#39;e&amp;#39;, t.elementAt(2))
        assertEquals(&amp;#39;e&amp;#39;, t.get(2))
        assertEquals(&amp;#39;e&amp;#39;, t[2])

        //conversion d&amp;#39;une chaine en tableau de caractères
        val ca = t.toCharArray()
        assertEquals(t.length, ca.size)
        t.mapIndexed { index, char -&amp;amp;gt; assertEquals(char, ca[index]) }

        //place les 4 premiers caractères de t1
        //dans le tableau ca a la position 2
        (t as java.lang.String).getChars(
            /* srcBegin = */ 0,
            /* srcEnd = */ 3,
            /* dst = */ ca,
            /* dstBegin = */ 1
        )
        assertEquals(&amp;quot;CC&amp;#39;et le moment.&amp;quot;, String(ca))
        //colle tous les caractères dans un meme string
        assertEquals(&amp;quot;CC&amp;#39;et le moment.&amp;quot;, ca.concatToString())

        //to lower case
        assertEquals(&amp;quot;c&amp;#39;est le moment.&amp;quot;, t.toLowerCase())
        assertEquals(&amp;quot;c&amp;#39;est le moment.&amp;quot;, t.lowercase())

        //to upper case
        assertEquals(&amp;quot;C&amp;#39;EST LE MOMENT.&amp;quot;, t.toUpperCase())
        assertEquals(&amp;quot;C&amp;#39;EST LE MOMENT.&amp;quot;, t.uppercase())

        //comparaison de chaines de caractères
        //t = &amp;quot;C&amp;#39;est le moment.&amp;quot;
        assertFalse(t.equals(&amp;quot;hello&amp;quot;))
        assertFalse(t == &amp;quot;hello&amp;quot;)

        //ignore la casse
        assertTrue(t.equalsIgnoreCase(&amp;quot;C&amp;#39;EST LE MOMENT.&amp;quot;))
        assertTrue(t.equals(&amp;quot;C&amp;#39;EST LE MOMENT.&amp;quot;, ignoreCase = true))

        //démarre par
        assertTrue(t.startsWith(&amp;quot;C&amp;#39;est&amp;quot;))
        //se finit par
        assertTrue(t.endsWith(&amp;quot;le moment.&amp;quot;))

        //compareTo
        //retourne une valeur &amp;amp;lt; 0, car s est
        //alphabétiquement avant &amp;quot;N&amp;#39;est&amp;quot;
        val r1: Int = s.compareTo(&amp;quot;N&amp;#39;est&amp;quot;)
        assertTrue(r1 &amp;amp;lt; 0)

        //variante ignorant la casse
        val r1Prime: Int = (s as java.lang.String).compareToIgnoreCase(&amp;quot;n&amp;#39;est&amp;quot;)
        assertTrue(r1Prime &amp;amp;lt; 0)

        //retourne 0 si les chaines sont equivalente
        val r2: Int = s.compareTo(&amp;quot;C&amp;#39;est&amp;quot;)
        assertEquals(0, r2)

        //retourne une valeur &amp;amp;gt; 0 car s vient apres &amp;quot;B&amp;#39;est&amp;quot;
        val r3: Int = s.compareTo(&amp;quot;B&amp;#39;est&amp;quot;)
        assertTrue(r3 &amp;amp;gt; 0)

        //Recherche de caractères et de sous-chaines de caractères

        //recherche de caractères
        //position du premier caractères &amp;#39;t&amp;#39;
        var pos = t.indexOf(&amp;#39;t&amp;#39;)
        assertEquals(4, pos)

        //position du suivant
        pos = t.indexOf(&amp;#39;t&amp;#39;, pos + 1)
        assertEquals(14, pos)

        //retour d&amp;#39;érreur -1 si absence de suivant
        pos = t.indexOf(&amp;#39;t&amp;#39;, pos + 1)
        assertEquals(-1, pos)

        //position du dernier &amp;#39;t&amp;#39; dans la chaine: 14
        pos = t.lastIndexOf(&amp;#39;t&amp;#39;)
        assertEquals(14, pos)

        //recherche de &amp;#39;t&amp;#39; vers l&amp;#39;arrière a partir du caractères 13
        pos = t.lastIndexOf(&amp;#39;t&amp;#39;, pos - 1)
        assertEquals(4, pos)

        //recherche de sous-chaines
        //retourne 2
        pos = t.indexOf(&amp;quot;est&amp;quot;)
        assertEquals(2, pos)

        //&amp;quot;est&amp;quot; n&amp;#39;apparait qu&amp;#39;une seule fois: retourne -1
        pos = t.indexOf(&amp;quot;est&amp;quot;, pos + 1)
        assertEquals(-1, pos)

        //recherche d&amp;#39;une sous-chaine depuis l&amp;#39;arrière
        //t = &amp;quot;C&amp;#39;est le moment.&amp;quot;
        //retourne 6
        pos = t.lastIndexOf(&amp;quot;le &amp;quot;)
        assertEquals(6, pos)

        //extrait depuis la position 9,
        //renvoi toute la chaine après &amp;quot;le &amp;quot;
        val noun = t.substring(pos + 3)
        assertEquals(-1, noun.indexOf(&amp;quot;le &amp;quot;))

        //remplacement de toutes les instances d&amp;#39;un caractère
        //par un autre caractère
        //ne fonctionne que avec les caractères, pas les chaines
        val exclaim: String = t.replace(&amp;#39;.&amp;#39;, &amp;#39;!&amp;#39;)
        assertEquals(&amp;#39;!&amp;#39;, exclaim.get(exclaim.length - 1))
        assertEquals(exclaim.length - 1, exclaim.indexOf(&amp;#39;!&amp;#39;))
        assertEquals(-1, exclaim.indexOf(&amp;#39;.&amp;#39;))

        //suppression des espaces blancs
        //au début et à la fin d&amp;#39;une chaine
        val noextraspaces = t.trim()
        assertNotEquals(&amp;#39; &amp;#39;, noextraspaces.get(0))
        assertNotEquals(&amp;#39; &amp;#39;, noextraspaces.get(noextraspaces.length - 1))

        //extraction des instances uniques de chaines de caractères
        //avec intern()
        val s1 = s.intern()
        assertEquals(s, s1)
        val s2 = &amp;quot;C&amp;#39;est&amp;quot;.intern()
        assertEquals(&amp;quot;C&amp;#39;est&amp;quot;, s2)
        assertEquals(s1, s2)

        //StringBuilder pour manipuler les caractères d&amp;#39;une chaine de caractères
        //crée un tampon StringBuilder à partir d&amp;#39;une chaine de caractères
        val b = StringBuilder(&amp;quot;N&amp;#39;est&amp;quot;)

        //extrait et définit des caractères individuel du tampon StringBuilder
        //le caractères à l&amp;#39;index 0
        val c: Char = b.get(0)
        assertEquals(&amp;#39;N&amp;#39;, c)

        //modifier le premier caractère de la chaine
        b.setCharAt(0, &amp;#39;C&amp;#39;)
        assertEquals(s, b.toString())

        //ajouter des données à un StringBuilder
        b.append(&amp;#39; &amp;#39;)
        b.append(&amp;quot;le moment.&amp;quot;)
        b.append(23)

        //insère des chaines de caractères ou autre dans le StringBuilder
        b.insert(6, &amp;quot;pas &amp;quot;)
        assertEquals(&amp;quot;C&amp;#39;est pas le moment.23&amp;quot;, b.toString())

        //remplace un sous ensemble de caractères
        //avec une chaine de caractères donnée
        b.replace(2, 9, &amp;quot;est&amp;quot;)
        assertEquals(&amp;quot;C&amp;#39;est le moment.23&amp;quot;, b.toString())

        //supprime les caractères
        b.delete(15, 18)
        assertEquals(&amp;quot;C&amp;#39;est le moment&amp;quot;, b.toString())
        b.deleteCharAt(2)
        assertEquals(&amp;quot;C&amp;#39;st le moment&amp;quot;, b.toString())

        //insert à la postion 2 et décale reste à droite(sans perte de données)
        b.insert(2, &amp;#39;e&amp;#39;)

        //tronque la taille de la donnée
        b.setLength(5)
        assertEquals(&amp;quot;C&amp;#39;est&amp;quot;, b.toString())

        //inverse les caractères de la chaine
        b.reverse()
        assertEquals(&amp;quot;tse&amp;#39;C&amp;quot;, b.toString())

        //écrase le StringBuilder, pret à etre réutilisé
        b.setLength(0)
        assertEquals(&amp;quot;&amp;quot;, b.toString())

        //java.util.StringTokenizer pour fragmenter une chaine
        //de caractères en un ensemble de mots
        var st = StringTokenizer(t)
        //nb d&amp;#39;items encore présentent dans la file
        assertEquals(3, st.countTokens())

        //est ce que il y a encore des items dans la file
        assertTrue(st.hasMoreTokens())

        //récupérer le token courrant
        assertEquals(&amp;quot;C&amp;#39;est&amp;quot;, st.nextToken())
        assertEquals(&amp;quot;le&amp;quot;, st.nextToken())
        assertEquals(&amp;quot;moment.&amp;quot;, st.nextToken())
        assertFalse(st.hasMoreTokens())
        assertEquals(0, st.countTokens())

        //extraire des occurences de mots délimités
        //par des caractères autres que des expaces.
        val str = &amp;quot;a:b:c:d&amp;quot;
        st = StringTokenizer(str, &amp;quot;:&amp;quot;)
        assertEquals(4, st.countTokens())
        assertTrue(st.hasMoreTokens())
        assertEquals(&amp;quot;a&amp;quot;, st.nextToken())
        assertEquals(&amp;quot;b&amp;quot;, st.nextToken())
        assertEquals(&amp;quot;c&amp;quot;, st.nextToken())
        assertEquals(&amp;quot;d&amp;quot;, st.nextToken())
        assertFalse(st.hasMoreTokens())
        assertEquals(0, st.countTokens())


        //text=&amp;quot;C&amp;#39;est le moment.&amp;quot;
        val text = t.toCharArray()
        var p = 0

        //sauter les espaces de tete
        //pour ramener p à la position du premier caractère imprimable
        while ((p &amp;amp;lt; text.size) &amp;amp;amp;&amp;amp;amp;
            (Character.isWhitespace(text[p]))
        ) p++
        assertEquals(0, p)
        assertEquals(&amp;quot;C&amp;#39;est le moment.&amp;quot;, text.concatToString())

        //met le premier mot du texte en majuscule
        while (p &amp;amp;lt; text.size &amp;amp;amp;&amp;amp;amp; Character.isLetter(text[p])) {
            text[p] = Character.toUpperCase(text[p])
            p++
        }
        assertEquals(1, p)
        assertEquals(&amp;#39;C&amp;#39;, text[0])
        assertTrue(Character.isUpperCase(text[0]))
        assertFalse(Character.isLetter(text[1]))

        //comparer des chaines de caractères
        // avec les contrainte la locale système
        val col = Collator.getInstance()
        //le résulat est négatif car chica
        //est avant chico dans l&amp;#39;ordre alphabétique
        assertTrue(col.compare(&amp;quot;chica&amp;quot;, &amp;quot;chico&amp;quot;) &amp;amp;lt; 0)
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc&amp;quot;&amp;gt;sumário&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;math&amp;quot;&amp;gt;números e matemática&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;import java.math.BigInteger
import java.security.SecureRandom
import java.text.NumberFormat
import java.util.*
import kotlin.test.*

class NumbersMathTest {
    @Test
    fun `Nombres et Math`() {
        //Constantes utiles
        Byte.MIN_VALUE
        Byte.MAX_VALUE
        Short.MIN_VALUE
        Short.MAX_VALUE
        Float.MIN_VALUE
        Float.MAX_VALUE
        Math.PI
        Math.E
        val s = &amp;quot;-42&amp;quot;
        //conversion de chaine de caractères
        //vers un nombre, si possible.
        var b: Byte = java.lang.Byte.parseByte(s)
        var sh: Short = java.lang.Short.parseShort(s)
        var i: Int = java.lang.Integer.parseInt(s)
        var l: Long = java.lang.Long.parseLong(s)
        var f: Float = java.lang.Float.parseFloat(s)
        var d: Double = java.lang.Double.parseDouble(s)

        //valeur exacte
        val f_exac = java.lang.Float.valueOf(s)
        val d_exac = java.lang.Double.valueOf(s)

        //les routines de conversions entière gérent
        //les nombres dans diverses bases.
        //1011 en binare est égal a 11 en base dix
        b = java.lang.Byte.parseByte(&amp;quot;1011&amp;quot;, 2)
        assertEquals(11, b)
        //ff en base 16(hexa) est égal à 255 en base dix.
        sh = java.lang.Short.parseShort(&amp;quot;ff&amp;quot;, 16)
        assertEquals(255, sh)

        //la méthode valueOf() peut gérer des bases arbitraires.
        i = java.lang.Integer.valueOf(&amp;quot;egg&amp;quot;, 17).toInt()
        assertEquals(4334, i)

        //la méthode decode() gére les representations octale,
        //décimal, hexadécimal, en fonction du préfixe numérique
        //de la chaine de caractères
        //un 0 de tete signifie base 8
        //un 0x de tete signifie base 16
        //les autres sont en base 10
        val sho = java.lang.Short.decode(&amp;quot;0377&amp;quot;)

        //la classe Integer peut convertir les nombres
        //en diverses chaines de caractères.
        val decimal = java.lang.Integer.toString(42)
        assertEquals(&amp;quot;42&amp;quot;, decimal)

        val decimal_ = 42.toString()
        assertEquals(&amp;quot;42&amp;quot;, decimal_)

        val binary = java.lang.Integer.toBinaryString(42)
        assertEquals(&amp;quot;101010&amp;quot;, binary)

        val octal = java.lang.Integer.toOctalString(42)
        assertEquals(&amp;quot;52&amp;quot;, octal)

        val hex = java.lang.Integer.toHexString(42)
        assertEquals(&amp;quot;2a&amp;quot;, hex)

        val base36 = java.lang.Integer.toString(42, 36)
        assertEquals(&amp;quot;16&amp;quot;, base36)

        val base36_ = 42.toString(36)
        assertEquals(&amp;quot;16&amp;quot;, base36_)

        //java.text.NumberFormat effectue la conversion
        // d&amp;#39;une maniere spécifique aux parametres locaux
        //sans parametre prend la local systeme comme reference
        val nf = NumberFormat.getNumberInstance(Locale.FRANCE)
        val formatted_number = nf.format(9876543.21)
        assertNotEquals(&amp;quot;9876543.21&amp;quot;, formatted_number)

        //parse la chaine de caractères en fonction des parametres locaux(fr)
        val n = nf.parse(&amp;quot;1234567,89&amp;quot;)
        assertEquals(1234567.89, n)

        //les valeurs monétaires sont parfois formaté
        // d&amp;#39;une maniere differente des nombres
        val money_format = NumberFormat.getCurrencyInstance(Locale.FRANCE)
        assertEquals(&amp;quot;123,40 €&amp;quot;, money_format.format(1234.56))

        //java.lang.Math
        d = Math.toRadians(27.0)
        d = Math.cos(d)
        d = Math.sqrt(d)
        d = Math.log(d)
        d = Math.exp(d)
        d = Math.pow(10.0, d)
        d = Math.atan(d)
        d = Math.toDegrees(d)
        //arrondi au dessus
        val up = Math.ceil(d)
        //arrondi au dessous
        val down = Math.floor(d)
        //arrondi au plus près
        val nearest = Math.round(d)

        //java.lang.Math.Random()
        val r = Math.random()
        assertTrue(r &amp;amp;gt;= 0.0 &amp;amp;amp;&amp;amp;amp; r &amp;amp;lt; 1.0)

        //créé un nouvel objet Random, en l&amp;#39;initialisant
        //avec l&amp;#39;heure courante
        val generator = java.util.Random(System.currentTimeMillis())

        //prochaine valeur aléatoire de taille double
        d = generator.nextDouble()
        assertTrue((d &amp;amp;gt;= 0.0) &amp;amp;amp;&amp;amp;amp; (d &amp;amp;lt; 1.0))


        //prochaine valeur aléatoire de taille float
        f = generator.nextFloat()
        assertTrue((f &amp;amp;gt;= 0.0) &amp;amp;amp;&amp;amp;amp; (f &amp;amp;lt; 1.0))


        //prochaine valeur aléatoire de taille long
        l = generator.nextLong()
        assertTrue(
            (Math.abs(l) &amp;amp;lt;= Long.MAX_VALUE) &amp;amp;amp;&amp;amp;amp;
                    (Math.abs(l) &amp;amp;gt;= 0)
        )


        //prochaine valeur aléatoire de taille int
        i = generator.nextInt()
        assertTrue(
            (Math.abs(i) &amp;amp;lt;= java.lang.Integer.MAX_VALUE) &amp;amp;amp;&amp;amp;amp;
                    (Math.abs(i) &amp;amp;gt;= 0)
        )

        val limit = 100
        //prochaine valeur aléatoire de taille int
        //la limit max du ramdom est ramené à limit
        //et la limit min est 0
        i = generator.nextInt(limit)
        assertTrue(i in 0 until limit)


        //prochaine valeur aléatoire de taille booléen
        val bool = generator.nextBoolean()
        assertNotNull(bool)


        //valeur moyenne 0.0, déviation standard 1.0
        d = generator.nextGaussian()


        //randoms bytes
        //rempli un tableau avec des valeurs byte aléatoires
        val b_arr = ByteArray(128)
        generator.nextBytes(b_arr)
        b_arr.iterator().forEachRemaining {
            assertTrue(
                it &amp;amp;lt;= Byte.MAX_VALUE &amp;amp;amp;&amp;amp;amp;
                        it &amp;amp;gt;= Byte.MIN_VALUE
            )
        }

        //java.security.SecureRandom pour les nombres aléatoires
        //utilisé en cryptographie
        val secure_generator = SecureRandom()
        //le générateur génère sa propre tete de liste sur 16 octets
        secure_generator.setSeed(secure_generator.generateSeed(16))
        val sec_b_arr = ByteArray(128)
        secure_generator.nextBytes(sec_b_arr)
        sec_b_arr.iterator().forEachRemaining {
            assertTrue(
                it &amp;amp;lt;= java.lang.Byte.MAX_VALUE &amp;amp;amp;&amp;amp;amp;
                        it &amp;amp;gt;= java.lang.Byte.MIN_VALUE
            )
        }

        //java.math.BigDecimal java.math.BigInteger
        //pour travailler sur des grandes valeurs.
        //calcule de la factorielle de 1000
        var total = BigInteger.valueOf(1)
        (2..1000).forEach {
            total = total.multiply(BigInteger.valueOf(it.toLong()))
        }
        assertTrue(total.toString().length == 2568)
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc&amp;quot;&amp;gt;sumário&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;date&amp;quot;&amp;gt;datas e horas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;import java.text.DateFormat
import java.text.SimpleDateFormat
import java.time.Instant
import java.util.*
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class DatesHoursTest {
    @Test
    fun `Dates et heures`() {
        //l&amp;#39;heure courante en millisecondes
        val t0 = System.currentTimeMillis()
        //une autre représentation de la meme information
        val now = java.util.Date()
        //converti un objet java.util.Date en une valeur long.
        val t1 = now.getTime()
        assertTrue(t1 &amp;amp;gt; Instant.EPOCH.toEpochMilli())
        //kotlin property access syntaxe style
        val t1_prime = now.time

        //java.text.DateFormat
        //affiche la date d&amp;#39;aujourd&amp;#39;hui en utilisant le format
        //par défaut des parametres locaux
        val defaultDateFormat = DateFormat.getDateInstance()
        //personnalisation du formatage et de la locale
        val df = DateFormat.getDateInstance(DateFormat.LONG, Locale.FRANCE)
        val localeFormattedDate = df.format(Date())

        //constantes pour les styles de pattern de formatage
        assertEquals(0, DateFormat.FULL)
        assertEquals(1, DateFormat.LONG)
        assertEquals(2, DateFormat.MEDIUM)
        assertEquals(3, DateFormat.SHORT)
        assertEquals(2, DateFormat.DEFAULT)

        //utilise pour l&amp;#39;heure un format abrégé avec
        //des parametres personnalisés
        val tf = DateFormat.getTimeInstance(
            DateFormat.SHORT,
            Locale.FRANCE
        )
        //affiche l&amp;#39;heure en utilisant le format de tf
        val shortTime = tf.format(Date())
        assertTrue(shortTime.contains(&amp;#39;:&amp;#39;))

        //affiche la date et l&amp;#39;heure en utilisant
        //un format détaillé
        val longTimeStamp = DateFormat.getDateTimeInstance(
            DateFormat.FULL,
            DateFormat.FULL,
        )
        assert(longTimeStamp.format(Date()).isNotEmpty())

        //utilisez java.text.SimpleDateFormat
        //pour définir votre propre modele de formatage
        val customFormat = SimpleDateFormat(&amp;quot;yyyy.MM.dd&amp;quot;)
        assertEquals(10, customFormat.format(Date()).length)

        //DateFormat peut également parser les date contenu dans des chaines
        val kotlinAnnounceDate = customFormat.parse(&amp;quot;2019.05.08&amp;quot;)

        //la class Date et sa représentation en millisecondes
        //n&amp;#39;autorise qu&amp;#39;une forme trés simple d&amp;#39;arithmétique
        //on ajoute 3 600 000 millisecondes à l&amp;#39;heure courante
        val anHourFromNow = now.getTime() + (60 * 60 * 1000)
        assert(anHourFromNow &amp;amp;gt; now.getTime())

        //java.util.Calendar
        //pour manipuler les dates et heures de facon plus sophistiquée
        //instanciation selon les parametres locaux
        //et le fuseau horaire local
        val calendar = Calendar.getInstance()
        //initialisation du calendrier à la date de maintenant
        calendar.setTime(now)
        //détermine le jour de l&amp;#39;année auquel correspond la date courante
        val dayOfYear = calendar.get(Calendar.DAY_OF_YEAR)
        assert(dayOfYear &amp;amp;lt; 366)
        //réinitialisation de la date courante
        calendar.set(2019, Calendar.MAY, 8)
        assertEquals(4, calendar.get(Calendar.DAY_OF_WEEK))

        //à quel jour du mois correspond le deuxieme mercredi de mai 2019
        //set(key,value)
        calendar.set(Calendar.YEAR, 2019)
        calendar.set(Calendar.MONTH, Calendar.MAY)
        calendar.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY)
        //defini à quel (n=2) semaine du mois est la date
        calendar.set(Calendar.DAY_OF_WEEK_IN_MONTH, 2)
        //extrait le jour du mois
        val dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH)
        assertEquals(8, dayOfMonth)

        calendar.setTime(kotlinAnnounceDate)
        //ajoute 30j à la date
        calendar.add(Calendar.DATE, 30)
        val monthAfter = calendar.getTime()
        //date est elle avant ou apres?
        assertTrue(monthAfter.after(kotlinAnnounceDate))
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc&amp;quot;&amp;gt;sumário&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;java8date&amp;quot;&amp;gt;datas e horas java8&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Java 8 introduz o novo pacote &amp;lt;strong&amp;gt;&amp;lt;em&amp;gt;java.time&amp;lt;/em&amp;gt;&amp;lt;/strong&amp;gt;, que contém as classes básicas que&amp;lt;br&amp;gt;
a maioria dos desenvolvedores trabalha com. Contém também quatro subpacotes:&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;em&amp;gt;java.time.chrono&amp;lt;/em&amp;gt;&amp;lt;/strong&amp;gt;&amp;lt;br&amp;gt;
Cronologias alternativas que os desenvolvedores que usam sistemas de calendário que não&amp;lt;br&amp;gt;
seguir a norma ISO vai interagir com. Um exemplo seria um cal japonês&amp;lt;br&amp;gt;
sistema duradouro.&amp;lt;br&amp;gt;
&amp;lt;strong&amp;gt;&amp;lt;em&amp;gt;java.time.format&amp;lt;/em&amp;gt;&amp;lt;/strong&amp;gt;&amp;lt;br&amp;gt;
Contém o DateTimeFormatter usado para converter os objetos de data e hora&amp;lt;br&amp;gt;
em uma cadeia e também para analisar as cadeias nos objetos de dados e de tempo&amp;lt;br&amp;gt;
&amp;lt;strong&amp;gt;&amp;lt;em&amp;gt;java.time.temporal&amp;lt;/em&amp;gt;&amp;lt;/strong&amp;gt;&amp;lt;br&amp;gt;
Contém as interfaces exigidas pelas classes de data e hora de base e também&amp;lt;br&amp;gt;
abstrações (como consultas e ajustadores) para operações avançadas com datas.&amp;lt;br&amp;gt;
&amp;lt;strong&amp;gt;&amp;lt;em&amp;gt;java.time.zone&amp;lt;/em&amp;gt;&amp;lt;/strong&amp;gt;&amp;lt;br&amp;gt;
Classes usadas para as regras de fuso horário subjacentes;&amp;lt;br&amp;gt;
A maioria dos desenvolvedores não precisará deste pacote.&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;../../img/java8-date-time.png&amp;quot; alt=&amp;quot;java8 date time&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;import java.time.LocalDate
import java.time.Month
import java.time.Period
import java.time.YearMonth
import java.time.temporal.*
import java.util.HashMap
import java.util.stream.Collectors
import kotlin.test.Test

class DatesHoursJava8Test {
    class BirthdayDiary {
        private val birthdays: MutableMap&amp;amp;lt;String, LocalDate&amp;amp;gt;

        init {
            birthdays = HashMap()
        }

        fun addBirthday(
            name: String, day: Int, month: Int,
            year: Int
        ): LocalDate {
            val birthday: LocalDate = LocalDate.of(year, month, day)
            birthdays[name] = birthday
            return birthday
        }

        fun getBirthdayFor(name: String): LocalDate? {
            return birthdays[name]
        }

        fun getAgeInYear(name: String, year: Int): Int {
            val period: Period = Period.between(
                birthdays[name],
                birthdays[name]!!.withYear(year)
            )
            return period.getYears()
        }

        fun getFriendsOfAgeIn(age: Int, year: Int): Set&amp;amp;lt;String&amp;amp;gt; {
            return birthdays.keys.stream()
                .filter { p: String -&amp;amp;gt; getAgeInYear(p, year) == age }
                .collect(Collectors.toSet())
        }

        fun getDaysUntilBirthday(name: String): Int {
            val period: Period = Period.between(
                LocalDate.now(),
                birthdays[name]
            )
            return period.getDays()
        }

        fun getBirthdaysIn(month: Month): Set&amp;amp;lt;String&amp;amp;gt; {
            return birthdays.entries.stream()
                .filter { (_, value): Map.Entry&amp;amp;lt;String, LocalDate&amp;amp;gt; -&amp;amp;gt; value.getMonth() === month }
                .map&amp;amp;lt;String&amp;amp;gt; { (key): Map.Entry&amp;amp;lt;String, LocalDate&amp;amp;gt; -&amp;amp;gt; key }
                .collect(Collectors.toSet())
        }

        val birthdaysInCurrentMonth: Set&amp;amp;lt;String&amp;amp;gt;
            get() = getBirthdaysIn(LocalDate.now().getMonth())
        val totalAgeInYears: Int
            get() = birthdays.keys.stream()
                .mapToInt { p: String -&amp;amp;gt;
                    getAgeInYear(
                        p,
                        LocalDate.now().getYear()
                    )
                }
                .sum()
    }

    //Les ajusteurs modifient les objets de date et d&amp;#39;heure. Supposons, par exemple, que nous voulions
    //renvoie le premier jour d&amp;#39;un trimestre qui contient un horodatage particulier :
    class FirstDayOfQuarter : TemporalAdjuster {
        override fun adjustInto(temporal: Temporal): Temporal? {
            val currentQuarter: Int = YearMonth.from(temporal)
                .get(IsoFields.QUARTER_OF_YEAR)
            return when (currentQuarter) {
                1 -&amp;amp;gt; LocalDate.from(temporal)
                    .with(TemporalAdjusters.firstDayOfYear())

                2 -&amp;amp;gt; LocalDate.from(temporal)
                    .withMonth(Month.APRIL.value)
                    .with(TemporalAdjusters.firstDayOfMonth())

                3 -&amp;amp;gt; LocalDate.from(temporal)
                    .withMonth(Month.JULY.value)
                    .with(TemporalAdjusters.firstDayOfMonth())

                4 -&amp;amp;gt; LocalDate.from(temporal)
                    .withMonth(Month.OCTOBER.value)
                    .with(TemporalAdjusters.firstDayOfMonth())

                else -&amp;amp;gt; null
            }
        }
    }

    enum class Quarter {
        FIRST, SECOND, THIRD, FOURTH
    }

    //Dans quel trimestre de l&amp;#39;année cette date se situe-t-elle ?
    class QuarterOfYearQuery : TemporalQuery&amp;amp;lt;Quarter&amp;amp;gt; {
        override fun queryFrom(temporal: TemporalAccessor): Quarter {
            val now = LocalDate.from(temporal)
            return if (now.isBefore(now.with(Month.APRIL).withDayOfMonth(1)))
                Quarter.FIRST
            else if (now.isBefore(
                    now.with(Month.JULY)
                        .withDayOfMonth(1)
                )
            ) Quarter.SECOND else if (now.isBefore(
                    now.with(Month.NOVEMBER)
                        .withDayOfMonth(1)
                )
            ) Quarter.THIRD else Quarter.FOURTH
        }
    }

    @Test
    fun `Dates et heures après java 8`() {
        val today = LocalDate.now()
        val currentMonth = today.month
        val firstMonthOfQuarter = currentMonth.firstMonthOfQuarter()

        val q = QuarterOfYearQuery()
        // Direct
        var quarter: Quarter? = q.queryFrom(LocalDate.now())
        println(quarter)
        // Indirect
        quarter = LocalDate.now().query(q)
        println(quarter)


        val now = LocalDate.now()
        val fdoq: Temporal = now.with(FirstDayOfQuarter())
        println(fdoq)
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc&amp;quot;&amp;gt;sumário&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;arrCol&amp;quot;&amp;gt;tabelas e coleções&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;../../img/collections_classes_inheritance.png&amp;quot; alt=&amp;quot;Classes de collections et héritage&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;package playground.programming

import java.util.*
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class ArrayCollectionTest {
    @Test
    fun `Tableaux, collections`() {
        //Tableau
        //java.util.Arrays définit d&amp;#39;utiles méthodes de manipulation de tableaux,
        //y compris de tri et de recherche au sein d&amp;#39;un tableau
        val intArray = arrayOf(10, 5, 7, -3)
        //tri le tableau
        Arrays.sort(intArray)
        var pos = Arrays.binarySearch(intArray, 7)
        //la valeur 7 est trouvé a l&amp;#39;index 2
        assertEquals(2, pos)
        //12 pas trouvé retourne une valeur negative
        assert(Arrays.binarySearch(intArray, 12) &amp;amp;lt; 0)

        //les tableaux peuvent également etre triés
        //et faire l&amp;#39;objet d&amp;#39;une recherche
        val stringArray = arrayOf(&amp;quot;le&amp;quot;, &amp;quot;moment&amp;quot;, &amp;quot;c&amp;#39;est&amp;quot;)
        assertEquals(&amp;quot;c&amp;#39;est&amp;quot;, stringArray[2])
        assertEquals(&amp;quot;le&amp;quot;, stringArray[0])
        assertEquals(&amp;quot;moment&amp;quot;, stringArray[1])
        Arrays.sort(stringArray)
        assertEquals(&amp;quot;c&amp;#39;est&amp;quot;, stringArray[0])
        assertEquals(&amp;quot;le&amp;quot;, stringArray[1])
        assertEquals(&amp;quot;moment&amp;quot;, stringArray[2])

        //Arrays.equals() compare tous les éléments de deux tableaux
        //Arrays.clone() copie tous les elements du tableau dans un autre
        stringArray.forEachIndexed { i, it -&amp;amp;gt; assertEquals(it, stringArray.clone()[i]) }

        val data = ByteArray(100)
        //Arrays.fill() initialise tous les éléments des deux tableaux
        //initalise tous les éléments à -1
        Arrays.fill(data, -1)
        data.forEach { assertEquals(-1, it) }

        //attribue aux éléments 5, 6, 7, 8 et 9 la valeur -2
        Arrays.fill(data, 5, 10, -2)
        ((5 until (10 - 1))).forEach { assertEquals(-2, data[it]) }

        //récupère le type de data
        val type = data::class.java
        //est ce que data est un tableau?
        assertTrue(type.isArray())
        //est ce que data est un tableau de byte
        assertEquals(Byte::class.java, type.getComponentType())

        //Collection
        val s = java.util.HashSet&amp;amp;lt;String&amp;amp;gt;()
        s.add(&amp;quot;test&amp;quot;)
        assertTrue(s.contains(&amp;quot;test&amp;quot;))
        assertFalse(s.contains(&amp;quot;test2&amp;quot;))
        s.remove(&amp;quot;test&amp;quot;)
        assertFalse(s.contains(&amp;quot;test&amp;quot;))

        val ss = TreeSet&amp;amp;lt;String&amp;amp;gt;()
        ss.add(&amp;quot;b&amp;quot;)
        ss.add(&amp;quot;a&amp;quot;)
        ss.iterator().forEach { assertTrue(it == &amp;quot;a&amp;quot; || it == &amp;quot;b&amp;quot;) }

        //liste doublement chainée
        var dll: List&amp;amp;lt;String&amp;amp;gt; = LinkedList&amp;amp;lt;String&amp;amp;gt;()

        //plus efficace
        val l = java.util.ArrayList&amp;amp;lt;String&amp;amp;gt;()
        l.addAll(ss)
        l.addAll(1, ss)

        val obj = l.get(1)
        val obj_prime = l[1]
        assertEquals(obj, obj_prime)

        l.set(3, &amp;quot;nouvel élément&amp;quot;)
        l.add(&amp;quot;test&amp;quot;)
        l.add(0, &amp;quot;test2&amp;quot;)
        l.removeAt(1)
        l.remove(&amp;quot;a&amp;quot;)
        assertFalse(l.contains(&amp;quot;a&amp;quot;))
        l.removeAll(ss)
        assertFalse(l.containsAll(ss))
        assertFalse(l.isEmpty())
        assertTrue(l.isNotEmpty())


        val sublist = l.subList(1, 3)
        val elements = l.toArray()
        l.clear()

        val m = HashMap&amp;amp;lt;String, Integer&amp;amp;gt;()
        m.put(&amp;quot;clé&amp;quot;, Integer(42))
        m[&amp;quot;clé&amp;quot;] = Integer(42)
        val value: Integer = m.get(&amp;quot;clé&amp;quot;)!!
        assertEquals(Integer(42), value)
        m.remove(&amp;quot;clé&amp;quot;)
        assertTrue(m.isEmpty())
        val keys = m.keys
        assertTrue(keys.isEmpty())


        val set = HashSet&amp;amp;lt;String&amp;amp;gt;()
        set.add(&amp;quot;key_1&amp;quot;)
        set.add(&amp;quot;key_2&amp;quot;)
        set.add(&amp;quot;key_3&amp;quot;)
        val members = set.toArray()
        assertEquals(3, members.size)
        val list = ArrayList&amp;amp;lt;String&amp;amp;gt;()
        list.add(&amp;quot;items1&amp;quot;)
        list.add(&amp;quot;items2&amp;quot;)
        list.add(&amp;quot;items3&amp;quot;)
        val items = list.toArray()
        assertEquals(3, items.size)

        //trie et recherche d&amp;#39;éléments sur les collections
        list.add(&amp;quot;clé&amp;quot;)
        //en premier on trie
        Collections.sort(list)
        //en kotlin
        list.sort()
        //en deuxieme on cherche
        //retourne l&amp;#39;index du premier trouvé sinon -1
        pos = Collections.binarySearch(list, &amp;quot;clé&amp;quot;)
        assertEquals(0, pos)
        val list1 = mutableListOf(1, 2, 3, 4, 5)
        val list2 = mutableListOf&amp;amp;lt;Int&amp;amp;gt;(0, 0, 0, 0, 0)

        //d&amp;#39;autres méthodes intéressantes concernant Collections

        //copie list1 dans list2, 2e parametre dans 1er parametre
        Collections.copy(list2, list1)
        //comparaison de la copy avec filter
        assertTrue(list1.filterIndexed { i: Int, it: Int -&amp;amp;gt; it != list2[i] }.isEmpty())
        //comparaison de la copy avec map
        list1.mapIndexed { index: Int, it: Int -&amp;amp;gt; assertEquals(it, list2[index]) }

        //rempli avec des 0
        Collections.fill(list2, 0)
        assertTrue(list2.none { it != 0 })

        //le maximum
        assertEquals(5, Collections.max(list1))

        //le minimum
        assertEquals(1, Collections.min(list1))

        //renverse
        Collections.reverse(list)
        listOf(&amp;quot;items3&amp;quot;, &amp;quot;items2&amp;quot;, &amp;quot;items1&amp;quot;, &amp;quot;clé&amp;quot;).mapIndexed { i: Int, it: String -&amp;amp;gt; assertEquals(it, list[i]) }

        //mélange la list
        Collections.shuffle(list)

        //retourne un ensemble immuable possédant un seul élément 0
        Collections.singleton(0)
        //renvoi un emballage immuable autour d&amp;#39;une liste
        Collections.unmodifiableList(list)
        //renvoi un emballage synchronisé autour d&amp;#39;une map, ensemble clé valeur
        Collections.synchronizedMap(m)

        //java.util.Properties un est objet key value
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc&amp;quot;&amp;gt;sumário&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;sysProp&amp;quot;&amp;gt;System.Properties&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html&amp;quot;&amp;gt;System.Properties documentação oficial&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;aqui está uma tabela de algumas propriedades interessantes&amp;lt;br&amp;gt;
Estas propriedades são interessantes para obter informações&amp;lt;br&amp;gt;
no sistema host da JVM.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3333%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 3.3343%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Chave&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Significado&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;file.separator&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Caractere que separa os componentes de um caminho de arquivo. É &amp;#39;/&amp;#39; no UNIX e &amp;#39;\&amp;#39; no Windows.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;java.class.path&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Caminho usado para encontrar diretórios e arquivos JAR que contêm arquivos de classe. Os elementos do caminho de classe são separados por um caractere específico da plataforma especificado na propriedade path.separator.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;java.home&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Diretório de instalação para Ambiente de Execução Java (JRE)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;java.vendor&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;nome do fornecedor da JRE&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;java.vendor.url&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;URL do fornecedor JRE&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;java.version&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Número da versão do JRE&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;separador de linha&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Sequência usada pelo sistema operacional para separar linhas em arquivos de texto&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;os.arch&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Arquitetura do sistema operacional&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;os.name&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nome do sistema operacional&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;os.version&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Versão do sistema operacional&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;path.separator&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Caractere separador de caminho usado em java.class.path&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;user.dir&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Diretório de trabalho do usuário&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;user.home&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;diretório pessoal do usuário&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;user.name&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Nome da conta do usuário&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc&amp;quot;&amp;gt;sumário&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;threads&amp;quot;&amp;gt;Fios&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;import java.text.DateFormat
import java.util.*
import kotlin.test.Test

class ThreadsTest {

    @Test
    fun `threads test`() {
        //java.lang.Thread représente le thread fondamentale de l&amp;#39;API java
        //il existe deux manière de définir un thread
        //1) étendre la classe Thread ou une lambda en kotlin
        //2) implémenter l&amp;#39;interface Runnable,
        //      puis passer une instance de cet objet Runnable au constructeur de Thread.

        val list1: List&amp;amp;lt;Int&amp;amp;gt; = List(
            size = 45,
            init = { (1..31).random() }
        )
        println(&amp;quot;list1$list1&amp;quot;)
        //facon 1
        val t = Thread {
            Collections.sort(list1)
            println(&amp;quot;list1 sorted$list1&amp;quot;)
        }
        t.start()

        val list2 = List(
            size = 45,
            init = { (1..31).random() }
        )
        println(&amp;quot;list2$list2&amp;quot;)
        //facon 2
        val sorter = BackgroundSorter(list2)
        sorter.start()

        //priorité des threads
        //tant qu&amp;#39;un thread de niveau de priorité supérieure n&amp;#39;est pas fini
        //alors celui de niveau inférieur ne peut s&amp;#39;exécuter

        //on définit un thread avec une priorité inférieur à la normale
        t.setPriority(Thread.NORM_PRIORITY - 1)

        //ici on définit un thread avec une priorité inférieur à la priorité
        //du thread courant
        t.setPriority(Thread.currentThread().getPriority() - 1)

        //Thread.yield() fait une pause pour laisser les autres threads de meme priorité s&amp;#39;exécuter
    }

    class BackgroundSorter(private val l: List&amp;amp;lt;Int&amp;amp;gt;) : Thread() {
        override fun run() {
            Collections.sort(l)
            println(&amp;quot;list2 sorted$l&amp;quot;)
        }
    }

    //pour l&amp;#39;arrêt du thread, plutôt qu&amp;#39;utiliser la fonction Thread.stop()
    // qui laisse la memoire dans un état non controlé.
    //utiliser la méthode tel que l&amp;#39;exemple pleaseStop()
    class DummyClock(
        private val df: DateFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM),
        private var keepRunning: Boolean = true
    ) : Thread() {
        init {
            isDaemon = true
            start()
        }

        override fun run() {
            while (keepRunning) {
                println(df.format(Date()))
                try {
                    sleep(1000)
                } catch (e: InterruptedException) {
                    println(e.message)
                }
            }
        }

        fun pleaseStop() {
            keepRunning = false
        }
    }

    //java.util.Timer
    //java.util.TimerTask
    //ces classes permettent l&amp;#39;exécution de taches répétitives
    @Test
    fun `Timer et TimerTask test`() {
        DummyClock()
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#toc&amp;quot;&amp;gt;sumário&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;regex&amp;quot;&amp;gt;Expressões Regulares&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;import java.util.*
import java.util.regex.Matcher
import java.util.regex.Pattern
import java.util.stream.Collectors
import kotlin.test.Test
import kotlin.test.assertEquals

class RegularExpressionsTest {
    @Test
    fun `expressions régulières`() {
        val p: Pattern = Pattern.compile(&amp;quot;honou?r&amp;quot;)
        val caesarUK = &amp;quot;For Brutus is an honourable man&amp;quot;
        val mUK: Matcher = p.matcher(caesarUK)
        assertEquals(true, mUK.find(), &amp;quot;Should matches UK spelling&amp;quot;)
        val caesarUS = &amp;quot;For Brutus is an honorable man&amp;quot;
        val mUS: Matcher = p.matcher(caesarUS)
        assertEquals(true, mUS.find(), &amp;quot;Should matches US spelling&amp;quot;)
    }

    @Test
    fun `expressions régulières plus complexes`() {
        //Notez que nous devons utiliser \\ car nous avons besoin d&amp;#39;un littéral \
        //et Java utilise un seul \ comme caractère d&amp;#39;échappement
        var pStr = &amp;quot;\\d&amp;quot; // Un chiffre numérique

        var text = &amp;quot;Apollo 13&amp;quot;
        var p = Pattern.compile(pStr)
        var m = p.matcher(text)
        print(pStr + &amp;quot; matches &amp;quot; + text + &amp;quot;? &amp;quot; + m.find())
        println(&amp;quot; ; match: &amp;quot; + m.group())
        pStr = &amp;quot;[a..zA..Z]&amp;quot; //N&amp;#39;importe quelle lettre

        p = Pattern.compile(pStr)
        m = p.matcher(text)
        print(pStr + &amp;quot; matches &amp;quot; + text + &amp;quot;? &amp;quot; + m.find())
        println(&amp;quot; ; match: &amp;quot; + m.group())

        //N&amp;#39;importe quel nombre de lettres, qui doivent toutes être comprises entre &amp;#39;a&amp;#39; et &amp;#39;j&amp;#39;
        //mais peut-être en majuscule ou en minuscule.
        pStr = &amp;quot;([a..jA..J]*)&amp;quot;
        p = Pattern.compile(pStr)
        m = p.matcher(text)
        print(pStr + &amp;quot; matches &amp;quot; + text + &amp;quot;? &amp;quot; + m.find())
        println(&amp;quot; ; match: &amp;quot; + m.group())
        text = &amp;quot;abacab&amp;quot;
        //&amp;#39;a&amp;#39; suivi de quatre caractères quelconques, suivi de &amp;#39;b&amp;#39;
        pStr = &amp;quot;a....b&amp;quot;
        p = Pattern.compile(pStr)
        m = p.matcher(text)
        print(pStr + &amp;quot; matches &amp;quot; + text + &amp;quot;? &amp;quot; + m.find())
        println(&amp;quot; ; match: &amp;quot; + m.group())
    }

    @Test
    fun `Quelles chaînes correspondent à la regex ?`() {
        val pStr = &amp;quot;\\d&amp;quot; // Un chiffre numérique
        val p = Pattern.compile(pStr)
        val ls: List&amp;amp;lt;String&amp;amp;gt; = Arrays.asList(&amp;quot;Cat&amp;quot;, &amp;quot;Dog&amp;quot;, &amp;quot;Ice-9&amp;quot;, &amp;quot;99 Luftballoons&amp;quot;)
        val containDigits: List&amp;amp;lt;String&amp;amp;gt; = ls.stream()
            .filter(p.asPredicate())
            .collect(Collectors.toList())
        assert(containDigits.contains(&amp;quot;Ice-9&amp;quot;))
        assert(containDigits.contains(&amp;quot;99 Luftballoons&amp;quot;))
        assertEquals(2, containDigits.size)
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_tabela_regex_metacaracteres&amp;quot;&amp;gt;Tabela regex metacaracteres&amp;lt;/h3&amp;gt;
&amp;lt;table class=&amp;quot;tableblock frame-all grid-all stretch&amp;quot;&amp;gt;
&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7543%;&amp;quot;&amp;gt;
&amp;lt;col style=&amp;quot;width: 1.7592%;&amp;quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Metacaractere&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;funcionalidade&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Notas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;?&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Caractere opcional—zero ou uma instância&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;*&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Zero ou mais do caractere anterior&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;+&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Um ou mais dos caracteres anteriores&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;{M,N}&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Entre M e N instâncias do caractere anterior&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\d&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Um número&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\D&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Um caractere não numérico&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\w&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Um caractere de palavra&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Números, letras e _&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\W&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Um caractere sem palavra&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\s&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Um caractere de espaçamento&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\S&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Um caractere não branco&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\n&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Caractere de quebra de linha&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;\t&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;caractere de tabulação&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;.&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Qualquer caractere&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Não inclui a nova linha em Java&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;[ ]&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Qualquer caractere contido entre colchetes&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Chamada uma classe de caracteres&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;[^ ]&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Qualquer caractere não contido entre colchetes&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Chamado uma classe de caracteres invertida&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;( )&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Construir um grupo de elementos do padrão&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Chamado um grupo (ou grupo de captura)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;|&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Definir possibilidades alternativas&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Implemente o OU lógico&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;^&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;tableblock&amp;quot;&amp;gt;Início da string $ Fim da string&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&amp;quot;tableblock halign-left valign-top&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;completarhttps://www.codeurjava.com/2015/05/les-expressions-regulieres-avec-regex.html[com]&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Memorando Gradle</title>
            <link >https://cheroliv.com//blog/2022/0047_memo_gradle_post.html</link>
            <pubDate>Mon, 23 May 2022 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2022/0047_memo_gradle_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;toc&amp;quot; class=&amp;quot;toc&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;toctitle&amp;quot;&amp;gt;Sommaire&amp;lt;/div&amp;gt;
&amp;lt;ul class=&amp;quot;sectlevel1&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_mostrar_ajuda&amp;quot;&amp;gt;Mostrar ajuda&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_definir_a_propriedade_do_sistema_da_jvm_d_system_prop&amp;quot;&amp;gt;Definir a propriedade do sistema da JVM: `-D, --system-prop&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_definir_a_propriedade_do_projeto_para_o_script_de_build_pmypropmyvalue&amp;quot;&amp;gt;Definir a propriedade do projeto para o script de build : `-Pmyprop=myvalue&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_especifique_o_diretório_de_início_do_gradle_o_valor_padrão_é_o_diretório_atual&amp;quot;&amp;gt;Especifique o diretório de início do Gradle. O valor padrão é o diretório atual.&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_sobrescrever_a_propriedade_param_component_pela_linha_de_comando&amp;quot;&amp;gt;Sobrescrever a propriedade &amp;#39;param_component&amp;#39; pela linha de comando:&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_adicionar_uma_pasta_a_um_source_set_em_um_projeto_normal_com_o_dsl_kotlin&amp;quot;&amp;gt;Adicionar uma pasta a um source-set em um projeto normal com o DSL Kotlin&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_adicionar_uma_pasta_a_um_source_set_em_um_projeto_android_com_o_groovy_dsl&amp;quot;&amp;gt;Adicionar uma pasta a um source-set em um projeto Android com o Groovy DSL&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_evite_construir_e_testar_um_submódulo&amp;quot;&amp;gt;Evite construir e testar um submódulo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_tar_um_arquivo&amp;quot;&amp;gt;Tar um arquivo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_exibir_as_dependências_de_um_build&amp;quot;&amp;gt;Exibir as dependências de um build&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_exemplo_de_uso_de_reduce_concatenação_de_uma_list_em_uma_string&amp;quot;&amp;gt;Exemplo de uso de reduce: concatenação de uma list em uma string&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_recuperar_a_descrição_de_uma_tarefa_em_particular&amp;quot;&amp;gt;Recuperar a descrição de uma tarefa em particular&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_para_lançar_uma_tarefa_a_partir_do_workingdir_root_então_prefixar_a_tarefa_não_um&amp;quot;&amp;gt;Para lançar uma tarefa a partir do workingDir root, então prefixar a tarefa não um `:&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_para_reiniciar_as_tarefas_que_falharam_usar_a_opção_rerun_tasks&amp;quot;&amp;gt;Para reiniciar as tarefas que falharam, usar a opção `--rerun-tasks&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#_lançar_uma_tarefa_em_um_projeto_específico_a_partir_de_outro_diretório&amp;quot;&amp;gt;Lançar uma tarefa em um projeto específico a partir de outro diretório&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_mostrar_ajuda&amp;quot;&amp;gt;Mostrar ajuda&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./gradlew --help&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_definir_a_propriedade_do_sistema_da_jvm_d_system_prop&amp;quot;&amp;gt;Definir a propriedade do sistema da JVM: `-D, --system-prop&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./gradlew -Dmyprop=myvalue
#ou
./gradlew --system-prop myvalue&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_definir_a_propriedade_do_projeto_para_o_script_de_build_pmypropmyvalue&amp;quot;&amp;gt;Definir a propriedade do projeto para o script de build : `-Pmyprop=myvalue&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./gradlew -Pmyprop=myvalue
#ou
./gradlew --project-prop myvalue&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_especifique_o_diretório_de_início_do_gradle_o_valor_padrão_é_o_diretório_atual&amp;quot;&amp;gt;Especifique o diretório de início do Gradle. O valor padrão é o diretório atual.&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./gradlew -p ~/src/next_startup
#ou
./gradlew --project-dir ~/src/next_startup&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_sobrescrever_a_propriedade_param_component_pela_linha_de_comando&amp;quot;&amp;gt;Sobrescrever a propriedade &amp;#39;param_component&amp;#39; pela linha de comando:&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./gradlew -Pparam_component=CUSTOM_VALUE&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_adicionar_uma_pasta_a_um_source_set_em_um_projeto_normal_com_o_dsl_kotlin&amp;quot;&amp;gt;Adicionar uma pasta a um source-set em um projeto normal com o DSL Kotlin&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;sourceSets {
    getByName(&amp;quot;test&amp;quot;){
        java.srcDir(&amp;quot;src/scripts/groovy&amp;quot;)
    }
    getByName(&amp;quot;test&amp;quot;){
        java.srcDir(&amp;quot;src/scripts/kscript&amp;quot;)
    }
    getByName(&amp;quot;test&amp;quot;){
        java.srcDir(&amp;quot;src/test/javascript&amp;quot;)
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_adicionar_uma_pasta_a_um_source_set_em_um_projeto_android_com_o_groovy_dsl&amp;quot;&amp;gt;Adicionar uma pasta a um source-set em um projeto Android com o Groovy DSL&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;android {
    sourceSets {
        main.java.srcDirs += &amp;quot;src/main/../../../../ceelo/domain/src/main/java/&amp;quot;
        test.java.srcDirs += &amp;quot;src/test/../../../../ceelo/domain/src/test/java/&amp;quot;
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_evite_construir_e_testar_um_submódulo&amp;quot;&amp;gt;Evite construir e testar um submódulo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;gradle build -x :excluded-module:check -x :excluded-module:assemble -x :excluded-module:build&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Mover arquivos&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;tasks.register(&amp;quot;moveWebappNode&amp;quot;) {
    doLast {
        ant.withGroovyBuilder {
            &amp;quot;move&amp;quot;(
                &amp;quot;webapp/node_modules&amp;quot; to &amp;quot;$rootDir/webapp-src/node_modules&amp;quot;,
            )
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_tar_um_arquivo&amp;quot;&amp;gt;Tar um arquivo&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;tasks.register&amp;amp;lt;Tar&amp;amp;gt;(&amp;quot;tarWebapp&amp;quot;) {
    dependsOn(&amp;quot;moveWebappNpm&amp;quot;)
    group = WEBAPP
    description = &amp;quot;tar webapp&amp;quot;
    doLast {
        setOf(
            &amp;quot;build&amp;quot;,
            &amp;quot;target&amp;quot;,
            &amp;quot;node_modules&amp;quot;
        ).forEach { dir -&amp;amp;gt; exclude { it.name .dir } }
        archiveFileName.set(&amp;quot;webapp.tar&amp;quot;)
        destinationDirectory.set(File(&amp;quot;${rootDir.absolutePath}$sep$WEBAPP_SRC&amp;quot;))
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_exibir_as_dependências_de_um_build&amp;quot;&amp;gt;Exibir as dependências de um build&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;tasks.register(&amp;quot;printDependencies&amp;quot;) {
    description = &amp;quot;print project dependencies&amp;quot;
    group = WEBAPP
    doLast {
        println(&amp;quot;${project.name} dependencies&amp;quot;)
        mutableMapOf&amp;amp;lt;String, Map&amp;amp;lt;String, String&amp;amp;gt;&amp;amp;gt;(
            &amp;quot;buildDependencies&amp;quot; to buildDependencies,
            &amp;quot;domainDeps&amp;quot; to domainDeps,
            &amp;quot;domainTestDeps&amp;quot; to domainTestDeps,
        ).apply { putAll(appModules) }
            .forEach { module -&amp;amp;gt;
                if (module.value.isNotEmpty()) {
                    println(&amp;quot;${module.key}:&amp;quot;)
                    module.value.forEach { println(dependency(it)) }
                    println()
                }
            }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_exemplo_de_uso_de_reduce_concatenação_de_uma_list_em_uma_string&amp;quot;&amp;gt;Exemplo de uso de reduce: concatenação de uma list em uma string&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;tasks.register(&amp;quot;printWebappSrc&amp;quot;) {
    description = &amp;quot;print webapp sources&amp;quot;
    group = WEBAPP
    doLast {
        webAppSrc
            .reduce { acc, s -&amp;amp;gt; &amp;quot;$acc\n\t$s&amp;quot; }
            .run { println(&amp;quot;$WEBAPP_SRC: $this\n&amp;quot;) }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_recuperar_a_descrição_de_uma_tarefa_em_particular&amp;quot;&amp;gt;Recuperar a descrição de uma tarefa em particular&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./gradlew -q help --task foo&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_para_lançar_uma_tarefa_a_partir_do_workingdir_root_então_prefixar_a_tarefa_não_um&amp;quot;&amp;gt;Para lançar uma tarefa a partir do workingDir root, então prefixar a tarefa não um `:&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./gradlew :foo&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_para_reiniciar_as_tarefas_que_falharam_usar_a_opção_rerun_tasks&amp;quot;&amp;gt;Para reiniciar as tarefas que falharam, usar a opção `--rerun-tasks&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./gradlew check --rerun-tasks&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_lançar_uma_tarefa_em_um_projeto_específico_a_partir_de_outro_diretório&amp;quot;&amp;gt;Lançar uma tarefa em um projeto específico a partir de outro diretório&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A opção`-p` (ou &amp;lt;code&amp;gt;--project-dir&amp;lt;/code&amp;gt;) permite especificar o caminho de acesso a um projeto Gradle e executar uma tarefa nele, mesmo que o diretório atual seja diferente.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Por exemplo, para lançar a tarefa`build`do projeto situado em`~/projets/mon-autre-projet`enquanto estiver em outro diretório :&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;./gradlew -p ~/projets/mon-autre-projet build&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Isso é particularmente útil para scriptar ações em vários projetos a partir de um local central.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;ver também :&amp;lt;/div&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.gradle.org/current/userguide/command_line_interface.html#sec:command_line_warnings&amp;quot;&amp;gt;ref&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.gradle.org/current/userguide/command_line_interface.html#sec:command_line_options&amp;quot;&amp;gt;referência&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://stackoverflow.com/a/36178581/837404&amp;quot;&amp;gt;discussão&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Treinamento - kotlin: enum, classes seladas</title>
            <link >https://cheroliv.com//blog/2022/0038_training_kotlin_enum_sealed_class_post.html</link>
            <pubDate>Tue, 17 May 2022 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2022/0038_training_kotlin_enum_sealed_class_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;as classes seladas, não são enumerações(palavra-chave:enum).&amp;lt;br&amp;gt;
Vindo do Java, você poderia ser tentado de sobrecarregar sua enumeração de aportes de funcionalidades: // code Java&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-java&amp;quot; data-lang=&amp;quot;java&amp;quot;&amp;gt;enum PizzaOrderStatus {
    ORDER_RECEIVED,
    PIZZA_BEING_MADE,
    OUT_FOR_DELIVERY,
    COMPLETED;
    public PizzaOrderStatus nextStatus() {
        switch (this) {
        case ORDER_RECEIVED: return PIZZA_BEING_MADE;
        case PIZZA_BEING_MADE: return OUT_FOR_DELIVERY;
        case OUT_FOR_DELIVERY: return COMPLETED;
        case COMPLETED:return COMPLETED;
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em vez disso, você pode usar a classe sealed:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;sealed class PizzaOrderStatus(protected val orderId: Int) {
    abstract fun nextStatus() : PizzaOrderStatus
    class OrderReceived(orderId: Int) : PizzaOrderStatus(orderId) {
        override fun nextStatus(): PizzaOrderStatus {
            return PizzaBeingMade(orderId)
        }
    }
    class PizzaBeingMade(orderId: Int) : PizzaOrderStatus(orderId) {
        override fun nextStatus(): PizzaOrderStatus {
            return OutForDelivery(orderId)
        }
    }
    class OutForDelivery(orderId: Int) : PizzaOrderStatus(orderId) {
        override fun nextStatus(): PizzaOrderStatus {
            return Completed(orderId)
        }
    }
    class Completed(orderId: Int) : PizzaOrderStatus(orderId) {
        override fun nextStatus(): PizzaOrderStatus {
            return this
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A vantagem desta abordagem é que agora podemos transmitir dados com o status:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;var status: PizzaOrderStatus = OrderReceived(123)
while (status !is Completed) {
    status = when (status) {
        is OrderReceived -&amp;amp;gt; status.nextStatus()
        is PizzaBeingMade -&amp;amp;gt; status.nextStatus()
        is OutForDelivery -&amp;amp;gt; status.nextStatus()
        is Completed -&amp;amp;gt; status
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em geral, as classes seladas são boas se você quiser ter dados associados a um Estado.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Treinamento - Kotlin</title>
            <link >https://cheroliv.com//blog/2022/0036_training_kotlin_post.html</link>
            <pubDate>Thu, 12 May 2022 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2022/0036_training_kotlin_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_mapa_mental&amp;quot;&amp;gt;mapa mental&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;span class=&amp;quot;image&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;../../diagram/training_kotlin.png&amp;quot; alt=&amp;quot;training_kotlin&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_programação_funcional&amp;quot;&amp;gt;Programação funcional&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Os exercícios são tirados do livro:&amp;lt;br&amp;gt;
Programação funcional em Kotlin por tutoriais&amp;lt;br&amp;gt;
Escrito por Massimo Carli&amp;lt;br&amp;gt;
repositório: &amp;lt;a href=&amp;quot;https://github.com/kodecocodes/fpk-materials&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://github.com/kodecocodes/fpk-materials&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_sintaxe&amp;quot;&amp;gt;sintaxe&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_firstprogramtest&amp;quot;&amp;gt;FirstProgramTest&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;noções: exibição de tela, função&amp;lt;br&amp;gt;
FirstProgramTest:https://github.com/cheroliv/cheroliv.com/blob/master/codes/src/test/kotlin/programming/FirstProgramTest.kt[source]+&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_concatfunctiontest&amp;quot;&amp;gt;ConcatFunctionTest&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;noções: memória, variável, valor, objeto, extensão de função&amp;lt;br&amp;gt;
ExampleUnitTest: &amp;lt;a href=&amp;quot;https://github.com/cheroliv/cheroliv.com/blob/master/codes/src/test/kotlin/programming/ConcatFunctionTest.kt&amp;quot;&amp;gt;fonte&amp;lt;/a&amp;gt;+&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_birthdaymessagetestoutput&amp;quot;&amp;gt;BirthdayMessageTestOutput&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;noções: conjuntos, laços&amp;lt;br&amp;gt;
&amp;lt;a href=&amp;quot;https://developer.android.com/codelabs/basic-android-kotlin-training-first-kotlin-program?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fandroid-basics-kotlin-one%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fbasic-android-kotlin-training-first-kotlin-program&amp;quot;&amp;gt;Introdução ao Kotlin&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;
BirthdayMessageTestOutput:https://github.com/cheroliv/cheroliv.com/blob/master/codes/src/test/kotlin/programming/BirthdayMessageTestOutput.kt[fonte]&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_curso_de_kotlin&amp;quot;&amp;gt;Curso de kotlin&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;videoblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;iframe src=&amp;quot;https://www.youtube.com/embed/YRjY3jRrQYY?rel=0&amp;quot; frameborder=&amp;quot;0&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_tutotiel_sobre_as_coleções_em_java&amp;quot;&amp;gt;Tutotiel sobre as coleções em Java&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://docs.oracle.com/javase/tutorial/collections/index.html&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener&amp;quot;&amp;gt;Coleções&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O coração das interfaces de coleção&amp;lt;br&amp;gt;
&amp;lt;span class=&amp;quot;image&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;../../img/0036_training_kotlin_post/colls-coreInterfaces.gif&amp;quot; alt=&amp;quot;coleções&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_básicos_funcionais&amp;quot;&amp;gt;Básicos funcionais&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_abordagem_declarativa_x_imperativa&amp;quot;&amp;gt;Abordagem declarativa X imperativa&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;package functional

import kotlin.test.Test
import kotlin.test.assertEquals

class DeclarativeTests {
    val input = listOf(
        &amp;quot;123&amp;quot;, &amp;quot;abc&amp;quot;, &amp;quot;1ds&amp;quot;, &amp;quot;987&amp;quot;, &amp;quot;abdf&amp;quot;, &amp;quot;1d3&amp;quot;, &amp;quot;de1&amp;quot;, &amp;quot;88&amp;quot;, &amp;quot;101&amp;quot;
    )

    fun imperativeSum(list: List&amp;amp;lt;String&amp;amp;gt;): Int {
        var sum = 0
        for (item in list) {
            try {
                sum += item.toInt()
            } catch (_: NumberFormatException) {
            }
        }
        return sum
    }

    @Test
    fun `test imperative approach`() {
        imperativeSum(input).run {
            println(&amp;quot;Sum $this&amp;quot;)
            assertEquals(1299, this)
        }
    }

    fun isValidNumber(s: String) = try {
        s.toInt()
        true
    } catch (_: NumberFormatException) {
        false
    }

    fun declarativeSum(list: List&amp;amp;lt;String&amp;amp;gt;) = list
        .filter(::isValidNumber)
        .map(String::toInt)
        .sum()

    @Test
    fun `test declarative approach`() {
        assertEquals(1299, declarativeSum(input).apply {
            println(&amp;quot;Sum $this&amp;quot;)
        })
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_exercício_1_1&amp;quot;&amp;gt;Exercício 1.1&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Implementar a função sumInRange, que soma os valores em&amp;lt;br&amp;gt;
uma List&amp;amp;lt;String&amp;amp;gt; dentro de um intervalo dado. A assinatura é:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;fun sumInRange(input: List&amp;amp;lt;String&amp;amp;gt;, range: IntRange): Int&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test
fun `Exercise 1_1`() {
    assertEquals(4, sumInRange(
        listOf(&amp;quot;1&amp;quot;, &amp;quot;10&amp;quot;, &amp;quot;a&amp;quot;, &amp;quot;7&amp;quot;, &amp;quot;ad2&amp;quot;, &amp;quot;3&amp;quot;),
            1..5
        ).apply { println(&amp;quot;sumInRange 1..5: $this&amp;quot;) }
    )
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Experimente e verifique sua resposta com ahttps://github.com/cheroliv/cheroliv.com/blob/master/codes/src/test/kotlin/functional/DeclarativeTests.kt[solution, windows=&amp;quot;_blank].&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_funções_de_ordem_superior&amp;quot;&amp;gt;Funções de ordem superior&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;package functional

import java.io.ByteArrayOutputStream
import java.io.PrintStream
import java.lang.System.*
import java.lang.Thread.sleep
import kotlin.math.sign
import kotlin.test.Test
import kotlin.test.assertEquals

class BasicsHOFTests {
    val ONE_SECOND = 1000L

    @Test
    fun `high order function`() {
        //capture de la sortie standard
        val standardOut: PrintStream? = out
        val outputStreamCaptor = ByteArrayOutputStream()
        setOut(PrintStream(outputStreamCaptor))

        3.times { println(&amp;quot;Hello&amp;quot;) }
        assertEquals(
            buildString {
                repeat(3) { append(&amp;quot;Hello\n&amp;quot;) }
                deleteAt(length - 1)
            }, outputStreamCaptor
                .toString()
                .trim()
        )

        //libération de la sortie standard
        setOut(standardOut)
    }

    fun Int.times1(fn: () -&amp;amp;gt; Unit) {
        for (i in 1..this) {
            fn()
        }
    }

    fun Int.times2(fn: () -&amp;amp;gt; Unit) {
        for (i in 1..this) fn()
    }

    fun Int.times3(fn: () -&amp;amp;gt; Unit) =
        (1..this).forEach { fn() }


    fun Int.times4(fn: () -&amp;amp;gt; Unit) =
        repeat((1..this).count()) { fn() }

    fun Int.times(fn: () -&amp;amp;gt; Unit) =
        (1..this).forEach { _ -&amp;amp;gt; fn() }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_exercício_1_2&amp;quot;&amp;gt;Exercício 1.2&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Implementar chrono, que aceita uma função do tipo`() &amp;amp;#8594;`&amp;lt;br&amp;gt;
Unidade na entrada e devolve o tempo gasto para executá-lo. A assinatura é :&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;fun chrono(fn : () -&amp;amp;gt; Unité) : Long&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test
fun `Exercise 1_2`() {
    val waitOneSec = { sleep(ONE_SECOND) }
    chrono(waitOneSec).apply {
        println(&amp;quot;chrono: $this&amp;quot;)
        assertEquals(1, sign)
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Experimente-o e verifique sua resposta com ahttps://github.com/cheroliv/cheroliv.com/blob/master/codes/src/test/kotlin/functional/BasicsHOFTests.kt[solução, windows=&amp;quot;_blank].&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_composição&amp;quot;&amp;gt;Composição&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;package functional

import kotlin.test.Test
import kotlin.test.assertEquals


fun double(x: Int): Int = 2 * x
fun square(x: Int): Int = x * x
fun squareAndDouble1(x: Int) = double(square(x))

infix fun &amp;amp;lt;A, B, C&amp;amp;gt; ((A) -&amp;amp;gt; B).compose(g: (B) -&amp;amp;gt; C)
        : (A) -&amp;amp;gt; C = { a -&amp;amp;gt; g(this(a)) }

class CompositionTests {
    @Test
    fun composition_impure() {
        assertEquals(200, double(square(10)))
        assertEquals(200, squareAndDouble1(10))
    }

    @Test
    fun composition_pure() {
        val squareAndDouble = ::square compose ::double
        assertEquals(200, squareAndDouble(10))
    }

}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_funções_puras_e_testabilidade&amp;quot;&amp;gt;Funções puras e testabilidade&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;package functional

import java.io.ByteArrayOutputStream
import java.io.PrintStream
import java.lang.System.out
import java.lang.System.setOut
import kotlin.test.Test
import kotlin.test.assertEquals


var count = 0

//impure car une variable global subit un effet de bord
fun impure(value: Int): Int {
    count++
    return value + count
}

//impure car utilisation de la sortie standard qui fait muter le system
fun addOneAndLog(x: Int): Int {
    val result = x + 1
    println(&amp;quot;New Value is $result&amp;quot;)
    return result
}

//pure
fun addOne(x: Int) = (x + 1).run {
    Pair(this, &amp;quot;New Value is $this&amp;quot;)
}

class PureTests {
    @Test
    fun `impure fonction`() {
        assertEquals(3, impure(2))

        val standardOut = out
        val outputStreamCaptor = ByteArrayOutputStream()
        setOut(PrintStream(outputStreamCaptor))

        addOneAndLog(3)

        assertEquals(
            &amp;quot;New Value is 4&amp;quot;,
            outputStreamCaptor
                .toString()
                .trim()
        )
        setOut(standardOut)
    }

    @Test
    fun `pure fonction`() {
        addOne(3).run {
            assertEquals(4, first)
            assertEquals(&amp;quot;New Value is 4&amp;quot;, second)
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_tratamento_de_exceções&amp;quot;&amp;gt;Tratamento de exceções&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;package functional

import org.junit.jupiter.api.assertThrows
import kotlin.Result.Companion.failure
import kotlin.Result.Companion.success
import kotlin.test.Test
import kotlin.test.assertEquals

//NumberFormatException est un effet de bord qui rend la fonction impure
fun strToInt(str: String) = str.toInt()

//pure
fun strToIntOrNull(str: String) = try {
    str.toInt()
} catch (nfe: NumberFormatException) {
    null
}

//pure avec gestion de l&amp;#39;exception plus élégante
fun strToIntResult(str: String): Result&amp;amp;lt;Int&amp;amp;gt; =
    try {
        success(str.toInt())
    } catch (nfe: NumberFormatException) {
        failure(nfe)
    }

class ExceptionHandlingTests {
    @Test
    fun impure() {
        assertThrows&amp;amp;lt;NumberFormatException&amp;amp;gt; { strToInt(&amp;quot;foo&amp;quot;) }
        assertEquals(1, strToInt(&amp;quot;1&amp;quot;))
    }

    @Test
    fun pure() {
        assertEquals(null, strToIntOrNull(&amp;quot;foo&amp;quot;))
        assertEquals(1, strToIntOrNull(&amp;quot;1&amp;quot;))
    }

    @Test
    fun `pure avec result`() {
        assertEquals(1, strToIntResult(&amp;quot;1&amp;quot;).getOrNull())
        assertEquals(
            &amp;quot;For input string: \&amp;quot;foo\&amp;quot;&amp;quot;,
            strToIntResult(&amp;quot;foo&amp;quot;)
                .exceptionOrNull()
                ?.message
        )
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_pontos_chave&amp;quot;&amp;gt;Pontos-chave&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Enquanto a programação orientada a objetos significa programar com objetos,&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;programação funcional significa programar com funções. Você decompõe um problema em vários subproblemas, que você modela com as funções&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;As funções de ordem superior aceitam outras funções como entrada ou retornam outras&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;funcionam como valores de retorno. A teoria das categorias é a teoria da composição, e você a usa para entender como compor suas funções em um programa de trabalho. O valor de saída de uma função pura depende apenas dos seus parâmetros de entrada, e ela não tem efeitos colaterais&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Um efeito colateral é algo que uma função faz no mundo exterior. Este&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;pode ser um log na saída padrão ou a modificação do valor de uma variável global. A programação funcional funciona para as funções puras, mas ela fornece também os ferramentas para transformar funções impuras em funções puras. Você pode tornar uma função impura pura movendo os efeitos para torná-los parte do valor de retorno.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;Programação funcional é uma questão de composição.&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Gerenciamento de erros é um caso típico de efeitos colaterais, e Kotlin lhe fornece as ferramentas para gerenciá-los de forma funcional&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_aprofundar_java8_lambdas_expressões_e_interface_funcionais&amp;quot;&amp;gt;Aprofundar java8: lambdas expressões e interface funcionais&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;videoblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;iframe src=&amp;quot;https://www.youtube.com/embed/20waNRw6wMA?rel=0&amp;amp;amp;list=PLzzeuFUy_Cng0wZhqbnkvWAW0d2fdVfyQ&amp;quot; frameborder=&amp;quot;0&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_teoria_das_categorias&amp;quot;&amp;gt;Teoria das categorias&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;A teoria matemática das categorias:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;videoblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;iframe src=&amp;quot;https://www.youtube.com/embed/LVHoROSF3KA?rel=0&amp;quot; frameborder=&amp;quot;0&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_fundamentos_das_funções&amp;quot;&amp;gt;Fundamentos das funções&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_exercício_2_1&amp;quot;&amp;gt;Exercício 2.1&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Pode escrever um exemplo de função mapeando valores distintos?&amp;lt;br&amp;gt;
cujo domínio assume valores não distintos no intervalo, como f(b) e f(c) na figura abaixo ?&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Experimente, depois verifique o projeto do desafio para uma solução para ver como você fez.&amp;lt;br&amp;gt;
Você encontrará conselhos e uma explicação seguindo o link para a &amp;lt;a href=&amp;quot;https://github.com/cheroliv/cheroliv.com/blob/master/codes/src/test/kotlin/functional/BasicsHOFTests.kt&amp;quot;&amp;gt;solução&amp;lt;/a&amp;gt;.&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_exercício_2_2&amp;quot;&amp;gt;Exercício 2.2&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Você pode escrever a função inversa de duas vezes ?&amp;lt;br&amp;gt;
Quais são o domínio e a imagem da função inversa?&amp;lt;br&amp;gt;
Confira o projeto do desafio e o Apêndice B para a solução.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;fun chrono(fn : () -&amp;amp;gt; Unité) : Long&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;@Test
fun `Exercise 1_2`() {
    val waitOneSec = { sleep(ONE_SECOND) }
    chrono(waitOneSec).apply {
        println(&amp;quot;chrono: $this&amp;quot;)
        assertEquals(1, sign)
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Experimente-o e verifique sua resposta com ahttps://github.com/cheroliv/cheroliv.com/blob/master/codes/src/test/kotlin/functional/BasicsHOFTests.kt[solução, windows=&amp;quot;_blank].&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Memo design system</title>
            <link >https://cheroliv.com//blog/2022/0031_memo_design_system_post.html</link>
            <pubDate>Sun, 9 Jan 2022 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2022/0031_memo_design_system_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_ferramentas&amp;quot;&amp;gt;Ferramentas&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Ilustrações&amp;lt;/strong&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://undraw.co/&amp;quot;&amp;gt;undraw.co&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Ilustrações de código aberto para todas as ideias que você pode imaginar e criar.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://absurd.design/&amp;quot;&amp;gt;absurd.design&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Ilustrações absurdas que fazem sentido&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://drawkit.com/&amp;quot;&amp;gt;drawkit.com&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Ilustrações gratuitas magníficas. Atualizações semanais.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://icons8.com/&amp;quot;&amp;gt;icons8.com&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Ícones, ilustrações, fotos, música e ferramentas de design.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://blush.design/&amp;quot;&amp;gt;rubor.design&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Ícones, ilustrações, fotos, música e ferramentas de design&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://www.humaaans.com/&amp;quot;&amp;gt;humaaans.com&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Misture e combine ilustrações de pessoas com uma biblioteca de design.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;cores&amp;lt;/strong&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://coolors.co/&amp;quot;&amp;gt;coolors.co&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;O gerador de esquemas de cores ultra-rápido!&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Tendências&amp;lt;/strong&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://fr.pinterest.com&amp;quot;&amp;gt;pinterest&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Descoberta visual de ideias: imagens, vídeos, coleções temáticas.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://dribbble.com&amp;quot;&amp;gt;Drible&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Plataforma para designers que compartilham suas criações e interagem.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;UX/UI e Design Systems&amp;lt;/strong&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://material.io/&amp;quot;&amp;gt;Google Material Design&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Sistema de design do Google para interfaces de usuário unificadas e adaptáveis.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://developer.apple.com/design/human-interface-guidelines/&amp;quot;&amp;gt;Diretrizes de Interface Humana da Apple&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Recomendações da Apple para projetar aplicações iOS e macOS intuitivas.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://www.uxpin.com/&amp;quot;&amp;gt;UXPin&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Plataforma de design e de colaboração para criar protótipos e sistemas de design.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://www.frontify.com/&amp;quot; class=&amp;quot;bare&amp;quot;&amp;gt;https://www.frontify.com/&amp;lt;/a&amp;gt;[&amp;amp;lt;/think&amp;amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;Frontify]&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Plataforma de gestão de marca e de sistemas de design para garantir a consistência.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://designsystemsrepo.com/design-systems/&amp;quot;&amp;gt;Repositório de Sistemas de Design&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Coleção de recursos e exemplos de design systems para inspiração.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Desenvolvimento Frontend&amp;lt;/strong&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://htmlcheatsheet.com&amp;quot; target=&amp;quot;&amp;quot;_blank&amp;quot;&amp;gt;htmlcheatsheet.com&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Folha de dicas HTML&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://fontawesome.com&amp;quot;&amp;gt;fontawesome.com&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;biblioteca de ícones&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://www.toptal.com/designers/htmlarrows/symbols/&amp;quot; target=&amp;quot;&amp;quot;_blank&amp;quot;&amp;gt;toptal.com/designers/htmlarrows/symbols&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Ícones nativos em HTML&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://css-tricks.com/&amp;quot;&amp;gt;CSS-Tricks&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Essencial para CSS, com artigos, tutoriais e dicas frontend modernas.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://www.frontendmentor.io/&amp;quot;&amp;gt;Frontend Mentor&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Desafios de código frontend baseados em projetos reais para melhorar suas habilidades.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://dev.to/&amp;quot;&amp;gt;Dev.to&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Comunidade de desenvolvedores para compartilhar artigos, tutoriais e projetos.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://codepen.io/&amp;quot;&amp;gt;CodePen&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Editor de código online e comunidade para compartilhar trechos de código e demonstrações.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Desenvolvimento Frontend auxiliado por IA&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;dlist&amp;quot;&amp;gt;
&amp;lt;dl&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://v0.dev/&amp;quot;&amp;gt;v0.dev&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;v0 converte descrições em linguagem natural para código e interface do usuário.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://uizard.io/&amp;quot;&amp;gt;Uizard&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Uizard concentra-se na transformação de maquetas (imagens, esboços) em código HTML, CSS e React.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://www.usegalileo.ai/explore&amp;quot;&amp;gt;usegalileo.ai&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;Galileo AI permite que você descreva a interface que deseja em linguagem natural, e a IA gera designs Figma editáveis.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;dt class=&amp;quot;hdlist1&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://play.teleporthq.io/&amp;quot;&amp;gt;TeleportHQ&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;
&amp;lt;dd&amp;gt;
&amp;lt;p&amp;gt;TeleportHQ é uma plataforma de desenvolvimento front-end low-code que utiliza a IA para ajudá-lo a construir e implantar sites web visualmente.&amp;lt;/p&amp;gt;
&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Kubernetes, Microk8s, Docker: primeiros passos</title>
            <link >https://cheroliv.com//blog/2020/0029_kubernetes_microk8s_docker_premiers_pas_post.html</link>
            <pubDate>Tue, 3 Nov 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0029_kubernetes_microk8s_docker_premiers_pas_post.html</guid>
            <description>&amp;lt;div id=&amp;quot;preamble&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://microk8s.io/docs/&amp;quot;&amp;gt;documento oficial do microk8s&amp;lt;/a&amp;gt;+&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_instalação_do_pacote_microk8s&amp;quot;&amp;gt;Instalação do pacote microk8s:&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Gradle: Executar um processo externo de uma tarefa gradle</title>
            <link >https://cheroliv.com//blog/2020/0028_gradle_executer_un_processus_externe_depuis_une_tache_gradle_post.html</link>
            <pubDate>Mon, 28 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0028_gradle_executer_un_processus_externe_depuis_une_tache_gradle_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_gradle_kotlin_dsl&amp;quot;&amp;gt;Gradle-Kotlin-DSL:&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-kotlin&amp;quot; data-lang=&amp;quot;kotlin&amp;quot;&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Kotlin: NotImplementedException, NotImplementedError</title>
            <link >https://cheroliv.com//blog/2020/0027_kotlin_not_implemented_exception_not_implemented_error_post.html</link>
            <pubDate>Sun, 27 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0027_kotlin_not_implemented_exception_not_implemented_error_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;NotImplementedException NotImplementedError, utilidade para os testes, permite saber onde estamos, e a causa da falha.&amp;lt;br&amp;gt;
Ainda não implementado, permite lançar a exceção correta.&amp;lt;br&amp;gt;
Il y a :&amp;lt;br&amp;gt;
&amp;lt;a href=&amp;quot;http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/NotImplementedException.html&amp;quot;&amp;gt;org.apache.commons.lang3.NotImplementedException&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;a href=&amp;quot;https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-not-implemented-error/&amp;quot;&amp;gt;kotlin.NotImplementedError&amp;lt;/a&amp;gt;+&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Untar</title>
            <link >https://cheroliv.com//blog/2020/0026_untar_post.html</link>
            <pubDate>Sat, 26 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0026_untar_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_links_interserver_net&amp;quot;&amp;gt;Links: &amp;lt;a href=&amp;quot;https://www.interserver.net/tips/kb/extract-tar-gz-files-using-linux-command-line/&amp;quot;&amp;gt;interserver.net&amp;lt;/a&amp;gt;&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Gradle: Reexecutar os testes mesmo quando os testes estiverem atualizados</title>
            <link >https://cheroliv.com//blog/2020/0025_gradle_rejouer_les_tests_meme_lorsque_les_tests_sont_UP-TO-DATE_post.html</link>
            <pubDate>Fri, 25 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0025_gradle_rejouer_les_tests_meme_lorsque_les_tests_sont_UP-TO-DATE_post.html</guid>
            <description></description>
        </item><item>
            <title>Docker, não instala a aplicação mas a puxa em vez</title>
            <link >https://cheroliv.com//blog/2020/0024_docker_n_installe_pas_l_application_mais_tire_la_plutot_post.html</link>
            <pubDate>Thu, 24 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0024_docker_n_installe_pas_l_application_mais_tire_la_plutot_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_hackernoon_com&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;https://hackernoon.com/dont-install-postgres-docker-pull-postgres-bee20e200198&amp;quot;&amp;gt;hackernoon.com&amp;lt;/a&amp;gt;&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Gherkin em português :</title>
            <link >https://cheroliv.com//blog/2020/0023_gherkin_en_francais_post.html</link>
            <pubDate>Wed, 23 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0023_gherkin_en_francais_post.html</guid>
            <description></description>
        </item><item>
            <title>Gradle cli: sobrescrever um parâmetro</title>
            <link >https://cheroliv.com//blog/2020/0022_gradle_cli_surcharger_un_parametre_post.html</link>
            <pubDate>Tue, 22 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0022_gradle_cli_surcharger_un_parametre_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code&amp;gt;----
----
$ ./gradlew -Pparam_component=CUSTOM
----
----&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Memo git</title>
            <link >https://cheroliv.com//blog/2020/0021_memo_git_post.html</link>
            <pubDate>Mon, 21 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0021_memo_git_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_sources&amp;quot;&amp;gt;Sources:&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;*https://xenovation.com/blog/source-control-management/git/how-to-change-remote-git-repository[xenovação] *https://intellipaat.com/community/3102/git-show-remote-url-how-can-i-determine-the-url-that-a-local-git-repository-was-originally-cloned-from[intellipaat]&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_mudar_o_repositório_remoto_no_git&amp;quot;&amp;gt;mudar o repositório remoto no git&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;mostrar a url do repositório remoto atual:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Aspirar de uma URL</title>
            <link >https://cheroliv.com//blog/2020/0021_aspirer_depuis_une_URL_post.html</link>
            <pubDate>Sun, 20 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0021_aspirer_depuis_une_URL_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code&amp;gt;----
----
$ wget -r -k -np http://site.com/docs/manual/
----

Para definir uma pasta de destino adicionar o argumento -P e depois dar o caminho
----&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Criar um disco bootável</title>
            <link >https://cheroliv.com//blog/2020/0020_creer_un_disk_bootable_post.html</link>
            <pubDate>Sat, 19 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0020_creer_un_disk_bootable_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_links_etcher&amp;quot;&amp;gt;Links: &amp;lt;a href=&amp;quot;https://www.balena.io/etcher/&amp;quot;&amp;gt;etcher&amp;lt;/a&amp;gt;&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Descompactar em CLI em um diretório de destino</title>
            <link >https://cheroliv.com//blog/2020/0019_dezipper_en_CLI_dans_un_dossier_cible_post.html</link>
            <pubDate>Fri, 18 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0019_dezipper_en_CLI_dans_un_dossier_cible_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_links_askubuntu&amp;quot;&amp;gt;Links: &amp;lt;a href=&amp;quot;https://askubuntu.com/questions/86849/how-to-unzip-a-zip-file-from-the-terminal&amp;quot;&amp;gt;askubuntu&amp;lt;/a&amp;gt;&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Se quiser extrair para uma pasta de destino específica, você pode utilizar: &amp;lt;code&amp;gt;` $ unzip arquivo.zip -d pasta_destino &amp;lt;/code&amp;gt;` Se os diretórios de origem e de destino forem idênticos, você pode simplesmente fazer: &amp;lt;code&amp;gt;` $ unzip file.zip &amp;lt;/code&amp;gt;`&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Exibir a soma dos tamanhos dos arquivos na lista dos diretórios</title>
            <link >https://cheroliv.com//blog/2020/0018_Afficher_la_somme_des_tailles_de_fichier_dans_la_liste_des_r%C3%A9pertoires_post.html</link>
            <pubDate>Thu, 17 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0018_Afficher_la_somme_des_tailles_de_fichier_dans_la_liste_des_r%C3%A9pertoires_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_links_stackexchange&amp;quot;&amp;gt;Links: &amp;lt;a href=&amp;quot;https://unix.stackexchange.com/questions/72661/show-sum-of-file-sizes-in-directory-listing&amp;quot;&amp;gt;stackexchange&amp;lt;/a&amp;gt;&amp;lt;/h4&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code&amp;gt;----
----
$ du -ach --exclude &amp;quot;./.*&amp;quot; /home/troll/
----
----&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Excluir uma branch no github</title>
            <link >https://cheroliv.com//blog/2020/0017_detruire_une_branch_sur_github_post.html</link>
            <pubDate>Wed, 16 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0017_detruire_une_branch_sur_github_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_apagar_uma_branch_no_github&amp;quot;&amp;gt;Apagar uma branch no github&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_link_gist&amp;quot;&amp;gt;Link: &amp;lt;a href=&amp;quot;https://gist.github.com/cheroliv/921a13ed23ae6d4d66af8d43dd536987&amp;quot;&amp;gt;gist&amp;lt;/a&amp;gt;&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Asciidoc/Markdown Memorando</title>
            <link >https://cheroliv.com//blog/2020/0016_asciidoc_markdown_memo_post.html</link>
            <pubDate>Tue, 15 Sep 2020 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2020/0016_asciidoc_markdown_memo_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Por que asciidoc ou markdown? Aqui, estou me direcionando para asciidoc, pois já tenho alguns tickets escritos em asciidoc.&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;Asciidoc é uma linguagem de marcação que permite produzir conteúdo, trazendo uma formatação que mantém o texto legível para um ser humano.&amp;lt;br&amp;gt;
É uma maneira de padronizar os tratamentos de texto para conteúdos simples sem software adicional.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;ulist&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Links :&amp;lt;/div&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://blog.oxiane.com/2018/06/13/asciidoc-documentation-as-code/&amp;quot;&amp;gt;@jmdoudoux no blog.oxiane.com&amp;lt;/a&amp;gt; [asciidoc]&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://powerman.name/doc/asciidoc&amp;quot; target=&amp;quot;&amp;quot;_blank&amp;quot;&amp;gt;powerman.name&amp;lt;/a&amp;gt;[asciidoc]&amp;lt;/p&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Debian : Instalar um arquivo .deb via linha de comando</title>
            <link >https://cheroliv.com//blog/2019/0013_how-to-install-a-deb-file_post.html</link>
            <pubDate>Mon, 19 Aug 2019 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2019/0013_how-to-install-a-deb-file_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para instalar um pacote Debian a partir de um arquivo .deb na linha de comando.&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ sudo dpkg -i /chemin/du/fichier/deb/&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;veja aqui para ahttps://unix.stackexchange.com/questions/159094/how-to-install-a-deb-file-by-dpkg-i-or-by-apt[referência]&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Gradle: pasta resources visível no IDE</title>
            <link >https://cheroliv.com//blog/2019/0011_gradle_configuration_ide_jbake_content_post.html</link>
            <pubDate>Fri, 16 Aug 2019 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2019/0011_gradle_configuration_ide_jbake_content_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Em um projeto de aplicativo conduzido pelo gerenciador de compilação Gradle&amp;lt;br&amp;gt;
é mais agradável ter visibilidade sobre todos os arquivos&amp;lt;br&amp;gt;
participante do desenvolvimento da aplicação,&amp;lt;br&amp;gt;
como pastas de recursos que não são&amp;lt;br&amp;gt;
nos caminhos por convenção.&amp;lt;br&amp;gt;
Por exemplo eu quero que a pasta src/jbake,&amp;lt;br&amp;gt;
seja visível como ‘resource folder’,&amp;lt;br&amp;gt;
é possível fazer isso entrando na configuração do projeto,&amp;lt;br&amp;gt;
nos wizards da IDE No entanto esta opção não é salva no próximo recarregamento&amp;lt;br&amp;gt;
do projeto Gradle na IDE. Para aplicar essa configuração permanentemente.&amp;lt;br&amp;gt;
a solução é sobrescrever a configuração dos caminhos de fontes&amp;lt;br&amp;gt;
no arquivo de configuração build.gradle tal como nas linhas 18, 19, 20 e 21 :&amp;lt;br&amp;gt;
build.gradle&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;plugins {
    id &amp;quot;java&amp;quot;
    id &amp;quot;groovy&amp;quot;
    id &amp;quot;org.jbake.site&amp;quot; version &amp;quot;5.0.0&amp;quot;
    id &amp;quot;org.ajoberstar.git-publish&amp;quot; version &amp;quot;2.1.1&amp;quot;
}
repositories {
    mavenCentral()
    jcenter()
}
group project_group
version project_version
sourceSets {
    main {
        java { srcDirs = [] }
        groovy { srcDirs =[&amp;quot;src/main/java&amp;quot;, &amp;quot;src/main/groovy&amp;quot;] }
        resources {
            srcDirs =[
                    &amp;quot;src/main/resources&amp;quot;,
                    &amp;quot;src/jbake&amp;quot;//in order to get site src content in the IDE
            ]
        }
    }
    test {
        java { srcDirs = [] }
        groovy { srcDirs =[&amp;quot;src/test/java&amp;quot;, &amp;quot;src/test/groovy&amp;quot;] }
    }
}
test {
    useJUnitPlatform()
}
configurations {
    ivy
}
dependencies {
    ivy &amp;quot;org.apache.ivy:ivy:$ivy_version&amp;quot;
    implementation &amp;quot;org.codehaus.groovy:groovy-all:$groovy_version&amp;quot;
    implementation &amp;quot;org.asciidoctor:asciidoctor-java-integration:$asciidoctor_java_integration_version&amp;quot;
    implementation &amp;quot;org.freemarker:freemarker:$freemarker_version&amp;quot;
    testImplementation &amp;quot;org.junit.jupiter:junit-jupiter-api:$junit5_version&amp;quot;
    testRuntimeOnly &amp;quot;org.junit.jupiter:junit-jupiter-engine:$junit5_version&amp;quot;
}
gitPublish {
    repoUri = github_pages_blog_repository
    branch = git_branch
    contents {
        from(file(jbake_content_from)) {
            into jbake_content_to
        }
    }
}
tasks.withType(GroovyCompile) {
    groovyClasspath += configurations.ivy
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Script Groovy, grab e configuração Ivy</title>
            <link >https://cheroliv.com//blog/2019/0010_gradle_groovy_script_grab_ivy_configuration_post.html</link>
            <pubDate>Wed, 14 Aug 2019 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2019/0010_gradle_groovy_script_grab_ivy_configuration_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O assunto do ticket é a configuração de um script de build Gradle.&amp;lt;br&amp;gt;
Quero poder usar um script Groovy sem fazer referência&amp;lt;br&amp;gt;
ao projeto Gradle no qual eu o anexei.&amp;lt;br&amp;gt;
Quero ter a possibilidade de executar este script Groovy fora do Gradle&amp;lt;br&amp;gt;
sem que as anotações ivy de recuperação de dependência externa&amp;lt;br&amp;gt;
não vêm causar problemas de desempenho ou de compilação&amp;lt;br&amp;gt;
do script de build do Gradle.&amp;lt;br&amp;gt;
Para ilustrar a configuração, eu me baseio em dois scripts:&amp;lt;br&amp;gt;
um script de build Gradle e um script Groovy.&amp;lt;br&amp;gt;
O script Groovy contém no início na linha 2,&amp;lt;br&amp;gt;
uma instrução para puxar uma dependência  externa&amp;lt;br&amp;gt;
usando o gerenciador de dependências ivy.&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-Groovy&amp;quot; data-lang=&amp;quot;Groovy&amp;quot;&amp;gt;#!/usr/bin/env groovy
@Grab(&amp;quot;commons-io:commons-io:2.6&amp;quot;)

import org.apache.commons.io.FileUtils

static String getSeparator() {
    System.getProperty(&amp;quot;file.separator&amp;quot;)
}

String from = &amp;quot;${System.getProperty(&amp;quot;user.home&amp;quot;)}${separator}.config${separator}transmission${separator}torrents&amp;quot;
String to = &amp;quot;${System.getProperty(&amp;quot;user.home&amp;quot;)}${separator}Documents${separator}torrents_completed&amp;quot;

File fromDlDir = new File(&amp;quot;${System.getProperty(&amp;quot;user.home&amp;quot;)}${separator}Téléchargements&amp;quot;)
File fromDir = new File(from)
File toDir = new File(to)

Collection&amp;amp;lt;File&amp;amp;gt; torrentFiles = FileUtils.listFiles(
        fromDir,
        [&amp;quot;torrent&amp;quot;] as String[],
        false)
torrentFiles.addAll(FileUtils.listFiles(
        fromDlDir,
        [&amp;quot;torrent&amp;quot;, &amp;quot;torrent.added&amp;quot;] as String[],
        false))


torrentFiles.empty ?: torrentFiles.each { it -&amp;amp;gt;
    FileUtils.copyFileToDirectory(it, toDir)
}
println torrentFiles.size()&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;No arquivo build.gradle, agora vamos ver como harmonizar&amp;lt;br&amp;gt;
os dois gerenciadores de dependências, ivy e Gradle. O acordo entre os dois se faz nas linhas:&amp;lt;br&amp;gt;
26, 30, 37, 38 et 39&amp;lt;br&amp;gt;
Assim posso executar meu script Groovy manualmente,&amp;lt;br&amp;gt;
e a compilação do Gradle não é prejudicada pelo &amp;lt;code&amp;gt;@Grab&amp;lt;/code&amp;gt;.&amp;lt;br&amp;gt;
As dependências do script Groovy não devem ser fornecidas no Gradle.&amp;lt;br&amp;gt;
Por outro lado, não consigo executar o script Groovy a partir do Gradle.&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-Groovy&amp;quot; data-lang=&amp;quot;Groovy&amp;quot;&amp;gt;plugins {
    id &amp;quot;java&amp;quot;
    id &amp;quot;groovy&amp;quot;
}

repositories {
    mavenCentral()
    jcenter()
}

sourceSets {
    main {
        java { srcDirs = [] }
        groovy { srcDirs =[&amp;quot;src/main/java&amp;quot;, &amp;quot;src/main/groovy&amp;quot;] }
        resources {
            srcDirs =[&amp;quot;src/main/resources&amp;quot;]
        }
    }
    test {
        java { srcDirs = [] }
        groovy { srcDirs =[&amp;quot;src/test/java&amp;quot;, &amp;quot;src/test/groovy&amp;quot;] }
    }
}

configurations {
    ivy
}

dependencies {
    ivy &amp;quot;org.apache.ivy:ivy:$ivy_version&amp;quot;
    implementation &amp;quot;org.codehaus.groovy:groovy-all:$groovy_version&amp;quot;
}



tasks.withType(GroovyCompile) {
    groovyClasspath += configurations.ivy
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;referência do ticket sobrehttps://stackoverflow.com/a/18174033/837404[stackoverflow]&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Groovy: um pouco de álgebra, o mdc e o mmc</title>
            <link >https://cheroliv.com//blog/2019/0009_groovy_algebre_pgcd_ppmc_post.html</link>
            <pubDate>Tue, 13 Aug 2019 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2019/0009_groovy_algebre_pgcd_ppmc_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_o_ppmc&amp;quot;&amp;gt;O PPMC&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://fr.wikipedia.org/wiki/Plus_petit_commun_multiple&amp;quot;&amp;gt;wiki PPMC ou Mínimo Múltiplo Comum&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;AlgebraUtils.groovy&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;package com.cheroliv.misc

import groovy.transform.CompileStatic

@CompileStatic
class AlgebraUtils {

    /**
     * Great common divisor
     * Plus grand commun diviseur(pgcd)
     * Great Common Divisor
     * @param a
     * @param b
     * @return
     */
    static Integer gcd(Integer a, Integer b) {
        !b ? a : gcd(b, a % b)
    }


    /**
     * Least common multiple
     * Plus petit commun multiplicateur(ppmc)
     * @param a
     * @param b
     * @return
     */
    static BigInteger lcm(Integer a, Integer b) {
        (a * b / gcd(a, b)) as BigInteger
    }

    static void main(String... args) {
        Integer a = 96
        Integer b = 28
        println &amp;quot;gcd($a, $b) = ${gcd(a, b)}&amp;quot;
        a = 790
        b = 990
        println &amp;quot;lcm($a, $b) = ${lcm(a, b)}&amp;quot;
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;resultado:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;mdc(96, 28) = 4 mmc(790, 990) = 78210&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Bash: Encontrar arquivos por extensão saída em arquivo com find</title>
            <link >https://cheroliv.com//blog/2019/0008_bash_find_trouver_fichiers_par_extension_post.html</link>
            <pubDate>Mon, 12 Aug 2019 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2019/0008_bash_find_trouver_fichiers_par_extension_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Estou procurando arquivos por critérios de nome: a lista dos arquivos que têm a extensão .h e .cpp&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ find . -name &amp;#39;*.h&amp;#39; -o -name &amp;#39;*.cpp&amp;#39; + +&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;a lista dos arquivos que têm como extensão .h e .cpp&amp;lt;br&amp;gt;
o resultado é salvo no arquivo list_of_txt_files.list&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ find . -name &amp;#39;*.h&amp;#39; -o -name &amp;#39;*.cpp&amp;#39; -print &amp;amp;gt; list_of_txt_files.list&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Estou procurando a lista dos arquivos cuja extensão é .chm&amp;lt;br&amp;gt;
E eu destrui esta lista.&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ find . -name &amp;quot;*.chm&amp;quot; -type f -delete&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Gerir um projeto digital com uma metodologia em cascata</title>
            <link >https://cheroliv.com//blog/2019/0015_projet_avec_methodologie_en_cascade_post.html</link>
            <pubDate>Thu, 1 Aug 2019 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2019/0015_projet_avec_methodologie_en_cascade_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_analisar_e_recolher_as_necessidades&amp;quot;&amp;gt;Analisar e recolher as necessidades&amp;lt;/h3&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_definir_a_gestão_de_projetos&amp;quot;&amp;gt;Definir a gestão de projetos&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;../../diagram/definir_projet.png&amp;quot; alt=&amp;quot;definir la gestion de projet&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;../../img/triangle_qcd.png&amp;quot; alt=&amp;quot;triangle_qcd&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_contextualizar_o_projeto&amp;quot;&amp;gt;Contextualizar o projeto&amp;lt;/h3&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_analisar_e_recolher_necessidades&amp;quot;&amp;gt;Analisar e recolher necessidades&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;sect3&amp;quot;&amp;gt;
&amp;lt;h4 id=&amp;quot;_formular_os_objetivos_e_entregáveis&amp;quot;&amp;gt;Formular os objetivos e entregáveis&amp;lt;/h4&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_decifrar_uma_grade_de_leitura&amp;quot;&amp;gt;Decifrar uma grade de leitura&amp;lt;/h3&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect1&amp;quot;&amp;gt;
&amp;lt;h2 id=&amp;quot;_defina_o_projeto_com_a_sua_equipe&amp;quot;&amp;gt;Defina o projeto com a sua equipe&amp;lt;/h2&amp;gt;
&amp;lt;div class=&amp;quot;sectionbody&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;../../img/conductor.png&amp;quot; alt=&amp;quot;chef d&amp;amp;#8217;orchestre&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_consultar_os_especialistas_para_encontrar_a_solução&amp;quot;&amp;gt;Consultar os especialistas para encontrar a solução&amp;lt;/h3&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;sect2&amp;quot;&amp;gt;
&amp;lt;h3 id=&amp;quot;_escolher_uma_metodologia_de_gestão_de_projeto&amp;quot;&amp;gt;Escolher uma metodologia de gestão de projeto&amp;lt;/h3&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;../../img/cascading_planning.png&amp;quot; alt=&amp;quot;planning en cascade&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;imageblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;../../img/Cycle_V_details.jpeg&amp;quot; alt=&amp;quot;cycle en V&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Listar os bancos de dados em uma instância do PostgreSQL</title>
            <link >https://cheroliv.com//blog/2019/0014_list_all_databases_in_postgresql_post.html</link>
            <pubDate>Tue, 30 Jul 2019 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2019/0014_list_all_databases_in_postgresql_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Não tenho vontade de instalarhttps://www.pgadmin.org/[pgAdmin] ou &amp;lt;a href=&amp;quot;http://phppgadmin.sourceforge.net/&amp;quot;&amp;gt;phppgadmin&amp;lt;/a&amp;gt; enquanto tudo está presente na linha de comando.&amp;lt;br&amp;gt;
Para isso devemos lançar o terminal e conectar como o usuário postgres.&amp;lt;br&amp;gt;
O usuário postgres é o único a ter por padrão o direito de executar a aplicação psql, que é o cliente de linha de comando. (&amp;lt;a href=&amp;quot;https://fr.wikipedia.org/wiki/Interface_en_ligne_de_commande&amp;quot;&amp;gt;CLI&amp;lt;/a&amp;gt;)postgresql,&amp;lt;br&amp;gt;
para poder falar com o banco de dados na linha de comando a partir do terminal&amp;lt;br&amp;gt;
Para se conectar, abrir o terminal e digitar isto:&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ sudo -i -u postgres&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para se conectar e chegar diretamente ao prompt de comando do postgresql, digite isso:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ sudo -i -u postgres psql&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Para listar os bancos de dados de uma instância de postgresql, aqui está o comando :&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-SQL&amp;quot; data-lang=&amp;quot;SQL&amp;quot;&amp;gt;postgres=# \l&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;resultado:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Lista das bases de dados Nome    | Proprietário | Codificação | Colação | Tipo caract. |    Direitos de acesso&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;fiber     | tech         | UTF8     | fr_FR.UTF-8     | fr_FR.UTF-8  | =Tc/tech&amp;lt;br&amp;gt;
|              |          |                 |              | tech=CTc/tech fiberdev  | tech         | UTF8     | fr_FR.UTF-8     | fr_FR.UTF-8  | =Tc/tech&amp;lt;br&amp;gt;
|              |          |                 |              | tech=CTc/tech fiberweb  | tech         | UTF8     | fr_FR.UTF-8     | fr_FR.UTF-8  | =Tc/tech&amp;lt;br&amp;gt;
|              |          |                 |              | tech=CTc/tech postgres  | postgres     | UTF8     | fr_FR.UTF-8     | fr_FR.UTF-8  | template0 | postgres     | UTF8     | fr_FR.UTF-8     | fr_FR.UTF-8  | =c/postgres&amp;lt;br&amp;gt;
|              |          |                 |              | postgres=CTc/postgres template1 | postgres     | UTF8     | fr_FR.UTF-8     | fr_FR.UTF-8  | =c/postgres&amp;lt;br&amp;gt;
|              |          |                 |              | postgres=CTc/postgres (6 linhas)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;digite q para sair da exibição dos resultados&amp;lt;br&amp;gt;
&amp;lt;br&amp;gt;
para listar os esquemas de uma instância de postgresl.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-sql&amp;quot; data-lang=&amp;quot;sql&amp;quot;&amp;gt;postgres=# SELECT schema_name FROM information_schema.schemata;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;resultado&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;público information_schema pg_catalog pg_toast_temp_1 pg_temp_1 pg_toast (6 linhas)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;ou&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-sql&amp;quot; data-lang=&amp;quot;sql&amp;quot;&amp;gt;postgres=# SELECT nspname FROM pg_catalog.pg_namespace;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;resultado&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;pg_toast pg_temp_1 pg_toast_temp_1 pg_catalog information_schema público (6 linhas)&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Groovy: Renomear arquivos recursivamente</title>
            <link >https://cheroliv.com//blog/2019/0007_groovy_rename_recursively_files_post.html</link>
            <pubDate>Mon, 29 Jul 2019 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2019/0007_groovy_rename_recursively_files_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Meu reprodutor de vídeo na minha TV tem seu jeito de passar&amp;lt;br&amp;gt;
de um ficheiro para o outro em leitura contínua&amp;lt;br&amp;gt;
e seu modo de reprodução é de passar de 1&amp;amp;#8230;&amp;amp;#8203;..mp4 para 10&amp;amp;#8230;&amp;amp;#8203;.mp4,&amp;lt;br&amp;gt;
e não de 1&amp;amp;#8230;&amp;amp;#8203;mp4 a 2&amp;amp;#8230;&amp;amp;#8203;.mp4.&amp;lt;br&amp;gt;
O problema é que há quase 900 ficheiros e pastas.&amp;lt;br&amp;gt;
Bingo um super caso de uso do Groovy para manipular&amp;lt;br&amp;gt;
de arquivos, renomeação, pesquisa de padrão no nome do arquivo&amp;lt;br&amp;gt;
ou pasta&amp;lt;br&amp;gt;
Aqui o objetivo é mudar os nomes que começam por&amp;lt;br&amp;gt;
um número e cujo segundo caractere do nome&amp;lt;br&amp;gt;
não é um dígito, acrescentarei um 0 no início deste nome&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;renameFilesToStartWithTwoDigit.groovy&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;#!/usr/bin/env groovy
/**
 * get a crossplateform separator
 * @return
 */
static String getSeparator() {
    System.getProperty(&amp;quot;file.separator&amp;quot;)
}
/**
 * rename file
 * @param file
 * @param newName
 * @return the renamed file
 */
static File rename(File file, String newName) {
    assert file.renameTo(file
            .parentFile.path +
            getSeparator() +
            newName)
    new File(file
            .parentFile.path +
            getSeparator() +
            newName)
}

String rootFolderPath = System.getProperty(&amp;quot;user.home&amp;quot;) +
        &amp;quot;/Bureau/ng-courses/Udemy - Angular 7 &amp;quot; +
        &amp;quot;(formerly Angular 2) - The Complete Guide&amp;quot;

File rootFolder = new File(rootFolderPath)
assert rootFolder.exists()
assert rootFolder.directory
assert rootFolder.canRead()

List&amp;amp;lt;File&amp;amp;gt; dirList = new ArrayList&amp;amp;lt;&amp;amp;gt;()

//Adding rootFolder in dirList
// just in case there is some files in it
dirList.add(rootFolder)

//looping to get list of directories
//recursively inside rootFolder
rootFolder.traverse { File file -&amp;amp;gt;
    !file.directory ?: dirList.add(file)
}

//looping through dirList
//to rename directories filling criterias
for (int i = dirList.size() - 1; i &amp;amp;gt;= 0; i--) {
    Character[] chars = dirList.get(i).name.chars
    !(chars[0].digit &amp;amp;amp;&amp;amp;amp; !chars[1].digit) ?:
            dirList.set(i, rename(dirList.get(i),
                    &amp;quot;0${dirList.get(i).name}&amp;quot;))
    if (dirList.get(i).directory) {
        //looping through directory
        //to rename file filling criterias
        dirList.get(i).listFiles().each { File f -&amp;amp;gt;
            Character[] cs = f.name.chars
            !(cs[0].digit &amp;amp;amp;&amp;amp;amp; !cs[1].digit) ?:
                    rename(f, &amp;quot;0${f.name}&amp;quot;)
        }
    }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Groovy: Pesquisa de arquivos com critério, cópia da seleção</title>
            <link >https://cheroliv.com//blog/2019/0006_groovy_find_files_copy_to_post.html</link>
            <pubDate>Fri, 19 Jul 2019 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2019/0006_groovy_find_files_copy_to_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;O objetivo deste script é procurar arquivos,&amp;lt;br&amp;gt;
a partir de um diretório-pai; recuperar a seleção&amp;lt;br&amp;gt;
em uma lista. Finalmente copiar esta lista de arquivos&amp;lt;br&amp;gt;
em um diretório de destino isso em Groovy.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;findCopyTo.groovy&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;#!/usr/bin/env groovy
@Grab(&amp;quot;commons-io:commons-io:2.6&amp;quot;)

import groovy.io.FileType
import org.apache.commons.io.FileUtils

/**
 * un separateur OS independant
 * @return
 */
static String getSep() {
    System.getProperty(&amp;quot;file.separator&amp;quot;)
}
/**
 * la liste des fichiers dont le titre contient un item
 * de la liste des motifs
 *
 * @param path
 * @param motifs
 * @return la liste des chemins complet de fichiers
 */
static List&amp;amp;lt;String&amp;amp;gt; findFilesContainingMotifs(
        String path,
        List&amp;amp;lt;String&amp;amp;gt; motifs) {
    List&amp;amp;lt;String&amp;amp;gt; filePathResultList = new ArrayList&amp;amp;lt;&amp;amp;gt;()
    new File(path)
            .eachFileRecurse(FileType.FILES) {
                if (it.name.toLowerCase().contains(&amp;#39;cucumber&amp;#39;)) {
                    filePathResultList.add it.path
                }
            }
    filePathResultList
}

/**
 * copy vers la liste des fichiers
 * @param files
 * @param to
 */
static void copyFilesTo(List&amp;amp;lt;String&amp;amp;gt; files, String to) {
    File toDirDest = new File(to)
    files.each { String filepath -&amp;amp;gt;
        File file = new File(filepath)
        //si le dernier caractere de to n&amp;#39;est pas un separateur
        // alors ajoute le a la chaine
        (to.substring(to.length() - 1) == sep) ?: to.concat(sep)
        if (to + file.name != filepath) {
            FileUtils.copyFileToDirectory(file, toDirDest)
            file.delete()
        }
    }
}
/**
 * la list des arguments
 */
String userName = System.getProperty(&amp;quot;user.name&amp;quot;)
List&amp;amp;lt;String&amp;amp;gt; motifs = [&amp;#39;cucumber&amp;#39;]
String path = &amp;quot;/media/$userName/320/Books/&amp;quot;
String to = &amp;quot;/media/$userName/320/Books/bdd/&amp;quot;
/**
 * lappel aux methodes
 */
copyFilesTo(findFilesContainingMotifs(path, motifs), to)

//affiche moi les fichiers déplacés
findFilesContainingMotifs(path, motifs).each {
    println it
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Groovy: Manipulação de arquivos, pesquisa e exclusão</title>
            <link >https://cheroliv.com//blog/2019/0005_groovy_manipulation_files_post.html</link>
            <pubDate>Thu, 18 Jul 2019 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2019/0005_groovy_manipulation_files_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui vou me interessar pelo carregamento de uma propriedade dentro de um build proyectohttps://docs.gradle.org/current/userguide/userguide.html[gradle]&amp;lt;br&amp;gt;
sem que ela esteja disponível no código.&amp;lt;br&amp;gt;
A propriedade será definida no arquivo gradle.properties do diretório ~/.gradle/gradle.properties&amp;lt;br&amp;gt;
~ é um atalho da variável simbólica HOME_PATH que representa a raiz da sessão do usuário: ohttps://fr.wikipedia.org/wiki/R%C3%A9pertoire_personnel[diretório pessoal].&amp;lt;br&amp;gt;
Da mesma forma em um projeto de buildhttps://maven.apache.org/guides/index.html[maven], com ~/.m2/settings.xml O objetivo deste script é excluir os arquivos que corresponderiam aos padrões mencionados&amp;lt;br&amp;gt;
na variável strCriteria. Para isso vou usar oshttps://fr.wikipedia.org/wiki/Fermeture_(informatique)[fechos]: cada, findAll,  coletar&amp;lt;br&amp;gt;
assim como a classe StringTokenizer que vai dividir em pedaços minha cadeia de critérios&amp;lt;br&amp;gt;
para obter uma lista de cadeias de caracteres Se um dos arquivos da minha pasta a ser limpa contém um dos padrões em seu nome&amp;lt;br&amp;gt;
então será um candidato à destruição por ser adicionado à lista de resultados.&amp;lt;br&amp;gt;
E na última linha um pequeno exemplo dehttps://en.wikipedia.org/wiki/Elvis_operator[Operador Elvis]&amp;lt;br&amp;gt;
filemanip.groovy&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;#!/usr/bin/env groovy
//Mes criteres de selection des fichiers à detruire
String strCriteria = &amp;quot;Dummies Dummie ajax Objective-C Windows blackberry &amp;quot; +
        &amp;quot;wordpress joomla ios c# f# rails ruby flex asp flash Dreamweaver adobe &amp;quot; +
        &amp;quot;CoffeeScript c++ .chm .epub .rar .zip .tar drupal drupals .net php javascript &amp;quot; +
        &amp;quot;.js iphone ipad js _js facebook gwt vaadin linq python py zope window &amp;quot; +
        &amp;quot;3D scala gaming active_directory tomcat jboss glassfish microsoft 2d &amp;quot; +
        &amp;quot;lego blogger blog cocoa dojo dom dotnetnuke eclipse .gz unity sharepoint &amp;quot; +
        &amp;quot;.tgz iwork filemaker .doc .txt&amp;quot;


//repertoire personnel
String userDir = System.getProperty(&amp;quot;user.home&amp;quot;)
//un separateur de chemin crossplatform
String sep = System.getProperty(&amp;quot;file.separator&amp;quot;)
//la variable symbolique
String sym_books_vrac_path = &amp;quot;books_vrac_path&amp;quot;

//le chemin de mon fichier gradle.properties hors projet
String propertiesFilePath = userDir
        .concat(&amp;quot;${sep}.gradle${sep}gradle.properties&amp;quot;)


File propertiesFile = new File(propertiesFilePath)


//je m&amp;#39;aassure que le fichier existe et que ce n&amp;#39;est pas un dossier
assert propertiesFile.exists() &amp;amp;amp;&amp;amp;amp; !propertiesFile.directory

Properties gradleProperties = new Properties()

//on boucle avec un inpustream sur le fichier
//gradle.properties pour peupler ma variable gradleProperties
propertiesFile.withInputStream { InputStream it -&amp;amp;gt;
    gradleProperties.load(it)
}

//le chemin du dossier books_vrac est la valeur de notre clé books_vrac_path
String pathVracDir = gradleProperties.get(sym_books_vrac_path)

//j&amp;#39;ai récuperé tous ces livres en tapant cette commande dans le terminal
//wget -m -np -c -R &amp;quot;index.html*&amp;quot; &amp;quot;http://the-eye.eu/public/Books/IT%20Various/&amp;quot;

//on ouvre le dossier dont le chemin est dans la variable pathVracDir
// on s&amp;#39;assure qu&amp;#39;il existe et que c&amp;#39;est un dossier
File vracDir = new File(pathVracDir)
assert vracDir.exists() &amp;amp;amp;&amp;amp;amp; vracDir.directory


//separer les élèments de ma liste de critère
StringTokenizer tokenizer = new StringTokenizer(strCriteria)

//la liste de critere
List&amp;amp;lt;String&amp;amp;gt; criteriaList = new ArrayList&amp;amp;lt;&amp;amp;gt;()

//j&amp;#39;itère sur les tokens et je construit ma liste de token,
// mot clef critère de recherche
tokenizer.each { String it -&amp;amp;gt;
    criteriaList.add(it.toLowerCase())
}

//je collect tous les noms de fichier dans le dossier
List&amp;amp;lt;String&amp;amp;gt; fileNameList = vracDir
        .listFiles()
        .collect { File it -&amp;amp;gt;
            it.directory ?: it.name
        }

// je collect les noms de fichier qui contiendraient un des criteres
List&amp;amp;lt;String&amp;amp;gt; resultList = fileNameList.findAll { String name -&amp;amp;gt;
    criteriaList.findAll { String crit -&amp;amp;gt;
        name.toLowerCase().contains(crit)
    }
}

// je parcours ma liste de résultat pour les effacer
resultList.each { String fileName -&amp;amp;gt;
    assert new File(vracDir.path + sep + fileName).delete()
}

println fileNameList.size()
println resultList.size()
resultList.empty ?: println(vracDir.path + sep + resultList.first())&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Groovy: Substituir uma sequência de caracteres numa String e carregar um Properties a partir de um arquivo</title>
            <link >https://cheroliv.com//blog/2019/0004_groovy_replace_string_load_properties_post.html</link>
            <pubDate>Wed, 17 Jul 2019 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2019/0004_groovy_replace_string_load_properties_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Este é um trecho de código funcional em Groovy,&amp;lt;br&amp;gt;
para mudar todas as ocorrências duma sequência de caracteres numa String&amp;lt;br&amp;gt;
e carregar um arquivo .properties em um objeto properties.&amp;lt;br&amp;gt;
Executar um comando em uma String.&amp;lt;br&amp;gt;
replaceTextinFile.groovy&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;#!/usr/bin/env groovy
println System.getProperty(&amp;quot;user.home&amp;quot;)

static String getSeparator_() {
    System.getProperty(&amp;quot;file.separator&amp;quot;)
}

String separator = separator_

String path = System.getProperty(&amp;quot;user.home&amp;quot;)
        .concat(&amp;quot;${separator}.gradle&amp;quot; +
                separator +
                &amp;quot;gradle.properties&amp;quot;)

File propertiesFile = new File(path)

assert propertiesFile.exists() &amp;amp;amp;&amp;amp;amp; !propertiesFile.directory

Properties gradleProperties = new Properties()

propertiesFile.withInputStream { InputStream it -&amp;amp;gt;
    gradleProperties.load(it)
}

String user_name = gradleProperties.getProperty(&amp;quot;user_name&amp;quot;)

String text
String resultText
text = new File(&amp;quot;${System.getProperty(&amp;quot;user.home&amp;quot;)}&amp;quot; +
        separator +
        &amp;quot;src&amp;quot; +
        separator +
        &amp;quot;cheroliv.github.io&amp;quot; +
        separator +
        &amp;quot;src&amp;quot; +
        separator +
        &amp;quot;main&amp;quot; +
        separator +
        &amp;quot;resources&amp;quot; +
        separator +
        &amp;quot;eco_space.txt&amp;quot;).text

String to = separator +
        &amp;quot;media&amp;quot; +
        separator +
        &amp;quot;${user_name}&amp;quot; +
        separator +
        &amp;quot;320&amp;quot; +
        separator +
        &amp;quot;videoCours&amp;quot; +
        separator
String from = &amp;quot;${System.getProperty(&amp;quot;user.home&amp;quot;)}&amp;quot; +
        separator +
        &amp;quot;p2p&amp;quot; +
        separator

resultText = text.replace(to, from)

println resultText

//execute command line in a string
println &amp;quot;rsync -avr ${resultText} ${to}&amp;quot;.execute().text&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item><item>
            <title>Groovy: Caracteres ASCII</title>
            <link >https://cheroliv.com//blog/2019/0003_groovy_char_ascci_post.html</link>
            <pubDate>Wed, 10 Jul 2019 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">blog/2019/0003_groovy_char_ascci_post.html</guid>
            <description>&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;Aqui está um trecho de código funcional em Groovy, que gera um arquivo de texto,&amp;lt;br&amp;gt;
com os 256 primeiros caracteres legíveis da tabela ASCII&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-groovy&amp;quot; data-lang=&amp;quot;groovy&amp;quot;&amp;gt;#!/usr/bin/env groovy
import java.nio.charset.StandardCharsets

List&amp;amp;lt;Character&amp;amp;gt; chars = new ArrayList&amp;amp;lt;&amp;amp;gt;()
int j = 0
Character jumpLine = &amp;#39;\n&amp;#39;

256.times { Integer idx -&amp;amp;gt;
    if (Character.isAlphabetic(idx) || Character.isDigit(idx)) {
        if (j % 10 == 0 &amp;amp;amp;&amp;amp;amp; j != 0) {
            chars.add(jumpLine)
            chars.add(idx as char)
        } else chars.add(idx as char)
        j++
    }
}

String seperator = System.getProperty(&amp;quot;file.separator&amp;quot;)

String path = &amp;quot;${System.getProperty(&amp;quot;user.home&amp;quot;)}${seperator}ascii.txt&amp;quot;
File speCharFile = new File(path)

if (speCharFile.exists() &amp;amp;amp;&amp;amp;amp; !speCharFile.directory) {
    speCharFile.text = &amp;quot;&amp;quot;
} else {
    speCharFile.createNewFile()
}

String text = new String()

chars.each { Character it -&amp;amp;gt;
    text = it == jumpLine ? &amp;quot;$text$it&amp;quot; : &amp;quot;$text $it &amp;quot;
}

speCharFile.setText(text, StandardCharsets.UTF_8.toString())&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;eu nomei o arquivo spe_char.groovy,&amp;lt;br&amp;gt;
do diretório onde está o arquivo&amp;lt;br&amp;gt;
abrir um terminal e copiar e colar para executar o script&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;listingblock&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;pre class=&amp;quot;highlight&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;language-bash&amp;quot; data-lang=&amp;quot;bash&amp;quot;&amp;gt;$ groovy spe_char.groovy&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;resultado:&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;paragraph&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;0  1  2  3  4  5  6  7  8  9 A  B  C  D  E  F  G  H  I  J K  L  M  N  O  P  Q  R  S  T U  V  W  X  Y  Z  a  b  c  d e  f  g  h  i  j  k  l  m  n o  p  q  r  s  t  u  v  w  x y  z  ª  µ  º  À  Á  Â  Ã  Ä Å  Æ  Ç  È  É  Ê  Ë  Ì  Í  Î Ï  Ð  Ñ  Ò  Ó  Ô  Õ  Ö  Ø  Ù Ú  Û  Ü  Ý  Þ  ß  à  á  â  ã ä  å  æ  ç  è  é  ê  ë  ì  í î  ï  ð  ñ  ò  ó  ô  õ  ö  ø ù  ú  û  ü  ý  þ  ÿ&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;</description>
        </item>

    </channel>
</rss>
