<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rost</title>
    <description>The latest articles on DEV Community by Rost (@rosgluk).</description>
    <link>https://dev.to/rosgluk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3544400%2F04dd81bf-749e-4055-971f-316c0134e76c.jpg</url>
      <title>DEV Community: Rost</title>
      <link>https://dev.to/rosgluk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rosgluk"/>
    <language>en</language>
    <item>
      <title>Configure Git User Name and Email Address</title>
      <dc:creator>Rost</dc:creator>
      <pubDate>Tue, 08 Sep 2026 10:48:32 +0000</pubDate>
      <link>https://dev.to/rosgluk/configure-git-user-name-and-email-address-1j9g</link>
      <guid>https://dev.to/rosgluk/configure-git-user-name-and-email-address-1j9g</guid>
      <description>&lt;p&gt;After clonning the git repository, do the local repository configuration, particularly set user name and email address.&lt;/p&gt;

&lt;p&gt;This page is part of &lt;a href="https://www.glukhov.org/developer-tools/" rel="noopener noreferrer"&gt;Developer Tools: The Complete Guide to Modern Development Workflows&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you doing first commit to newly clonned repo via command line like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"test commit msg"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you might receive a sencible message and git do what expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[master 3506e11] test commit msg
&lt;/span&gt;&lt;span class="gp"&gt; Committer: megaadmin &amp;lt;megaadmin@myhost&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but if you use vs code, you might get a message like&lt;/p&gt;

&lt;h2&gt;
  
  
  How
&lt;/h2&gt;

&lt;p&gt;Basically what we need to do is to run git with configure command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config user.email &lt;span class="s2"&gt;"root@microsoft.com"&lt;/span&gt;
git config user.name &lt;span class="s2"&gt;"bg"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just replace email and username with yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;About hosting local Gitea server: &lt;a href="https://www.glukhov.org/developer-tools/git-and-forges/gitea/" rel="noopener noreferrer"&gt;Choosing free on-prem git server - Gitea is the winner!&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;For SSL setup with Gitea: &lt;a href="https://www.glukhov.org/developer-tools/git-and-forges/gitea-ssl/" rel="noopener noreferrer"&gt;Gitea SSL with Apache as reverse proxy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Once your Gitea server is running: &lt;a href="https://www.glukhov.org/developer-tools/git-and-forges/gitea-backup-restore/" rel="noopener noreferrer"&gt;Backup and Restore Gitea server&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.glukhov.org/developer-tools/git-and-forges/git-cheatsheet/" rel="noopener noreferrer"&gt;GIT Cheatsheet: Most useful GIT commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;For branching strategies: &lt;a href="https://www.glukhov.org/developer-tools/git-and-forges/gitflow-steps-and-alternatives/" rel="noopener noreferrer"&gt;Gitflow Explained: Steps, Alternatives, Pros, and Cons&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>gitea</category>
      <category>git</category>
      <category>dev</category>
    </item>
    <item>
      <title>What Comes After LLMs? Mamba, Diffusion &amp; World Models</title>
      <dc:creator>Rost</dc:creator>
      <pubDate>Tue, 08 Sep 2026 10:48:05 +0000</pubDate>
      <link>https://dev.to/rosgluk/what-comes-after-llms-mamba-diffusion-world-models-24mm</link>
      <guid>https://dev.to/rosgluk/what-comes-after-llms-mamba-diffusion-world-models-24mm</guid>
      <description>&lt;p&gt;AI hype follows a rhythm: roughly every three years, the architecture everyone is betting on gets replaced by something newer. The next shift is already forming in research labs.&lt;/p&gt;

&lt;p&gt;What follows is a tour of the strongest candidates to replace the transformer, and of the evidence that the shift is already under way. The pattern repeats: a dominant architecture, a wall it cannot scale past, and a new design that routes around that wall.&lt;/p&gt;

&lt;p&gt;This article walks through the four limitations closing in on transformers, the three alternative architectures, and what the hybrid systems already in production tell us. The goal is a practical read for engineers deciding what to architect for 2027 and beyond — not a prediction market, just the evidence as it stands in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern: AI's three-year breakthrough cycle
&lt;/h2&gt;

&lt;p&gt;Every few years the center of gravity of AI shifts:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    A["~2017&amp;lt;br/&amp;gt;Transformers replace&amp;lt;br/&amp;gt;RNNs and CNNs"] --&amp;gt; B["2020-2022&amp;lt;br/&amp;gt;Diffusion models take&amp;lt;br/&amp;gt;image generation"]
    B --&amp;gt; C["2022&amp;lt;br/&amp;gt;LLMs reach everyone&amp;lt;br/&amp;gt;(GPT-3, ChatGPT)"]
    C --&amp;gt; D["2026-2028&amp;lt;br/&amp;gt;Hybrid post-transformer&amp;lt;br/&amp;gt;systems in production"]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Transformers showed up around 2017 and made RNNs and CNNs look like relics. Diffusion models then took over image generation between 2020 and 2022. And LLMs exploded into everything starting in 2022 with GPT-3 and ChatGPT.&lt;/p&gt;

&lt;p&gt;By that pattern, we should be approaching the next shift. It is already happening — just not where you would expect. Not on social media or in press releases, but in research labs.&lt;/p&gt;

&lt;p&gt;Yann LeCun spent 2025 arguing that autoregressive LLMs are fundamentally limited. "We need world models, not word predictors," he said at conference after conference. Meanwhile his team published a sequence of papers on Joint Embedding Predictive Architecture (JEPA) — first for images, then video, then language in September 2025.&lt;/p&gt;

&lt;p&gt;The supporting evidence from the last twelve months is unusually concrete:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;State space models like &lt;strong&gt;Mamba&lt;/strong&gt; handle million-token contexts with linear scaling — something transformers cannot do because of their quadratic attention mechanism.&lt;/li&gt;
&lt;li&gt;Google announced &lt;strong&gt;Gemini Diffusion&lt;/strong&gt;, promising text generation up to 10x faster.&lt;/li&gt;
&lt;li&gt;NVIDIA shipped &lt;strong&gt;Nemotron 3&lt;/strong&gt; with Mamba-2 layers built in.&lt;/li&gt;
&lt;li&gt;China's &lt;strong&gt;Qwen3.5&lt;/strong&gt; went live using &lt;strong&gt;Gated DeltaNets&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In December 2025, &lt;strong&gt;VL-JEPA&lt;/strong&gt; landed — a vision-language model with half the parameters of its competitors that still performed better.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why transformers are running out of road
&lt;/h2&gt;

&lt;p&gt;Transformers are incredible at what they do. But four problems are closing in that no amount of scaling can fix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quadratic attention breaks at long context
&lt;/h3&gt;

&lt;p&gt;Self-attention means every token talks to every other token — O(n²) complexity. Double your context and you quadruple the compute. At 10 million tokens, the math simply breaks. You can throw more GPUs at it, but you are fighting basic arithmetic.&lt;/p&gt;

&lt;h3&gt;
  
  
  The training data is running out
&lt;/h3&gt;

&lt;p&gt;LLMs train on internet text, and by 2026 most of it has been scraped. Books, Wikipedia, Reddit, GitHub — all consumed. Synthetic data sounds like a fix until you realize that models trained on AI-generated text degrade over generations: they learn the artifacts and biases of the generation process itself. The scaling laws that powered everything from 2020 to 2024 are hitting diminishing returns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Energy and cost are the hard ceiling
&lt;/h3&gt;

&lt;p&gt;Frontier models cost tens or hundreds of millions to train now. Serving billions of requests daily burns massive compute. AI data centers need gigawatts of power, and power generation capacity is becoming a bottleneck. Sam Altman admitted in 2024 that compute costs limit everything — and that has not changed.&lt;/p&gt;

&lt;p&gt;If the constraint is energy, the answer can come from the model side or the silicon side. My notes on &lt;a href="https://www.glukhov.org/llm-architecture/cost-optimization/cost-optimization-for-llm-systems/" rel="noopener noreferrer"&gt;cost optimization for LLM systems&lt;/a&gt; and on &lt;a href="https://www.glukhov.org/llm-performance/hardware/llm-asics/" rel="noopener noreferrer"&gt;specialized LLM inference chips&lt;/a&gt; cover each side of that tradeoff.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern matching is not understanding
&lt;/h3&gt;

&lt;p&gt;LLMs are pattern matchers at scale, predicting the next token from training data. Exceptional at it, sure — but not reasoning, and not understanding causal relationships in the physical world. If you want AI that can plan, simulate, or genuinely adapt to new environments, token prediction will not get you there.&lt;/p&gt;

&lt;h2&gt;
  
  
  State space models: linear scaling with a compressed memory
&lt;/h2&gt;

&lt;p&gt;State space models (SSMs) such as Mamba and Gated DeltaNet take a completely different approach to sequences. Instead of attending to all previous tokens, they maintain a compressed internal state that summarizes history — linear O(n) scaling instead of quadratic, with constant memory per token at inference regardless of how long the sequence gets.&lt;/p&gt;

&lt;p&gt;The mechanism behind that efficiency is worth understanding, because it explains both the speed and the tradeoff. Classic state space models (the S4 family) were time-invariant: the same fixed transition matrix applied at every step, which is fast but cannot decide that one token matters more than another. Mamba's contribution was to make those transition parameters &lt;em&gt;selective&lt;/em&gt; — functions of the current input — so the model can dynamically choose what to keep in its compressed state and what to discard, much like attention's softmax decides what to emphasize, but without materializing an O(n²) score matrix. To keep that selective recurrence fast, Mamba runs it as a hardware-aware parallel scan: an associative operation computed in GPU SRAM rather than a token-by-token loop, which is the same kind of low-level kernel engineering that FlashAttention did for transformers. Mamba-2 pushed this further with "state space duality," showing that a selective SSM and a restricted form of linear attention compute the same underlying operation — letting SSM kernels reuse the matrix-multiplication hardware that GPUs are optimized for.&lt;/p&gt;

&lt;p&gt;The real production breakthrough here was &lt;strong&gt;Gated DeltaNet&lt;/strong&gt;, an improvement on Mamba2 that uses a delta rule for better long-context handling. Qwen3.5 adopted it as its core architecture in February 2026 and got strong results on agent tasks and coding benchmarks using only &lt;strong&gt;3 billion active parameters&lt;/strong&gt; in its hybrid MoE configuration. That is impressive efficiency, and my &lt;a href="https://www.glukhov.org/llm-performance/benchmarks/comparing-qwen-3-6-mtp-vs-standard/" rel="noopener noreferrer"&gt;llama.cpp benchmarks of Qwen 3.6 MTP versus standard decoding&lt;/a&gt; on a 16 GB GPU give a sense of how Qwen models behave on consumer hardware.&lt;/p&gt;

&lt;p&gt;The tradeoff is structural, not just a training artifact: because an SSM compresses the entire history into a fixed-size state, it can lose fine-grained detail that transformers preserve by keeping every past token individually addressable through attention. If you need exact copying or citation, transformers still win. For most practical workloads — summarization, agentic tool use, long-document reasoning where the gist matters more than verbatim recall — the efficiency gain is worth it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diffusion language models: many tokens per forward pass
&lt;/h2&gt;

&lt;p&gt;Diffusion language models apply the same technique that transformed image generation to text. Instead of generating one token at a time, they can produce multiple tokens in parallel during each forward pass.&lt;/p&gt;

&lt;p&gt;NVIDIA's &lt;strong&gt;Nemotron-Labs-Diffusion&lt;/strong&gt;, released in May 2026, achieved nearly &lt;strong&gt;6x tokens per forward pass&lt;/strong&gt; compared to Qwen3-8B while maintaining competitive accuracy across benchmarks including HumanEval, GSM8K, and Math500. It is a tri-mode model that unifies autoregressive, diffusion, and self-speculation decoding. Google's Gemini Diffusion promises even more speedup.&lt;/p&gt;

&lt;p&gt;The catch is quality versus speed. Autoregressive models optimize for local coherence at each step — the most probable next token. Diffusion models optimize globally across the entire output, which can improve overall structure but sometimes sacrifices fine-grained detail. For creative writing or brainstorming, diffusion might be better. For precise technical output, autoregressive still wins.&lt;/p&gt;

&lt;p&gt;It is worth separating this from inference-time tricks. &lt;a href="https://www.glukhov.org/llm-performance/optimization/speculative-decoding/" rel="noopener noreferrer"&gt;Speculative decoding&lt;/a&gt; already delivers several tokens per forward pass by drafting and verifying, with no change to the output distribution. Diffusion moves parallel token generation from the inference layer into the model architecture itself — a structurally different bet.&lt;/p&gt;

&lt;h2&gt;
  
  
  World models and JEPA: predicting meaning, not tokens
&lt;/h2&gt;

&lt;p&gt;World models, specifically Yann LeCun's JEPA architecture, take the most radical approach. Instead of predicting the next token, they predict the &lt;strong&gt;next latent embedding&lt;/strong&gt; — a compressed representation of what should come next. That is closer to how human cognition works: we do not predict individual words, we predict meaning.&lt;/p&gt;

&lt;p&gt;The architectural difference from both transformers and diffusion models is the missing decoder. A generative model — autoregressive or diffusion — has to reconstruct its target in the original space: exact pixels, exact tokens. That forces the model to spend capacity modeling unpredictable surface noise: the exact phrasing of a sentence, the exact pixel value of a leaf in the wind. JEPA's loss operates entirely inside a learned representation space instead, comparing a predicted embedding against a target embedding produced by a separate, slowly-updated encoder. Nothing is ever decoded back to raw output during training. That lets the predictor discard the noise and keep only the structural, task-relevant part of the signal — which is also, not coincidentally, the part that is useful for planning: an agent can simulate "what happens if I do X" by rolling forward in latent space, without paying the cost of rendering a full image or generating full text for every hypothetical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VL-JEPA&lt;/strong&gt; achieved performance comparable to established vision-language models with only &lt;strong&gt;1.6 billion parameters&lt;/strong&gt; versus 7 billion or more for competitors. It reduces decoding operations by about &lt;strong&gt;2.85x&lt;/strong&gt; while improving accuracy on visual question answering. More importantly, JEPA architectures are designed for continual learning — they build internal models of how the environment works, not just statistical patterns in text.&lt;/p&gt;

&lt;p&gt;The limitation is that JEPA is still maturing for pure language tasks: LLM-JEPA exists but has not matched large autoregressive or diffusion models on standard benchmarks yet, and JEPA is not a drop-in replacement for a chatbot — it is a representation-learning and planning substrate that complements language models more than it competes with them head-on. For embodied AI and robotics, however — where understanding physical causality matters more than generating fluent prose — JEPA might be the right architecture from the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  What production systems are doing: hybrid architectures
&lt;/h2&gt;

&lt;p&gt;The most interesting developments combine elements from multiple architectures. AI21 Labs' &lt;strong&gt;Jamba&lt;/strong&gt; interleaves transformer attention layers with Mamba state space layers — attention for long-range dependencies, SSMs for efficient local processing. &lt;strong&gt;Qwen3.5&lt;/strong&gt; combines Gated DeltaNet with Mixture of Experts, activating only the relevant expert networks for each input. Cloud providers are already listing Mamba-based models — Mistral's Codestral Mamba was the first open-source Mamba-2 release — and the &lt;a href="https://www.glukhov.org/llm-hosting/cloud/cloud-llm-providers/" rel="noopener noreferrer"&gt;cloud LLM providers overview&lt;/a&gt; tracks what is available where.&lt;/p&gt;

&lt;p&gt;Different tasks have different requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code generation might benefit from diffusion's parallel token generation.&lt;/li&gt;
&lt;li&gt;Long-document analysis needs an SSM's linear scaling.&lt;/li&gt;
&lt;li&gt;Creative writing could use transformer attention for coherence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A single architecture will not solve everything. The same logic that drives &lt;a href="https://www.glukhov.org/llm-architecture/model-routing/multi-model-system-design/" rel="noopener noreferrer"&gt;multi-model system design&lt;/a&gt; — the right mechanism for the right task — is now appearing inside the models themselves.&lt;/p&gt;

&lt;p&gt;If you are building AI applications and architecting for 2027 or 2028, do not over-invest in pure transformer solutions. Consider whether SSM or diffusion components might better serve your use case. The ecosystem is shifting, and early adopters will have an advantage.&lt;/p&gt;

&lt;p&gt;Two further implications are worth tracking. Qwen3.5's performance with only 3 billion active parameters suggests that specialized, efficient architectures will outperform larger general-purpose models for many tasks — which could democratize AI deployment and make high-performance models accessible on consumer hardware. And diffusion language models could reduce latency by an order of magnitude for certain workloads; applications requiring real-time response — conversational agents, live translation, interactive tools — will benefit most.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this is heading: the post-transformer era
&lt;/h2&gt;

&lt;p&gt;Following the three-year cycle, I would expect the first production deployments of non-transformer architectures by &lt;strong&gt;2027–2028&lt;/strong&gt;. These will likely be hybrid systems that leverage transformer attention where needed but use SSMs or diffusion for efficiency gains.&lt;/p&gt;

&lt;p&gt;The consolidation phase we are in now — where labs prove that LLMs can reason and act — is giving way to a harder challenge: making these systems efficient, adaptable, and spatially aware. That demands breakthroughs in continual learning, world models, and architectural efficiency. Each addresses fundamental limitations that pure scaling cannot solve.&lt;/p&gt;

&lt;p&gt;The next big thing will not be bigger models. It will be smarter architectures that achieve more with less — models that understand rather than just predict, that adapt rather than just process. That is what comes after LLMs. Not a single new architecture, but a generation of specialized, efficient systems that finally move beyond pattern matching toward genuine understanding.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Adaline Labs. "The AI Research Landscape in 2026: From Agentic AI to Embodiment." &lt;a href="https://labs.adaline.ai/p/the-ai-research-landscape-in-2026" rel="noopener noreferrer"&gt;https://labs.adaline.ai/p/the-ai-research-landscape-in-2026&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Aftab, A. "The End of LLMs As We Know Them: Why 2026 Marks the Beginning of AI's Next Architecture Revolution." Medium, 2025. &lt;a href="https://medium.com/@aftab001x/the-end-of-llms-as-we-know-them-why-2026-marks-the-beginning-of-ais-next-architecture-revolution-902ee29484f7" rel="noopener noreferrer"&gt;https://medium.com/@aftab001x/the-end-of-llms-as-we-know-them-why-2026-marks-the-beginning-of-ais-next-architecture-revolution-902ee29484f7&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Greengard, S. "Beyond LLMs: A Post-Transformer World Emerges." Communications of the ACM, May 2026. &lt;a href="https://cacm.acm.org/news/beyond-llms-a-post-transformer-world-emerges" rel="noopener noreferrer"&gt;https://cacm.acm.org/news/beyond-llms-a-post-transformer-world-emerges&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;NVIDIA. "Nemotron-Labs-Diffusion: A Tri-Mode Language Model Unifying Autoregressive, Diffusion, and Self-Speculation Decoding." arXiv:2607.05722, 2026. &lt;a href="https://arxiv.org/html/2607.05722v1" rel="noopener noreferrer"&gt;https://arxiv.org/html/2607.05722v1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MarkTechPost. "NVIDIA AI Releases Nemotron-Labs-Diffusion: A Tri-Mode Language Model with 6× Tokens Per Forward Over Qwen3-8B." May 20, 2026. &lt;a href="https://www.marktechpost.com/2026/05/20/nvidia-ai-releases-nemotron-labs-diffusion-a-tri-mode-language-model-with-6x-tokens-per-forward-over-qwen3-8b" rel="noopener noreferrer"&gt;https://www.marktechpost.com/2026/05/20/nvidia-ai-releases-nemotron-labs-diffusion-a-tri-mode-language-model-with-6x-tokens-per-forward-over-qwen3-8b&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Chen, D. et al. "VL-JEPA: Joint Embedding Predictive Architecture for Vision-language." arXiv:2512.10942, 2025. &lt;a href="https://arxiv.org/pdf/2512.10942" rel="noopener noreferrer"&gt;https://arxiv.org/pdf/2512.10942&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenReview. "VL-JEPA: Joint Embedding Predictive Architecture for Vision-language." &lt;a href="https://openreview.net/forum?id=tjimrqc2BU" rel="noopener noreferrer"&gt;https://openreview.net/forum?id=tjimrqc2BU&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;NVIDIA Research. "Gated Delta Networks: Improving Mamba2 with Delta Rule." ICLR 2025. &lt;a href="https://research.nvidia.com/publication/2025-04_gated-delta-networks-improving-mamba2-delta-rule" rel="noopener noreferrer"&gt;https://research.nvidia.com/publication/2025-04_gated-delta-networks-improving-mamba2-delta-rule&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Laon People. "Why Did Qwen3.5 Choose Gated DeltaNet?" February 2026. &lt;a href="https://laonpeople.com/en/blog/why-did-qwen3-5-choose-gated-deltanet" rel="noopener noreferrer"&gt;https://laonpeople.com/en/blog/why-did-qwen3-5-choose-gated-deltanet&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Google DeepMind. "Gemini Diffusion." &lt;a href="https://deepmind.google/models/gemini-diffusion" rel="noopener noreferrer"&gt;https://deepmind.google/models/gemini-diffusion&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Vicentino, C. "Autoregressive vs. Masked Diffusion Language Models: A Controlled Comparison." arXiv:2603.22075, March 2026. &lt;a href="https://www.alphaxiv.org/abs/2603.22075" rel="noopener noreferrer"&gt;https://www.alphaxiv.org/abs/2603.22075&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JetBrains AI Blog. "Why Diffusion Models Could Change Developer Workflows in 2026." November 2025. &lt;a href="https://blog.jetbrains.com/ai/2025/11/why-diffusion-models-could-change-developer-workflows-in-2026" rel="noopener noreferrer"&gt;https://blog.jetbrains.com/ai/2025/11/why-diffusion-models-could-change-developer-workflows-in-2026&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Spheron Blog. "Mamba-3 and State Space Models on GPU Cloud: Deploy SSM Inference as the Transformer Alternative (2026 Guide)." &lt;a href="https://www.spheron.network/blog/mamba-3-state-space-model-gpu-cloud-deployment" rel="noopener noreferrer"&gt;https://www.spheron.network/blog/mamba-3-state-space-model-gpu-cloud-deployment&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Gartner. "Gartner Predicts 40 Percent of Enterprise Apps Will Feature Task-Specific AI Agents by 2026." August 26, 2025. &lt;a href="https://www.gartner.com/en/newsroom/press-releases/2025-08-26-gartner-predicts-40-percent-of-enterprise-apps-will-feature-task-specific-ai-agents-by-2026-up-from-less-than-5-percent-in-2025" rel="noopener noreferrer"&gt;https://www.gartner.com/en/newsroom/press-releases/2025-08-26-gartner-predicts-40-percent-of-enterprise-apps-will-feature-task-specific-ai-agents-by-2026-up-from-less-than-5-percent-in-2025&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Reddit r/deeplearning. "Following a 3-year AI breakthrough cycle." &lt;a href="https://www.reddit.com/r/deeplearning/comments/1k8gdwg/following_a_3year_ai_breakthrough_cycle" rel="noopener noreferrer"&gt;https://www.reddit.com/r/deeplearning/comments/1k8gdwg/following_a_3year_ai_breakthrough_cycle&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Gu, A., Dao, T. "Mamba: Linear-Time Sequence Modeling with Selective State Spaces." arXiv:2312.00752, 2023. &lt;a href="https://doi.org/10.48550/arxiv.2312.00752" rel="noopener noreferrer"&gt;https://doi.org/10.48550/arxiv.2312.00752&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Dao, T., Gu, A. "Transformers are SSMs: Generalized Models and Efficient Algorithms Through Structured State Space Duality (Mamba-2)." arXiv:2405.21060, 2024. &lt;a href="https://arxiv.org/abs/2405.21060" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2405.21060&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Meta AI Blog. "The first AI model based on Yann LeCun's vision for more human-like AI (I-JEPA)." &lt;a href="https://ai.meta.com/blog/yann-lecun-ai-model-i-jepa/" rel="noopener noreferrer"&gt;https://ai.meta.com/blog/yann-lecun-ai-model-i-jepa/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;LeCun, Y. "A Path Towards Autonomous Machine Intelligence." Position paper, 2022. &lt;a href="https://openreview.net/forum?id=BZ5a1r-kVsf" rel="noopener noreferrer"&gt;https://openreview.net/forum?id=BZ5a1r-kVsf&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>architecture</category>
      <category>deeplearning</category>
    </item>
    <item>
      <title>Agent Skills vs MCP Servers: Decision Framework</title>
      <dc:creator>Rost</dc:creator>
      <pubDate>Sun, 06 Sep 2026 01:55:19 +0000</pubDate>
      <link>https://dev.to/rosgluk/agent-skills-vs-mcp-servers-decision-framework-3n04</link>
      <guid>https://dev.to/rosgluk/agent-skills-vs-mcp-servers-decision-framework-3n04</guid>
      <description>&lt;p&gt;Agent Skills and MCP servers are often presented as competing ways to extend an AI agent. That framing is wrong: a skill teaches the agent how to work, while an MCP server gives it governed access to live capabilities.&lt;/p&gt;

&lt;p&gt;The useful question is not "Which standard wins?" It is "Where should this responsibility live?" This guide answers that for hosted assistants such as Hermes Agent and OpenClaw, where context size, long-running connections, credentials, and operational safety matter more than a tidy demo.&lt;/p&gt;

&lt;p&gt;This is not an academic comparison. It is a practical decision framework built from real deployment experience with both mechanisms, including the context-cost tradeoffs that only become visible once an agent is running in production. If you are building multi-agent systems, you may also want to read our &lt;a href="https://www.glukhov.org/ai-systems/mcp/a2a-vs-mcp-ai-agent-protocols/" rel="noopener noreferrer"&gt;A2A vs MCP protocol comparison&lt;/a&gt;, which covers a different axis of the same problem space.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent Skills vs MCP Servers in One Table
&lt;/h2&gt;

&lt;p&gt;Use a skill for procedure, judgment, and reusable operating knowledge. Use an MCP server for authoritative state, protected operations, and a stable capability contract.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decision signal&lt;/th&gt;
&lt;th&gt;Agent Skill&lt;/th&gt;
&lt;th&gt;MCP server&lt;/th&gt;
&lt;th&gt;Usually both&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Static instructions, checklists, or style rules&lt;/td&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Poor fit&lt;/td&gt;
&lt;td&gt;Sometimes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live tickets, deployments, records, or metrics&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credentials or delegated user identity&lt;/td&gt;
&lt;td&gt;Avoid&lt;/td&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Existing local CLI with safe, narrow commands&lt;/td&gt;
&lt;td&gt;Good fit&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;Sometimes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transactional writes or idempotency&lt;/td&gt;
&lt;td&gt;Weak fit&lt;/td&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portable procedure across agent hosts&lt;/td&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared capability across languages and clients&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human approval and escalation policy&lt;/td&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Enforce final check&lt;/td&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format and evidence rubric&lt;/td&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Sometimes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;My default is deliberately conservative: start with a skill when the job is local, read-heavy, and procedural. Add an MCP server when the agent crosses a trust boundary, touches changing external state, or needs an operation that should remain correct even when the model is confused.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Distinction: Procedure vs Capability
&lt;/h2&gt;

&lt;p&gt;An Agent Skill is a directory centered on &lt;code&gt;SKILL.md&lt;/code&gt;, with optional scripts, references, and assets. The &lt;a href="https://agentskills.io/specification" rel="noopener noreferrer"&gt;Agent Skills specification&lt;/a&gt; defines required metadata and a progressive disclosure model: the host can discover a small name and description first, load the full instructions when relevant, and fetch supporting files only when needed.&lt;/p&gt;

&lt;p&gt;That makes a skill a strong home for an incident rubric, a release checklist, a research method, or instructions for using an existing command-line tool. Its central value is encoded procedure: sequence, judgment, constraints, examples, and the definition of a good result. For Hermes-specific authoring details including frontmatter structure and conditional activation, see &lt;a href="https://www.glukhov.org/ai-systems/hermes/authoring-hermes-skill/" rel="noopener noreferrer"&gt;Hermes Agent Skill Authoring&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;MCP solves a different problem. The &lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/basic" rel="noopener noreferrer"&gt;Model Context Protocol specification&lt;/a&gt; gives a client and server a JSON-RPC-based contract for capabilities including tools, resources, and prompts, with standard transports and discovery behavior. Practical implementation guides for &lt;a href="https://www.glukhov.org/ai-systems/mcp/mcp-server-in-python/" rel="noopener noreferrer"&gt;MCP servers in Python&lt;/a&gt; and &lt;a href="https://www.glukhov.org/ai-systems/mcp/mcp-server-in-go/" rel="noopener noreferrer"&gt;MCP servers in Go&lt;/a&gt; show how straightforward the integration layer can be once the protocol handles the heavy lifting.&lt;/p&gt;

&lt;p&gt;An MCP server is therefore a good boundary around a ticket system, cloud control plane, source-of-truth database, or internal search service. It owns the mechanics of reaching that system and can enforce validation, authorization, timeouts, rate limits, and audit behavior outside the model's prose instructions.&lt;/p&gt;

&lt;h3&gt;
  
  
  A More Precise Rule
&lt;/h3&gt;

&lt;p&gt;Ask whether a responsibility must remain correct without the model remembering an instruction. If the answer is yes, it belongs in deterministic code or server policy, not only in &lt;code&gt;SKILL.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, "collect three supporting signals before escalating" is a useful skill instruction. "Reject a status change unless the caller has the incident-manager scope" must be enforced by the service or MCP server, even if the skill repeats the rule.&lt;/p&gt;

&lt;p&gt;This is the boundary that matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A skill can tell the agent when an action is appropriate.&lt;/li&gt;
&lt;li&gt;An MCP tool can make the action available through a typed interface.&lt;/li&gt;
&lt;li&gt;The backing service must decide whether the action is actually allowed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MCP servers can also expose prompts, so the standards overlap at the edges. Still, putting an entire operating procedure in a giant tool description usually produces a brittle capability catalog, while putting a privileged API client in a shell script hidden inside a skill usually produces an avoidable security problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  When a SKILL.md Is Enough
&lt;/h2&gt;

&lt;p&gt;A skill is enough when the agent already has safe access to everything required and the missing ingredient is know-how. This is common for repository analysis, document transformation, report generation, or a local workflow built on mature CLI commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Data Is Local or Supplied by the User
&lt;/h3&gt;

&lt;p&gt;Suppose an agent must inspect a checked-out repository, run read-only linters, compare configuration files, and produce a migration report. The files are already in the working environment, and the host already exposes filesystem and process tools, so another network service adds little value.&lt;/p&gt;

&lt;p&gt;The skill can describe which files to inspect, the command order, failure handling, and the required evidence. A bundled script may normalize output, but the host's existing sandbox and command permissions remain the actual execution boundary.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Workflow Depends on Judgment
&lt;/h3&gt;

&lt;p&gt;Skills are especially useful when several technically valid actions exist but the organization prefers one operating method. A code-review skill can explain which risks deserve blocking comments, when to request a reproduction, and how to separate correctness issues from taste.&lt;/p&gt;

&lt;p&gt;Those rules change as teams learn. Keeping them as version-controlled prose and small references is often clearer than recompiling or redeploying a server for every editorial adjustment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Portability Matters More Than Central Control
&lt;/h3&gt;

&lt;p&gt;The open Agent Skills format is designed as a portable folder rather than a remote runtime. A well-scoped skill can move between compatible hosts with its instructions, examples, and supporting assets intact, although tool names and sandbox behavior still require host-specific testing.&lt;/p&gt;

&lt;p&gt;This portability is useful for Hermes Agent and OpenClaw workflows that share a method but not necessarily the same deployment. Keep host-specific notes in short references instead of forking the core procedure at the first difference. The &lt;a href="https://www.glukhov.org/ai-systems/openclaw/skills/" rel="noopener noreferrer"&gt;OpenClaw skills ecosystem guide&lt;/a&gt; covers which skills are worth installing and how to gate them safely per agent role.&lt;/p&gt;

&lt;h3&gt;
  
  
  An Existing CLI Already Provides the Capability
&lt;/h3&gt;

&lt;p&gt;Do not build a server merely to wrap a reliable local command. If a single-user assistant can call a narrow CLI that already handles authentication, structured output, and errors, a skill may be the smaller and more maintainable solution.&lt;/p&gt;

&lt;p&gt;The caveat is important: a CLI is not automatically safe because it is local. Avoid broad shell interpolation, prefer structured output, constrain writable targets, and do not treat a skill's suggested tool allowlist as a complete authorization system.&lt;/p&gt;

&lt;h2&gt;
  
  
  When You Need an MCP Server
&lt;/h2&gt;

&lt;p&gt;Choose MCP when the problem is not merely remembering what to do. An MCP server becomes valuable when the agent needs a durable, typed, and governable bridge to a changing system.&lt;/p&gt;

&lt;h3&gt;
  
  
  The State Is Live and Authoritative
&lt;/h3&gt;

&lt;p&gt;Customer tickets, deployment status, inventory, billing records, and production metrics can change between two model turns. Copying that state into a skill makes it stale by construction, while asking the model to scrape an interface produces an unstable contract.&lt;/p&gt;

&lt;p&gt;An MCP resource or tool can retrieve the current record at execution time. The server can normalize upstream quirks and return a compact result instead of exposing an entire vendor response to the model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Credentials or User Identity Are Involved
&lt;/h3&gt;

&lt;p&gt;Credentials should not live in &lt;code&gt;SKILL.md&lt;/code&gt;, examples, or bundled helper scripts. For remote HTTP deployments, the MCP authorization specification defines an OAuth-based model; for local stdio servers, credentials can be supplied through the process environment or another host-controlled mechanism. See the official &lt;a href="https://modelcontextprotocol.io/docs/2026-07-28/tutorials/security/authorization" rel="noopener noreferrer"&gt;MCP authorization guidance&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The deeper reason to use a server is not secret storage alone. A server can map identity to scopes, restrict tenants, redact fields, and record who requested a mutation, while a prose instruction can only ask the model to behave.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writes Need Transactional Guarantees
&lt;/h3&gt;

&lt;p&gt;Creating an invoice, changing a ticket status, or starting a deployment requires more than a plausible JSON object. The operation may need idempotency keys, optimistic concurrency, server-side validation, and a durable audit trail.&lt;/p&gt;

&lt;p&gt;These properties belong below the model. The skill may define the approval policy, but the MCP server should reject an invalid transition and make a retried request safe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple Agents Need the Same Capability
&lt;/h3&gt;

&lt;p&gt;A shared MCP server can present one contract to several agent hosts, languages, and model providers. That gives platform teams a central place to improve schemas, patch upstream API behavior, and apply access controls without copying integration logic into every skill.&lt;/p&gt;

&lt;p&gt;Centralization is not free. The server becomes an operated dependency with versioning, observability, availability, and incident-response obligations, so it should earn its existence with a real boundary rather than architectural enthusiasm.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Context-Cost Question
&lt;/h2&gt;

&lt;p&gt;Context cost is frequently reduced to the slogan "skills are progressive, tools are always loaded." Real hosts are more nuanced, and the difference should be measured in serialized model input rather than assumed from the extension format.&lt;/p&gt;

&lt;p&gt;The Agent Skills documentation describes roughly 100 tokens of discovery metadata per skill, recommends keeping activated instructions below 5,000 tokens, and allows references to load on demand. A simple planning estimate is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C_skill = discovery metadata + activated instructions + selected references
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MCP clients discover tool definitions from servers, but the protocol does not require every discovered schema to appear in every model call. Hosts may filter, defer, cache, or route tools, so the practical estimate is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C_mcp = tool schemas exposed to this turn + tool results retained in context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/server/tools" rel="noopener noreferrer"&gt;MCP tools specification&lt;/a&gt; also notes that stable tool ordering can improve prompt-cache behavior. Caching may reduce repeated processing cost, but it does not make an oversized catalog easier for a model to choose from.&lt;/p&gt;

&lt;h3&gt;
  
  
  An Illustrative Token Budget
&lt;/h3&gt;

&lt;p&gt;Consider a hosted assistant with 20 installed skills. At the Agent Skills documentation's approximate discovery cost, the compact skill index is around 2,000 tokens; activating a focused triage skill might add another 1,200 tokens and one 600-token reference.&lt;/p&gt;

&lt;p&gt;Now compare two MCP designs. A thin ticket server with four concise schemas might serialize to 500-800 tokens, while a broad enterprise server with 35 verbose tools could consume several thousand tokens before any result arrives.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Turn component&lt;/th&gt;
&lt;th&gt;Focused design&lt;/th&gt;
&lt;th&gt;Broad design&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Skill discovery metadata&lt;/td&gt;
&lt;td&gt;About 2,000 tokens&lt;/td&gt;
&lt;td&gt;About 2,000 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Activated skill and one reference&lt;/td&gt;
&lt;td&gt;About 1,800 tokens&lt;/td&gt;
&lt;td&gt;About 1,800 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP tool catalog exposed to model&lt;/td&gt;
&lt;td&gt;500-800 tokens&lt;/td&gt;
&lt;td&gt;4,000+ tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First tool result&lt;/td&gt;
&lt;td&gt;300-700 tokens&lt;/td&gt;
&lt;td&gt;1,500+ tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are illustrative planning numbers, not protocol guarantees or benchmarks. Measure the exact prompt generated by your host because schema verbosity, descriptions, routing, result retention, and tokenizer choice can move the total substantially.&lt;/p&gt;

&lt;p&gt;The practical conclusion is not "skills are cheap" or "MCP is expensive." It is that progressive disclosure and capability selection are architecture features: keep skill metadata discriminative, activate only relevant instructions, expose the smallest useful tool set, and return projections rather than raw upstream payloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Thin-Server Pattern: MCP Below, Skill Above
&lt;/h2&gt;

&lt;p&gt;The most durable design often combines both mechanisms. Put a small capability boundary in MCP, then place the operating method in a skill that calls it.&lt;/p&gt;

&lt;p&gt;Consider a support-incident workflow used from either Hermes Agent or OpenClaw. The agent must read a ticket, gather evidence, classify severity, draft an operator note, and change status only after the required approval.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the MCP Server Owns
&lt;/h3&gt;

&lt;p&gt;Keep the server interface narrow and literal:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MCP tool&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Server-side responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tickets_search&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find candidate tickets&lt;/td&gt;
&lt;td&gt;Tenant filtering, pagination, field projection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tickets_get&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read one ticket&lt;/td&gt;
&lt;td&gt;Authorization, redaction, current version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tickets_add_note&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add an operator note&lt;/td&gt;
&lt;td&gt;Input validation, idempotency, audit record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tickets_change_status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply a valid transition&lt;/td&gt;
&lt;td&gt;Scope check, transition rules, concurrency check&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The server should not contain a tool called &lt;code&gt;triage_everything&lt;/code&gt; with a paragraph-long description and a dozen unrelated flags. Four bounded operations are easier to authorize, test, observe, and reuse.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the Skill Owns
&lt;/h3&gt;

&lt;p&gt;The skill owns the sequence and judgment. A compact &lt;code&gt;SKILL.md&lt;/code&gt; could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;incident-triage&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Triage support incidents using ticket evidence and the severity rubric.&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="p"&gt;1.&lt;/span&gt; Read the ticket and its current version.
&lt;span class="p"&gt;2.&lt;/span&gt; Collect at least two independent signals before assigning severity.
&lt;span class="p"&gt;3.&lt;/span&gt; Separate observed facts from hypotheses in the note.
&lt;span class="p"&gt;4.&lt;/span&gt; Ask for operator approval before any customer-visible note or status change.
&lt;span class="p"&gt;5.&lt;/span&gt; Re-read the ticket before a write; stop if its version changed.
&lt;span class="p"&gt;6.&lt;/span&gt; End with severity, evidence, uncertainty, and recommended next action.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That file is readable, reviewable, and easy to revise when the triage policy changes. A linked reference can hold the severity rubric, while the main instructions remain short enough to activate without dragging an operations handbook into every turn.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Runs in Hermes Agent
&lt;/h3&gt;

&lt;p&gt;Hermes Agent's &lt;a href="https://github.com/NousResearch/hermes-agent/blob/main/skills/autonomous-ai-agents/hermes-agent/references/native-mcp.md" rel="noopener noreferrer"&gt;native MCP documentation&lt;/a&gt; describes startup discovery, persistent connections, stdio and Streamable HTTP transports, and namespaced MCP tools. Its current configuration also filters the environment for stdio servers and passes explicitly configured variables, which is a useful defense against accidental secret inheritance.&lt;/p&gt;

&lt;p&gt;In this design, Hermes discovers the four ticket tools, while the incident skill activates only for relevant requests. The model follows the skill, the MCP server executes bounded operations, and the ticket service remains the final authority.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Runs in OpenClaw
&lt;/h3&gt;

&lt;p&gt;OpenClaw's &lt;a href="https://docs.openclaw.ai/tools/skills" rel="noopener noreferrer"&gt;skills documentation&lt;/a&gt; follows the Agent Skills structure and builds a compact list of eligible skills for the model. The same incident folder can carry the core procedure, with a short host-specific reference explaining the available ticket tool names.&lt;/p&gt;

&lt;p&gt;Do not put the ticket token in the shared skill. OpenClaw explicitly treats shared skills as inputs rather than secret storage, and third-party skills should be reviewed as untrusted code before they are enabled.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the Split Survives Change
&lt;/h3&gt;

&lt;p&gt;If the support team revises its severity rubric, update the skill. If the ticket vendor changes authentication or pagination, update the MCP server without rewriting the operating policy.&lt;/p&gt;

&lt;p&gt;If a second agent host arrives, it can reuse the same MCP contract and adapt the skill's small host-specific layer. This separation reduces duplicated integration logic without turning every procedural edit into a service deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Five-Step Decision Framework
&lt;/h2&gt;

&lt;p&gt;The following sequence is more reliable than picking the fashionable extension type first.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Identify the Source of Truth
&lt;/h3&gt;

&lt;p&gt;Write down every input and output the workflow touches. Static guidance, repository files, and user-provided documents lean toward a skill; mutable remote records and authoritative systems lean toward MCP.&lt;/p&gt;

&lt;p&gt;Not all state justifies a server. A local build artifact is state, but an existing sandboxed CLI may already provide a sufficient boundary.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Locate the Trust Boundary
&lt;/h3&gt;

&lt;p&gt;Mark where credentials, tenant identity, privileged data, or irreversible actions appear. If the agent crosses that line, introduce a deterministic enforcement point, typically an MCP server backed by service authorization.&lt;/p&gt;

&lt;p&gt;Treat the model and skill as request planners, not policy engines. They may propose a permitted action, but they should not be able to redefine permission by changing their own instructions.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Separate Capability From Policy
&lt;/h3&gt;

&lt;p&gt;Name capabilities as narrow verbs with typed inputs: get a ticket, add a note, or change status. Put the conditions for choosing those verbs, the evidence standard, and the preferred sequence in the skill.&lt;/p&gt;

&lt;p&gt;Some policy must exist in both layers for different reasons. "Ask the user before deploying" belongs in the skill for interaction quality, while "reject deployment without an approval token" belongs in code for enforcement.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Estimate Context and Operating Cost
&lt;/h3&gt;

&lt;p&gt;Capture a real prompt trace and count the skill metadata, activated instructions, tool definitions, and returned data. Then add the non-token cost of an MCP service: deployment, authentication, monitoring, versioning, and on-call ownership.&lt;/p&gt;

&lt;p&gt;If a 30-tool catalog supports one workflow, expose a task-specific subset or split the server by coherent capability domain. If a skill repeatedly loads a 200-page reference, create a retrieval step or smaller references instead of congratulating yourself on progressive disclosure.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Test the Boundary, Then the Behavior
&lt;/h3&gt;

&lt;p&gt;Test the MCP server as software and the skill as agent behavior. They fail differently, and a single happy-path chat transcript hides both classes of defect.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Test focus&lt;/th&gt;
&lt;th&gt;Example assertion&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Skill&lt;/td&gt;
&lt;td&gt;Selection and procedure&lt;/td&gt;
&lt;td&gt;Activates for incidents but not general support questions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skill&lt;/td&gt;
&lt;td&gt;Judgment&lt;/td&gt;
&lt;td&gt;Cites two signals before assigning high severity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP server&lt;/td&gt;
&lt;td&gt;Contract&lt;/td&gt;
&lt;td&gt;Rejects missing fields and malformed identifiers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP server&lt;/td&gt;
&lt;td&gt;Authorization&lt;/td&gt;
&lt;td&gt;Denies cross-tenant reads and under-scoped writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP server&lt;/td&gt;
&lt;td&gt;Reliability&lt;/td&gt;
&lt;td&gt;A retried note does not create a duplicate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integrated trace&lt;/td&gt;
&lt;td&gt;End-to-end behavior&lt;/td&gt;
&lt;td&gt;Requests approval, detects version conflict, and stops safely&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For tool safety, the MCP specification recommends input validation, access controls, rate limits, output sanitization, timeouts, confirmations for sensitive operations, and audit logging. Tool annotations are hints, not trusted proof that an operation is read-only or harmless. The &lt;a href="https://www.glukhov.org/llm-architecture/guardrails/a2a-mcp-agent-security/" rel="noopener noreferrer"&gt;A2A and MCP agent security guide&lt;/a&gt; covers the broader threat model including prompt injection and tool poisoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Rules That Do Not Fit in a Slogan
&lt;/h2&gt;

&lt;p&gt;Skills reduce the need for some servers, but they do not remove risk. A skill can include scripts and can persuade an agent to call powerful host tools, so review its instructions and executable files as code, pin trusted versions, and limit the host tools available to the session.&lt;/p&gt;

&lt;p&gt;MCP adds another boundary: a local subprocess or remote service with its own dependencies, inputs, outputs, and credentials. Apply least privilege, validate resource audience for remote authorization, use HTTPS, sanitize untrusted content, and keep approval visible for consequential writes.&lt;/p&gt;

&lt;p&gt;Most importantly, do not confuse discoverability with authority. A tool appearing in the model's catalog does not mean the current user should be allowed to execute every operation it describes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Anti-Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hiding a Remote API Client in a Skill
&lt;/h3&gt;

&lt;p&gt;A shell script that reads a static bearer token and calls a production API may work in a demo. It also mixes procedure, credentials, network behavior, and authorization into a package designed to be copied and read by agent hosts.&lt;/p&gt;

&lt;p&gt;Move the protected integration behind a narrow server or an existing approved CLI. Keep only the workflow and calling guidance in the skill.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encoding the Workflow in Tool Descriptions
&lt;/h3&gt;

&lt;p&gt;Tool descriptions should help the model select a capability and fill its schema. They are a poor substitute for a multi-step operating procedure with examples, exceptions, escalation rules, and output conventions.&lt;/p&gt;

&lt;p&gt;Long descriptions inflate every turn in which the tool is exposed and make the service contract harder to reuse. Put the procedure in a skill and keep tool semantics precise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building an &lt;code&gt;execute_anything&lt;/code&gt; Tool
&lt;/h3&gt;

&lt;p&gt;A generic shell, SQL, or HTTP proxy collapses many permissions into one difficult-to-audit capability. It shifts validation to the model and makes least privilege mostly fictional.&lt;/p&gt;

&lt;p&gt;Expose operations aligned to actual business actions. If expert operators truly need an escape hatch, separate it, restrict it, and require stronger approval and logging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Publishing a Kitchen-Sink MCP Server
&lt;/h3&gt;

&lt;p&gt;A server with dozens of unrelated tools burdens selection, schema context, permissions, and maintenance. Split by coherent domain or let the host expose a relevant subset for the current task.&lt;/p&gt;

&lt;p&gt;Hermes Agent's FastMCP guidance makes a sensible starting recommendation: begin with one to three high-value endpoints and prefer a thin server with clear names and schemas. See the official &lt;a href="https://hermes-agent.nousresearch.com/docs/user-guide/skills/optional/mcp/mcp-fastmcp" rel="noopener noreferrer"&gt;FastMCP skill documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Treating Tool Hints as Security Policy
&lt;/h3&gt;

&lt;p&gt;An experimental &lt;code&gt;allowed-tools&lt;/code&gt; field or a tool's read-only annotation can improve host behavior, but neither replaces sandboxing and server-side authorization. Metadata may be stale, misconfigured, or supplied by an untrusted component.&lt;/p&gt;

&lt;p&gt;Use hints to improve the interface. Use code and infrastructure to enforce the boundary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using MCP for Static Knowledge
&lt;/h3&gt;

&lt;p&gt;If a procedure or reference changes only with the repository, a remote round trip adds deployment and availability costs without making the information more authoritative. Package concise material with the skill and version it with the workflow.&lt;/p&gt;

&lt;p&gt;Introduce a retrieval service only when the corpus is large, access-controlled, independently updated, or genuinely needs search. Architecture should follow the data lifecycle, not the acronym.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Decision: Skill, MCP Server, or Both?
&lt;/h2&gt;

&lt;p&gt;Choose an Agent Skill when the hard part is knowing what to do. Choose an MCP server when the hard part is safely reaching something that changes, belongs to another trust domain, or must enforce a contract.&lt;/p&gt;

&lt;p&gt;Choose both when a real workflow needs judgment above a protected capability. That is not duplication: the skill makes the agent useful, the server makes the integration governable, and the backing system makes the final decision authoritative.&lt;/p&gt;

&lt;p&gt;For most hosted assistants, the best first architecture is modest: one focused skill, a small MCP surface only where live access demands it, and a captured prompt trace to verify the context cost. Add complexity after the boundary is clear, not before.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://agentskills.io/specification" rel="noopener noreferrer"&gt;Agent Skills specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/basic" rel="noopener noreferrer"&gt;Model Context Protocol specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/docs/2026-07-28/tutorials/security/authorization" rel="noopener noreferrer"&gt;MCP authorization guidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/NousResearch/hermes-agent/blob/main/skills/autonomous-ai-agents/hermes-agent/references/native-mcp.md" rel="noopener noreferrer"&gt;Hermes Agent native MCP documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.openclaw.ai/tools/skills" rel="noopener noreferrer"&gt;OpenClaw skills documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hermes-agent.nousresearch.com/docs/user-guide/skills/optional/mcp/mcp-fastmcp" rel="noopener noreferrer"&gt;FastMCP skill documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>hermes</category>
      <category>openclaw</category>
      <category>aicoding</category>
    </item>
    <item>
      <title>OpenCode CLI in Practice: Workflows, Automation, and Pitfalls</title>
      <dc:creator>Rost</dc:creator>
      <pubDate>Tue, 01 Sep 2026 10:29:55 +0000</pubDate>
      <link>https://dev.to/rosgluk/opencode-cli-in-practice-workflows-automation-and-pitfalls-3e69</link>
      <guid>https://dev.to/rosgluk/opencode-cli-in-practice-workflows-automation-and-pitfalls-3e69</guid>
      <description>&lt;p&gt;OpenCode's command-line interface is built for scripting, CI pipelines, and unattended agent runs. This article is a practical guide to using it in daily work.&lt;/p&gt;

&lt;p&gt;Behind the CLI sits a system of models, tools, permissions, agents, skills, commands, sessions, MCP servers, and a client/server architecture. The agent can read and edit project files, search repositories, execute shell commands, call external tools, and delegate work to subagents. The same environment runs interactively in the TUI or non-interactively from scripts.&lt;/p&gt;

&lt;p&gt;For small tasks, you can install it, connect a model, and start asking questions within minutes. For serious work, the quality of the experience depends heavily on model selection, repository instructions, permission boundaries, context management, and how aggressively you allow the agent to operate. This article focuses on that second stage, from the command line: which use cases pay off, which automation workflows hold up in daily use, and which problems appear after the novelty of an AI-powered terminal wears off. It is part of the &lt;a href="https://www.glukhov.org/ai-devtools/" rel="noopener noreferrer"&gt;AI Developer Tools&lt;/a&gt; section of this site.&lt;/p&gt;

&lt;p&gt;The observations here were checked against OpenCode 1.18.9 and the current documentation in August 2026. OpenCode changes rapidly, so configuration examples deserve a quick documentation check before being copied into a long-lived team setup. If you have not installed OpenCode yet, the &lt;a href="https://www.glukhov.org/ai-devtools/opencode/" rel="noopener noreferrer"&gt;OpenCode quickstart&lt;/a&gt; covers installation, verification, and provider connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  What OpenCode Actually Is: An Agent Environment, Not a Chat Box
&lt;/h2&gt;

&lt;p&gt;OpenCode is an open-source AI coding agent designed around the terminal. A simplified view of what it wires together looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    U[Developer] --&amp;gt; T[OpenCode CLI or TUI]

    T --&amp;gt; A[Primary Agent]
    A --&amp;gt; M[Selected LLM]
    A --&amp;gt; F[File Tools]
    A --&amp;gt; S[Shell]
    A --&amp;gt; W[Web Tools]
    A --&amp;gt; L[LSP and Code Intelligence]
    A --&amp;gt; X[MCP Tools]
    A --&amp;gt; C[Skills and Commands]
    A --&amp;gt; G[Subagents]

    F --&amp;gt; R[Repository]
    S --&amp;gt; R
    L --&amp;gt; R
    G --&amp;gt; M

    P[Permission Rules] --&amp;gt; A
    I[AGENTS.md] --&amp;gt; A&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The important part is the permission layer between a model's intent and the actions OpenCode can perform. An excellent model with bad permissions can be dangerous. A weak model with perfect permissions is merely slow and annoying. Productive OpenCode use requires getting both sides reasonably right.&lt;/p&gt;

&lt;p&gt;The same environment serves both surfaces: the interactive TUI and the non-interactive command line. That is what makes OpenCode scriptable in a way a pure chat interface is not. If you want a deliberately minimal take on the same terminal-agent idea — four default tools, no built-in sandbox, everything else via extensions — the &lt;a href="https://www.glukhov.org/ai-devtools/pi/pi-coding-agent-review/" rel="noopener noreferrer"&gt;Pi Coding Agent review&lt;/a&gt; is a useful contrast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup: Install, Connect, and Why Provider Independence Matters
&lt;/h2&gt;

&lt;p&gt;OpenCode installs in one line — official install script, npm, or Homebrew — and starts with &lt;code&gt;opencode&lt;/code&gt; from a repository directory. The &lt;a href="https://www.glukhov.org/ai-devtools/opencode/" rel="noopener noreferrer"&gt;OpenCode quickstart&lt;/a&gt; covers the full install matrix (Arch, Windows, Docker), verification, and provider connection (&lt;code&gt;/connect&lt;/code&gt; and &lt;code&gt;/models&lt;/code&gt;), so this article does not repeat it.&lt;/p&gt;

&lt;p&gt;You can use OpenCode's own model services or connect supported external providers. OpenCode currently builds much of its provider catalog using Models.dev and supports a wide range of commercial and local model configurations.&lt;/p&gt;

&lt;p&gt;Provider independence is one of OpenCode's most useful architectural decisions. Your coding workflow does not need to be permanently coupled to one model vendor: you can use one model for difficult architecture work, another for cheap implementation tasks, and a local model for code that should not leave your environment.&lt;/p&gt;

&lt;p&gt;That flexibility is real, but it creates another variable to manage. When OpenCode performs badly, the problem may be the harness, the prompt, the available context, the selected model, or the interaction between all four.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize the Repository with AGENTS.md Before Asking for Code
&lt;/h2&gt;

&lt;p&gt;One of the first commands worth running in a new repository is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OpenCode analyzes the project and creates an &lt;code&gt;AGENTS.md&lt;/code&gt; file. Commit that file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; is where repository-specific constraints can become durable context for every conversation — interactive or scripted — instead of being repeated by hand. A useful file is short enough to remain relevant but concrete enough to prevent predictable mistakes. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Repository Instructions&lt;/span&gt;

&lt;span class="gu"&gt;## Architecture&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; API handlers live under src/api.
&lt;span class="p"&gt;-&lt;/span&gt; Business logic belongs under src/services.
&lt;span class="p"&gt;-&lt;/span&gt; Database access belongs under src/repositories.
&lt;span class="p"&gt;-&lt;/span&gt; Do not call database clients directly from HTTP handlers.

&lt;span class="gu"&gt;## Validation&lt;/span&gt;

After TypeScript changes run:

&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;bash
&lt;/span&gt;npm run typecheck
npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;

After frontend changes also run:

&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;bash
&lt;/span&gt;npm run lint
&lt;span class="p"&gt;```&lt;/span&gt;

&lt;span class="gu"&gt;## Constraints&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Do not modify generated files.
&lt;span class="p"&gt;-&lt;/span&gt; Do not change public API contracts without asking first.
&lt;span class="p"&gt;-&lt;/span&gt; Do not create database migrations unless explicitly requested.
&lt;span class="p"&gt;-&lt;/span&gt; Never run deployment commands.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is less exciting than installing another MCP server, but it usually provides more value. Coding agents fail surprisingly often because they do not know which constraints are important. A short repository contract removes some of that ambiguity before the first tool call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Work from the Command Line with &lt;code&gt;opencode run&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The interactive interface receives most of the attention, but OpenCode's non-interactive mode changes the range of useful workflows considerably:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opencode run &lt;span class="s2"&gt;"Explain the error handling strategy in this package"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use OpenCode from shell scripts, CI jobs, Makefiles, task runners, or local automation without manually entering the TUI every time. For example, a diff review as a one-shot command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opencode run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"Review the current git diff for correctness and missing tests. Do not edit files."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or pipe context straight into a run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; HEAD~1 |
  opencode run &lt;span class="s2"&gt;"Inspect the changed files and identify risky behavior changes."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a Makefile, the same call becomes a target:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;review&lt;/span&gt;
&lt;span class="nl"&gt;review&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    opencode run &lt;span class="s2"&gt;"Review the current git diff for correctness and missing tests. Do not edit files."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because a scripted run has no human at the prompt, the permission policy you configure is the only guardrail standing between the model and your environment. That is why the permissions section below matters more for automation than for interactive use.&lt;/p&gt;

&lt;p&gt;The interesting direction here is not replacing deterministic scripts with an LLM. It is inserting model reasoning into places where traditional shell logic becomes awkward, while keeping deterministic validation around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Best OpenCode CLI Use Cases
&lt;/h2&gt;

&lt;p&gt;OpenCode can attempt almost any programming task, but that does not mean every task should be delegated in the same way. The highest-value workflows tend to have three properties: the desired result is testable, the relevant repository context can be discovered, and incorrect changes are cheap to inspect or revert. Each prompt below works in the TUI or as an argument to &lt;code&gt;opencode run&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Repository exploration
&lt;/h3&gt;

&lt;p&gt;OpenCode is excellent at answering questions that would otherwise require a sequence of &lt;code&gt;grep&lt;/code&gt;, editor searches, file jumps, and &lt;code&gt;git log&lt;/code&gt; commands. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Explain how authentication works in this repository.

Trace a request from the HTTP middleware through token validation,
user loading, authorization, and the final handler.

Do not modify anything.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A good agent will search for entry points, follow references, inspect tests, and return something closer to an architecture walkthrough than a plain text search. This is one of the safest ways to introduce OpenCode into an existing codebase because the agent can provide value without writing code.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Small, well-bounded fixes
&lt;/h3&gt;

&lt;p&gt;A narrowly scoped bug is close to the ideal coding-agent task. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The CLI exits with status 0 when config validation fails.

Find the code path responsible, add a regression test, implement
the smallest fix, and run the relevant tests.

Do not refactor unrelated code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key phrase is not "fix the bug." It is the constraints surrounding the task. OpenCode works better when success can be demonstrated with a test, a compiler, or an observable command. Vague requirements give the model room to create plausible code rather than demonstrably correct code.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Test generation after implementation
&lt;/h3&gt;

&lt;p&gt;Tests are useful agent work because the existing implementation gives OpenCode something concrete to reason about. A productive prompt might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review src/parser.ts and its existing tests.

Identify important edge cases that are currently uncovered.
Add tests only. Do not modify the implementation.

Run the parser test suite when finished.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Separating test generation from implementation is important. If the same agent writes both the feature and the tests in one unconstrained pass, it can accidentally create tests that validate its own interpretation rather than the intended behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Mechanical refactoring
&lt;/h3&gt;

&lt;p&gt;OpenCode is very good at repetitive transformations where the desired end state is clear. Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;replacing a deprecated API across a repository;&lt;/li&gt;
&lt;li&gt;converting repeated code to a shared helper;&lt;/li&gt;
&lt;li&gt;renaming a configuration field;&lt;/li&gt;
&lt;li&gt;migrating tests from one assertion pattern to another;&lt;/li&gt;
&lt;li&gt;updating imports after moving a package;&lt;/li&gt;
&lt;li&gt;replacing an obsolete logging abstraction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository's compiler and tests become the agent's feedback loop. A useful pattern is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Replace uses of LegacyResult&amp;lt;T&amp;gt; with Result&amp;lt;T, AppError&amp;gt; in
packages/api only.

Preserve runtime behavior.

Work in small batches. After each batch run the package typecheck.
At the end run the package test suite and show me the final git diff
summary.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is often more reliable than asking for the entire migration in one enormous step.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Code review
&lt;/h3&gt;

&lt;p&gt;OpenCode becomes much more useful when review is treated as a separate agent role rather than another prompt sent to the same editing context. You can create a review-oriented agent that cannot edit files. Then give it instructions such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review the current git diff.

Focus on:
- correctness;
- security;
- concurrency;
- missing tests;
- error handling;
- accidental API changes.

Do not summarize files that are unchanged.
Rank findings by severity.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A read-only reviewer is useful even if another coding agent produced the changes. The separation is valuable because implementation and criticism are different tasks: an agent that has just spent several thousand tokens defending one approach is often less skeptical of that approach than a fresh reviewer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Plan and Build Modes as Different Mental Modes
&lt;/h2&gt;

&lt;p&gt;OpenCode provides primary agents and subagents, with built-in workflows including planning and implementation-oriented behavior. Even when the exact agent configuration changes over time, the conceptual separation remains useful.&lt;/p&gt;

&lt;p&gt;Planning should answer questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which files matter?&lt;/li&gt;
&lt;li&gt;What existing patterns should be followed?&lt;/li&gt;
&lt;li&gt;What could break?&lt;/li&gt;
&lt;li&gt;How will we verify the change?&lt;/li&gt;
&lt;li&gt;Is the requested change actually local?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementation should happen only after those questions have reasonable answers. For substantial tasks, I prefer a prompt sequence like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;First investigate the request.

Do not edit files yet.

Return:
1. the relevant files;
2. the current behavior;
3. the proposed change;
4. risks;
5. the exact verification commands.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same sequence works as a one-shot command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opencode run &lt;span class="s2"&gt;"First investigate the request. Do not edit files yet. Return: the relevant files; the current behavior; the proposed change; risks; the exact verification commands."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then inspect the plan before allowing edits. This feels slower than immediately telling an agent to "implement it," but the expensive failures in agentic coding usually come from incorrect assumptions made before the first edit.&lt;/p&gt;

&lt;p&gt;This is a lightweight, per-task version of the same instinct behind &lt;a href="https://www.glukhov.org/ai-devtools/vibe-coding/spec-driven-development-vs-vibe-coding/" rel="noopener noreferrer"&gt;spec-driven development&lt;/a&gt;: agree on the plan before the agent starts editing. For work that spans multiple files, sessions, or contributors, a written spec earns its overhead in a way a single investigate-first prompt cannot; the &lt;a href="https://www.glukhov.org/ai-devtools/ai-coding-assistants/spec-kit-vs-kiro-vs-claude-code/" rel="noopener noreferrer"&gt;GitHub Spec Kit vs Kiro vs Claude Code comparison&lt;/a&gt; covers structured SDD workflows that go further than a single prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subagents Are Useful, but Delegation Is Not Free
&lt;/h2&gt;

&lt;p&gt;OpenCode can invoke specialized subagents automatically or through explicit mentions. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@general find where retry behavior is implemented
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also define dedicated subagents for jobs such as security review, dependency analysis, frontend testing, or documentation. The same pattern works in other harnesses; the &lt;a href="https://www.glukhov.org/ai-devtools/claude-code/claude-code-subagents/" rel="noopener noreferrer"&gt;Claude Code subagents guide&lt;/a&gt; covers the analogous design on the Anthropic side.&lt;/p&gt;

&lt;p&gt;This is powerful because subagent work can remain outside the primary conversation's immediate reasoning path. A primary agent can delegate repository exploration and consume the result instead of filling its own context with every intermediate search. But subagents create three less obvious costs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;They consume tokens.&lt;/strong&gt; A tree of agents that each re-read the repository can become surprisingly expensive — my &lt;a href="https://www.glukhov.org/ai-devtools/opencode/oh-my-opencode-experience/" rel="noopener noreferrer"&gt;Oh My Opencode experience report&lt;/a&gt; documents what happens when that overhead is taken to the extreme.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They create policy complexity.&lt;/strong&gt; Permissions must be considered for the delegated agent, not only for the parent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delegation can hide reasoning.&lt;/strong&gt; When the primary agent says "the subagent found X," you may need to inspect the child session to understand how reliable that conclusion actually is.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For most coding tasks, two or three purposeful agents are more useful than an elaborate fictional software company living inside your terminal.&lt;/p&gt;

&lt;p&gt;If the task genuinely needs a full orchestrator delegating to a fixed roster of specialists — parallel background execution, planning and research phases, model routing per role — that is a different product built on top of these primitives, not a bigger prompt. The &lt;a href="https://www.glukhov.org/ai-devtools/opencode/oh-my-opencode/" rel="noopener noreferrer"&gt;Oh My Opencode quickstart&lt;/a&gt; covers that harness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Custom Agents as Permission Boundaries
&lt;/h2&gt;

&lt;p&gt;OpenCode agents can have separate prompts, models, and permissions. That makes agents useful as security and workflow boundaries, not merely different personalities. For example, a project-local review agent can be defined as a Markdown file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reviews&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;without&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;modifying&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;repository"&lt;/span&gt;
&lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;subagent&lt;/span&gt;
&lt;span class="na"&gt;permission&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;edit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deny&lt;/span&gt;
  &lt;span class="na"&gt;bash&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ask&lt;/span&gt;
    &lt;span class="s"&gt;"git diff *"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allow&lt;/span&gt;
    &lt;span class="s"&gt;"git status *"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allow&lt;/span&gt;
    &lt;span class="s"&gt;"git log *"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allow&lt;/span&gt;
  &lt;span class="na"&gt;webfetch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deny&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

Review code for correctness, security, maintainability,
unexpected behavior changes, and missing tests.

Do not modify files.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much better than writing "please do not edit anything" in prose. Instructions influence the model. Permissions constrain the tool. Those are not equivalent controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure OpenCode Permissions on Day One
&lt;/h2&gt;

&lt;p&gt;One of OpenCode's most important practical characteristics is that its normal defaults are permissive. That is convenient during a demo and not necessarily what I want on a workstation containing SSH keys, production credentials, package publishing tokens, Kubernetes contexts, and access to several cloud accounts. For unattended &lt;code&gt;opencode run&lt;/code&gt; jobs, the stakes are higher still: nothing stops a run except the policy you configure.&lt;/p&gt;

&lt;p&gt;OpenCode's current permission model supports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;allow
ask
deny
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rules can be applied to file access, edits, shell commands, web operations, subagents, skills, external directories, and other tool categories. A conservative starting point might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://opencode.ai/config.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permission"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ask"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"grep"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"glob"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"edit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ask"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ask"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"git status *"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"git diff *"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"git log *"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"git push *"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"rm *"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deny"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact rules should match your environment. What matters is making the policy intentional rather than discovering the default after the agent has already executed something surprising.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do not confuse approval prompts with sandboxing
&lt;/h3&gt;

&lt;p&gt;Permission rules are useful, but they are not the same as operating-system isolation. If OpenCode has permission to execute an allowed shell command, that process runs with the access available to your environment. A coding agent can potentially interact with files, network services, environment variables, credentials, sockets, package managers, and other developer tooling.&lt;/p&gt;

&lt;p&gt;For sensitive repositories or unattended runs, stronger isolation is worth considering:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    H[Host Workstation] --&amp;gt; B[Restricted Container or VM]
    B --&amp;gt; O[OpenCode]
    O --&amp;gt; R[Repository Copy]
    O --&amp;gt; T[Build and Test Tools]
    O --&amp;gt; K[Limited Model Credentials]

    P[No Production Credentials] --&amp;gt; B
    N[Restricted Network Access] --&amp;gt; B&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Git is rollback. Permissions are policy. A container or VM is isolation. Those are three different layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn Repetitive Prompts into Custom Commands
&lt;/h2&gt;

&lt;p&gt;If you repeatedly type the same instructions, they should probably stop being chat history and become configuration. OpenCode supports project and global custom slash commands for the interactive interface. For example, create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.opencode/commands/review.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Review the current changes&lt;/span&gt;
&lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;plan&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

Review the current git diff.

Look for:
&lt;span class="p"&gt;-&lt;/span&gt; bugs;
&lt;span class="p"&gt;-&lt;/span&gt; security issues;
&lt;span class="p"&gt;-&lt;/span&gt; incomplete error handling;
&lt;span class="p"&gt;-&lt;/span&gt; missing tests;
&lt;span class="p"&gt;-&lt;/span&gt; accidental API changes.

Do not modify files.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a small feature with disproportionate value. Reliable coding-agent workflows emerge when good prompts become shared project infrastructure rather than personal clipboard snippets. Other useful project commands might include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/test-changes
/review
/prepare-pr
/check-migration
/update-docs
/release-check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use Skills for Reusable Workflows
&lt;/h2&gt;

&lt;p&gt;OpenCode also supports &lt;code&gt;SKILL.md&lt;/code&gt; files. Skills are useful when a workflow needs more than a single prompt. A skill can contain detailed operational guidance and supporting files while remaining unloaded until the agent actually needs it. Useful candidates include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;database migration procedures;&lt;/li&gt;
&lt;li&gt;release workflows;&lt;/li&gt;
&lt;li&gt;incident investigation;&lt;/li&gt;
&lt;li&gt;API compatibility reviews;&lt;/li&gt;
&lt;li&gt;package publishing;&lt;/li&gt;
&lt;li&gt;infrastructure validation;&lt;/li&gt;
&lt;li&gt;internal architecture conventions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.opencode/skills/database-migration/SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A skill description might tell OpenCode when the procedure applies, while the body explains how to inspect schemas, create migrations, validate rollback behavior, and run integration tests. If you already use the equivalent concept in another harness, the &lt;a href="https://www.glukhov.org/ai-devtools/claude-code/claude-skills-for-developers/" rel="noopener noreferrer"&gt;Claude Skills guide for developers&lt;/a&gt; maps the same design decisions.&lt;/p&gt;

&lt;p&gt;The advantage is context discipline. Dumping every organizational rule into &lt;code&gt;AGENTS.md&lt;/code&gt; eventually creates a giant system prompt that is expensive and increasingly easy for the model to ignore. Skills let specialized instructions enter context only when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP: Useful Until the Tool Catalog Becomes the Problem
&lt;/h2&gt;

&lt;p&gt;OpenCode supports local and remote MCP servers. That can expose issue trackers, documentation systems, browsers, observability platforms, databases, APIs, and other tools to the coding agent. A typical configuration might provide a documentation server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://opencode.ai/config.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"context7"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"remote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.context7.com/mcp"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MCP is one of those features that is easy to overuse. Every tool the agent must understand consumes attention and frequently context tokens. A setup with fifteen MCP servers may look powerful in a configuration screenshot while making actual model behavior slower, more expensive, and less predictable.&lt;/p&gt;

&lt;p&gt;My rule is simple: if a tool is not useful in a normal week of work, it probably should not be enabled globally. Load tools because a workflow requires them, not because the integration exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Models in OpenCode: A Real Option, Not a Magic Fix
&lt;/h2&gt;

&lt;p&gt;One of OpenCode's strongest use cases is its ability to work with local or self-hosted OpenAI-compatible model endpoints. This is attractive when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;source code must remain local;&lt;/li&gt;
&lt;li&gt;API costs are significant;&lt;/li&gt;
&lt;li&gt;you already operate GPU infrastructure;&lt;/li&gt;
&lt;li&gt;you want to experiment with open models;&lt;/li&gt;
&lt;li&gt;internet connectivity is unreliable;&lt;/li&gt;
&lt;li&gt;model routing is part of your platform.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A custom provider can point OpenCode to a local endpoint such as LM Studio, &lt;a href="https://www.glukhov.org/llm-hosting/llama-cpp/" rel="noopener noreferrer"&gt;llama.cpp&lt;/a&gt; via &lt;code&gt;llama-server&lt;/code&gt;, vLLM, Ollama-compatible infrastructure, or another OpenAI-compatible server.&lt;/p&gt;

&lt;p&gt;This is where expectations matter. A good coding harness cannot compensate completely for a model that is weak at tool use, long-horizon planning, code reasoning, or instruction retention. Community discussions around local OpenCode setups repeatedly converge on the same observation: local models can be excellent for bounded tasks, but the smaller ones often need more supervision. For measured numbers on how specific models actually behave inside OpenCode, see my &lt;a href="https://www.glukhov.org/ai-devtools/opencode/llms-comparison/" rel="noopener noreferrer"&gt;hands-on LLM comparison for OpenCode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The practical solution is model routing by task complexity. Use a cheaper or local model for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;repository search;&lt;/li&gt;
&lt;li&gt;documentation;&lt;/li&gt;
&lt;li&gt;simple tests;&lt;/li&gt;
&lt;li&gt;repetitive edits;&lt;/li&gt;
&lt;li&gt;formatting changes;&lt;/li&gt;
&lt;li&gt;straightforward bug fixes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use a stronger model for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;architectural changes;&lt;/li&gt;
&lt;li&gt;ambiguous bugs;&lt;/li&gt;
&lt;li&gt;cross-package refactors;&lt;/li&gt;
&lt;li&gt;concurrency;&lt;/li&gt;
&lt;li&gt;security-sensitive code;&lt;/li&gt;
&lt;li&gt;difficult migrations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Provider independence makes this strategy possible. It does not make model quality irrelevant.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Daily Workflow with OpenCode
&lt;/h2&gt;

&lt;p&gt;My preferred workflow is deliberately conservative. It works the same in the TUI or via &lt;code&gt;opencode run&lt;/code&gt;; where a step is a prompt, the command-line variant is shown alongside.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Start from a clean Git state
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Either commit existing changes or deliberately record what is already modified. An AI coding agent operating inside a dirty working tree makes review much harder because human changes and agent changes become mixed together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Ask for investigation first
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Investigate issue #482.

Do not modify files.

Explain:
- the current behavior;
- likely root cause;
- relevant files;
- existing tests;
- proposed fix;
- verification commands.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same prompt as a one-shot command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opencode run &lt;span class="s2"&gt;"Investigate issue #482. Do not modify files. Explain the current behavior, likely root cause, relevant files, existing tests, proposed fix, and verification commands."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the investigation is wrong, correcting it is cheap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Narrow the implementation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Implement the proposed fix only.

Do not refactor unrelated code.
Add the regression test first.
Run the smallest relevant test suite after the change.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or non-interactively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opencode run &lt;span class="s2"&gt;"Implement the proposed fix only. Do not refactor unrelated code. Add the regression test first. Run the smallest relevant test suite after the change."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent now has a smaller decision space.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Inspect the diff yourself
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git diff &lt;span class="nt"&gt;--stat&lt;/span&gt;
git diff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not outsource this step to another model entirely. You are checking not only whether the code looks plausible, but whether the agent changed files it did not need to touch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Run deterministic verification
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run typecheck
npm &lt;span class="nb"&gt;test
&lt;/span&gt;npm run lint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the actual project commands. "OpenCode says the tests pass" is not stronger evidence than your terminal displaying a successful test process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Run an independent review
&lt;/h3&gt;

&lt;p&gt;Ask a read-only agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review the uncommitted diff as if it were a pull request written
by another engineer.

Try to find reasons this implementation is wrong.

Do not edit files.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command-line variant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opencode run &lt;span class="s2"&gt;"Review the uncommitted diff as if it were a pull request written by another engineer. Try to find reasons this implementation is wrong. Do not edit files."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The phrase "try to find reasons this is wrong" is intentional. Models are very good at politely confirming plausible work. Review prompts should encourage falsification rather than applause.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Commit only after the diff makes sense
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nt"&gt;-p&lt;/span&gt;
git commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I still prefer interactive staging after agent-generated changes. It forces one final human pass across every hunk that becomes part of the repository history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where OpenCode Becomes Frustrating: Common Pitfalls
&lt;/h2&gt;

&lt;p&gt;The interesting limitations are not usually "the AI made a syntax error." Compilers are good at catching those. The difficult problems come from autonomy, context, hidden assumptions, and configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 1: Model quality dominates the experience
&lt;/h3&gt;

&lt;p&gt;The same OpenCode workflow can feel excellent with one model and almost unusable with another. This makes product reviews difficult because users often attribute model behavior to the agent harness.&lt;/p&gt;

&lt;p&gt;If OpenCode repeatedly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ignores instructions;&lt;/li&gt;
&lt;li&gt;rewrites too much code;&lt;/li&gt;
&lt;li&gt;calls tools incorrectly;&lt;/li&gt;
&lt;li&gt;loops on the same failed action;&lt;/li&gt;
&lt;li&gt;loses track of constraints;&lt;/li&gt;
&lt;li&gt;invents APIs;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;try another model before redesigning the entire OpenCode configuration. The terminal did not suddenly become smarter. The model did.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 2: Long sessions accumulate bad context
&lt;/h3&gt;

&lt;p&gt;Coding-agent sessions become less trustworthy as incorrect assumptions accumulate. An early mistake such as "this service is stateless" can influence dozens of later decisions even after the relevant files have changed.&lt;/p&gt;

&lt;p&gt;OpenCode supports compaction to manage context limits, but compression creates its own tradeoff. A summary necessarily decides which details survive. For major task transitions, starting a fresh session is often cleaner than continuing a heroic 80,000-token conversation. With scripted runs the answer is even simpler: a new &lt;code&gt;opencode run&lt;/code&gt; starts with a clean context every time, so long-lived state belongs in files like &lt;code&gt;AGENTS.md&lt;/code&gt;, not in a conversation.&lt;/p&gt;

&lt;p&gt;Context is not free memory. It is working state, and working state gets stale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 3: Permissions can become deceptively complicated
&lt;/h3&gt;

&lt;p&gt;Simple rules are easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;read -&amp;gt; allow
edit -&amp;gt; ask
git push -&amp;gt; deny
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Complex agent hierarchies are harder. Once primary agents can invoke subagents, custom tools, MCP servers, and shell commands, you need to reason about the effective capabilities of the whole workflow rather than one configuration line.&lt;/p&gt;

&lt;p&gt;There have also been real community and GitHub discussions about subagent permission behavior and permission inheritance. The lesson is broader than any one bug: test security assumptions with an actual disposable repository — for example, &lt;code&gt;git init /tmp/opencode-perm-test&lt;/code&gt; and try to make the agent run a command you denied there. If an action absolutely must not occur, do not rely on prose instructions alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 4: OpenCode changes quickly
&lt;/h3&gt;

&lt;p&gt;OpenCode has shipped at a remarkable pace. That is good for features and less pleasant for documentation longevity. A particularly easy trap in 2026 is finding configuration examples from a different OpenCode generation. Current documentation also contains separate V2 material whose configuration schema differs from the established 1.x syntax. For example, permission concepts may appear under different field names in V2 documentation.&lt;/p&gt;

&lt;p&gt;Do not casually combine examples from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/docs/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/v2/docs/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;without checking which runtime you are actually using. When troubleshooting a configuration copied from a blog post, check its publication date before assuming OpenCode is broken.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 5: The TUI can hide scale
&lt;/h3&gt;

&lt;p&gt;A conversational interface makes a ten-file change feel smaller than a ten-file change. The agent may report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Implemented the new validation and updated the relevant tests.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That sentence could represent three lines or 600 lines. Keep independent shell tools close:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status &lt;span class="nt"&gt;--short&lt;/span&gt;
git diff &lt;span class="nt"&gt;--stat&lt;/span&gt;
git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt;
git diff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The coding agent should not be the only interface through which you observe the coding agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 6: MCP can destroy context efficiency
&lt;/h3&gt;

&lt;p&gt;More integrations do not automatically produce a better coding agent. Large MCP servers can expose many tool schemas, each consuming model context and increasing tool-selection complexity. If OpenCode feels oddly indecisive after you installed half the MCP ecosystem, disable most of it and compare. A smaller tool surface often produces better agent behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 7: Local privacy requires verifying the whole path
&lt;/h3&gt;

&lt;p&gt;Running a local model does not automatically guarantee that every part of a toolchain is local. Community discussions in early 2026 raised privacy questions around OpenCode's auxiliary model behavior and web interface architecture. Some of those claims referred to older versions, some were disputed, and some code paths have since changed.&lt;/p&gt;

&lt;p&gt;The durable lesson is not "OpenCode sends everything somewhere." The durable lesson is: if local-only operation is a hard requirement, verify it. Use network inspection, read the current configuration, understand which interface you are using, disable unnecessary remote integrations, and test the exact version you intend to deploy. Security requirements should be validated, not inferred from a product category.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenCode vs Claude Code and Codex CLI
&lt;/h2&gt;

&lt;p&gt;OpenCode's strongest differentiator is not necessarily coding quality. The underlying model still contributes heavily to coding quality. Its differentiator is control over the harness.&lt;/p&gt;

&lt;p&gt;OpenCode is compelling when you value:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an open-source implementation;&lt;/li&gt;
&lt;li&gt;provider flexibility;&lt;/li&gt;
&lt;li&gt;terminal-first workflows;&lt;/li&gt;
&lt;li&gt;configurable agents;&lt;/li&gt;
&lt;li&gt;granular permissions;&lt;/li&gt;
&lt;li&gt;local-model support;&lt;/li&gt;
&lt;li&gt;MCP;&lt;/li&gt;
&lt;li&gt;reusable commands and skills;&lt;/li&gt;
&lt;li&gt;client/server architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.glukhov.org/ai-devtools/claude-code/" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; is compelling when you want a tightly integrated Anthropic experience with strong first-party model behavior and increasingly polished built-in agent workflows. Codex CLI is compelling when your preferred models and workflow are already centered on OpenAI's coding stack.&lt;/p&gt;

&lt;p&gt;I would not choose among them by counting features. Choose based on which layer you want to own. If you want a vendor to make most workflow decisions, a first-party coding agent can be attractive. If you want the harness to remain replaceable while you experiment with providers and agent configurations, OpenCode makes a stronger argument.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Reddit and Hacker News Get Right About OpenCode
&lt;/h2&gt;

&lt;p&gt;Community discussions around OpenCode are unusually polarized. Some developers describe it as their favorite coding harness, especially when combined with Codex models, local models, or custom agent setups. Others focus on resource usage, permission behavior, privacy questions, provider authentication changes, or the complexity that appears once a seemingly simple terminal application becomes infrastructure. Both perspectives are reasonable.&lt;/p&gt;

&lt;p&gt;OpenCode is not difficult because the command line is difficult. It is difficult because an autonomous coding environment exposes questions developers previously did not need to answer explicitly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which model should make this decision?&lt;/li&gt;
&lt;li&gt;Which files may it read?&lt;/li&gt;
&lt;li&gt;Which commands may it execute?&lt;/li&gt;
&lt;li&gt;Which credentials can the process see?&lt;/li&gt;
&lt;li&gt;Which tasks should become subagents?&lt;/li&gt;
&lt;li&gt;How much context should survive?&lt;/li&gt;
&lt;li&gt;Which tools deserve permanent context?&lt;/li&gt;
&lt;li&gt;Which result must a human verify?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A polished closed product can make many of those decisions for you. OpenCode makes more of them yours. That is precisely why advanced users like it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Starting Configuration I Would Recommend
&lt;/h2&gt;

&lt;p&gt;I would resist building an elaborate setup immediately. Start with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;one strong default model;&lt;/li&gt;
&lt;li&gt;one &lt;code&gt;AGENTS.md&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;conservative shell and edit permissions;&lt;/li&gt;
&lt;li&gt;a read-only review agent;&lt;/li&gt;
&lt;li&gt;two or three custom commands;&lt;/li&gt;
&lt;li&gt;no MCP servers until a real need appears.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A simple environment is easier to debug. Once a workflow becomes repetitive, promote it into a command, skill, or agent. Once a capability becomes risky, restrict it with permissions. Once context becomes bloated, split the workflow instead of merely buying a larger context window. This incremental approach is less impressive in screenshots and considerably more pleasant to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Should Use OpenCode
&lt;/h2&gt;

&lt;p&gt;OpenCode is a particularly good fit for developers who already live in terminals and want their coding agent to behave like another programmable development tool. I would strongly consider it if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;work across multiple LLM providers;&lt;/li&gt;
&lt;li&gt;want local-model support;&lt;/li&gt;
&lt;li&gt;dislike being locked to one AI vendor;&lt;/li&gt;
&lt;li&gt;need project-specific agents;&lt;/li&gt;
&lt;li&gt;automate development tasks from shell scripts;&lt;/li&gt;
&lt;li&gt;want to inspect or modify the coding harness;&lt;/li&gt;
&lt;li&gt;already understand Git and normal CLI tooling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is less compelling if you primarily want an invisible AI layer inside an IDE. OpenCode also expects more operational judgment than a traditional autocomplete tool. If reviewing diffs, understanding shell commands, and managing Git branches already feel uncomfortable, giving an autonomous process access to those tools will not make the underlying complexity disappear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Verdict
&lt;/h2&gt;

&lt;p&gt;OpenCode is one of the more convincing examples of an AI coding tool becoming developer infrastructure rather than merely a chat interface. Its best features are not flashy. Provider independence, explicit permissions, reusable agents, non-interactive execution, repository instructions, commands, skills, and composable tools make it possible to shape the agent around a real engineering workflow.&lt;/p&gt;

&lt;p&gt;The weakness is the mirror image of that strength. OpenCode gives you enough control to create a disciplined coding environment, but it also gives you enough control to create a complicated, expensive, poorly isolated swarm of agents with twenty MCP servers and no clear verification boundary.&lt;/p&gt;

&lt;p&gt;I would not optimize OpenCode for maximum autonomy. I would optimize it for short feedback loops. Give it a bounded problem, enough context to understand the problem, permission to perform only the necessary actions, and deterministic commands that can prove whether the result works. That is less magical than asking an agent to build an entire application while you sleep. It is also much closer to how OpenCode becomes genuinely useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;OpenCode documentation: &lt;a href="https://opencode.ai/docs/" rel="noopener noreferrer"&gt;https://opencode.ai/docs/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenCode changelog: &lt;a href="https://opencode.ai/changelog" rel="noopener noreferrer"&gt;https://opencode.ai/changelog&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenCode GitHub repository: &lt;a href="https://github.com/anomalyco/opencode" rel="noopener noreferrer"&gt;https://github.com/anomalyco/opencode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Models.dev provider catalog: &lt;a href="https://models.dev" rel="noopener noreferrer"&gt;https://models.dev&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opencode</category>
      <category>cheatsheet</category>
      <category>aicoding</category>
      <category>ai</category>
    </item>
    <item>
      <title>Superpowers Quickstart: Install, Workflow, and Tryout</title>
      <dc:creator>Rost</dc:creator>
      <pubDate>Mon, 24 Aug 2026 11:32:26 +0000</pubDate>
      <link>https://dev.to/rosgluk/superpowers-quickstart-install-workflow-and-tryout-505n</link>
      <guid>https://dev.to/rosgluk/superpowers-quickstart-install-workflow-and-tryout-505n</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/obra/superpowers" rel="noopener noreferrer"&gt;Superpowers&lt;/a&gt; packages a full spec-driven methodology into installable Claude Skills, enforcing brainstorming, planning, subagent-driven implementation, and strict TDD instead of leaving that structure up to you.&lt;/p&gt;

&lt;p&gt;Most Claude Code SDD setups fail the same way: someone writes a solid &lt;code&gt;specify-plan-implement&lt;/code&gt; skill once, uses it diligently for a week, then quietly drifts back to unstructured prompting the first time a deadline gets close. Nothing in a hand-rolled skill actually stops that. Superpowers is a direct answer to that discipline problem -- a skills package built by Jesse Vincent and the team at Prime Radiant that treats brainstorming, planning, subagent review, and red-green-refactor TDD as mandatory steps the agent checks before every task, not optional suggestions it can skip under pressure.&lt;/p&gt;

&lt;p&gt;What makes it worth a dedicated look rather than a footnote is the install story. Superpowers ships plugin manifests for Claude Code, Cursor, Codex, Gemini CLI, GitHub Copilot CLI, Grok Build CLI, Kimi Code, OpenCode, Pi, Devin CLI, Factory Droid, and Hermes Agent, so the same methodology follows you across harnesses instead of living in one repo's &lt;code&gt;.claude/skills/&lt;/code&gt; folder. This guide covers what it actually is, how to install and verify it on the agent you use, what a first end-to-end run looks like, and where it fits next to other spec-driven development tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Superpowers?
&lt;/h2&gt;

&lt;p&gt;Superpowers describes itself as "a complete software development methodology for your coding agents, built on top of a set of composable skills and some initial instructions that make sure your agent uses them." That last clause is the part that matters. Plenty of skill collections exist; the differentiator here is the bootstrap instruction that forces the agent to check for relevant skills before starting any task, so the workflow activates automatically instead of requiring you to remember a slash command.&lt;/p&gt;

&lt;p&gt;The philosophy behind it is stated plainly in the project's own docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test-Driven Development&lt;/strong&gt; -- write tests first, always&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Systematic over ad-hoc&lt;/strong&gt; -- process over guessing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity reduction&lt;/strong&gt; -- simplicity as the primary goal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evidence over claims&lt;/strong&gt; -- verify before declaring success&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, that philosophy shows up as a sequence of skills that hand off to each other. If you have read &lt;a href="https://www.glukhov.org/ai-devtools/claude-code/claude-skills-for-developers/" rel="noopener noreferrer"&gt;Claude Skills and SKILL.md for Developers&lt;/a&gt;, the mechanics will feel familiar -- Superpowers is a large, opinionated set of exactly that kind of &lt;code&gt;SKILL.md&lt;/code&gt; directory, distributed as an installable plugin rather than something you author yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Superpowers Across Coding Agents
&lt;/h2&gt;

&lt;p&gt;Installation is harness-specific, and the project is explicit that you install it separately for each agent you use -- there is no single global install.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;

&lt;p&gt;Superpowers is distributed through two marketplaces for &lt;a href="https://www.glukhov.org/ai-devtools/claude-code/" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Official Anthropic plugin marketplace
/plugin install superpowers@claude-plugins-official
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or through the dedicated Superpowers marketplace, which also carries a few related plugins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/plugin marketplace add obra/superpowers-marketplace
/plugin install superpowers@superpowers-marketplace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cursor
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/add-plugin superpowers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also search "superpowers" directly in Cursor's plugin marketplace UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other agents
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;Install command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Codex App&lt;/td&gt;
&lt;td&gt;Plugins sidebar -&amp;gt; Coding section -&amp;gt; install Superpowers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex CLI&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/plugins&lt;/code&gt;, search &lt;code&gt;superpowers&lt;/code&gt;, select Install Plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Antigravity&lt;/td&gt;
&lt;td&gt;&lt;code&gt;agy plugin install https://github.com/obra/superpowers&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Devin CLI&lt;/td&gt;
&lt;td&gt;&lt;code&gt;devin plugins install obra/superpowers&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Factory Droid&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;droid plugin marketplace add https://github.com/obra/superpowers&lt;/code&gt; then &lt;code&gt;droid plugin install superpowers@superpowers&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini CLI&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemini extensions install https://github.com/obra/superpowers&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Copilot CLI&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;copilot plugin marketplace add obra/superpowers-marketplace&lt;/code&gt; then &lt;code&gt;copilot plugin install superpowers@superpowers-marketplace&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok Build CLI&lt;/td&gt;
&lt;td&gt;&lt;code&gt;grok plugin install superpowers@xai-official --trust&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kimi Code&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/plugins install https://github.com/obra/superpowers&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenCode&lt;/td&gt;
&lt;td&gt;Fetch and follow &lt;code&gt;.opencode/INSTALL.md&lt;/code&gt; from the repo (separate install even if already used elsewhere)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pi&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pi install git:github.com/obra/superpowers&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hermes Agent&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hermes plugins install obra/superpowers --enable&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few install details are worth flagging before you run a real task:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Antigravity&lt;/strong&gt; runs the plugin's session-start hook automatically, so Superpowers is active from the first message; reinstall with the same command to update.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pi&lt;/strong&gt; loads the skills through a small extension that injects the &lt;code&gt;using-superpowers&lt;/code&gt; bootstrap at session startup and after context compaction, and does not require Pi's compatibility &lt;code&gt;Skill&lt;/code&gt; tool since Pi has native skill support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hermes Agent&lt;/strong&gt; has no post-compaction hook -- a very long session that compacts over its first turn can lose the bootstrap, so start a fresh session if skills stop triggering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenCode&lt;/strong&gt; treats this as a completely separate install path from any other harness you already use, even on the same machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Verify the install
&lt;/h3&gt;

&lt;p&gt;Once installed, ask the agent directly rather than trusting a silent success message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What skills are available?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Superpowers installed correctly, the agent should list skills such as &lt;code&gt;brainstorming&lt;/code&gt;, &lt;code&gt;writing-plans&lt;/code&gt;, &lt;code&gt;test-driven-development&lt;/code&gt;, and &lt;code&gt;subagent-driven-development&lt;/code&gt; among the results. This is the same discovery check recommended for &lt;a href="https://www.glukhov.org/ai-devtools/claude-code/claude-skills-for-developers/" rel="noopener noreferrer"&gt;debugging Claude Skills that fail to trigger&lt;/a&gt; -- if the skill exists but never activates, the routing description is almost always the problem, not the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Superpowers Workflow: Brainstorm to Shipped Code
&lt;/h2&gt;

&lt;p&gt;Superpowers documents seven skills as "the basic workflow," each one activating at a specific point and handing off to the next:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
  A[brainstorming] --&amp;gt; B[using-git-worktrees]
  B --&amp;gt; C[writing-plans]
  C --&amp;gt; D{subagent-driven-development&amp;lt;br/&amp;gt;or executing-plans}
  D --&amp;gt; E[test-driven-development]
  E --&amp;gt; F[requesting-code-review]
  F --&amp;gt; G[finishing-a-development-branch]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;brainstorming&lt;/strong&gt; activates before any code gets written. It refines a rough idea through questions, explores alternatives, and presents the design in short, reviewable chunks rather than one long document -- then saves the result as a design artifact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;using-git-worktrees&lt;/strong&gt; activates once you approve the design. It creates an isolated workspace on a new branch, runs project setup, and verifies a clean test baseline before implementation starts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;writing-plans&lt;/strong&gt; breaks the approved design into bite-sized tasks -- Superpowers targets two to five minutes of work per task -- with exact file paths, complete code where relevant, and explicit verification steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;subagent-driven-development&lt;/strong&gt; (fast iteration) or &lt;strong&gt;executing-plans&lt;/strong&gt; (batch execution with human checkpoints) dispatches a fresh subagent per task, with a two-stage review: spec compliance first, then code quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;test-driven-development&lt;/strong&gt; enforces strict red-green-refactor -- write a failing test, watch it fail, write the minimal code to pass, watch it pass, commit. Code written before a failing test existed for it gets deleted, not kept.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;requesting-code-review&lt;/strong&gt; activates between tasks, reviewing the diff against the plan and reporting issues by severity; critical issues block progress to the next task.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;finishing-a-development-branch&lt;/strong&gt; activates once all tasks complete. It verifies tests pass, then presents options -- merge, open a PR, keep the branch, or discard -- and cleans up the worktree.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This maps closely onto the tool-neutral five-phase loop in &lt;a href="https://www.glukhov.org/app-architecture/documentation/spec-driven-development-workflow/" rel="noopener noreferrer"&gt;Spec-Driven Development Workflow From Requirements to Code&lt;/a&gt;: specify becomes brainstorming, plan becomes writing-plans, tasks and implement fold into subagent-driven-development, and validate is split across TDD and code review. The difference is enforcement -- Superpowers is built so the agent checks for these skills automatically rather than relying on you to invoke each phase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying It Out: A First Task Walkthrough
&lt;/h2&gt;

&lt;p&gt;Do not point Superpowers at your most important feature on the first run. Pick something real but bounded -- a small endpoint, a focused bug fix, a single module refactor -- and start a session in a project that already has a test suite, since TDD enforcement is most visible there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I want to add rate limiting to our public API endpoints.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Superpowers installed, expect the session to open with brainstorming questions rather than code: which endpoints, what limit, per-user or per-key, what should happen when the limit is hit. That friction is intentional -- it is the same non-goals discipline covered in &lt;a href="https://www.glukhov.org/app-architecture/documentation/spec-driven-development-workflow/" rel="noopener noreferrer"&gt;Spec-Driven Development Workflow From Requirements to Code&lt;/a&gt;, just enforced by the skill rather than left to you to remember. Once you approve the design in chunks, expect a plan with numbered, small tasks before any file changes, then one subagent dispatched per task with a diff you review at each checkpoint.&lt;/p&gt;

&lt;p&gt;Two things to watch on a first run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Confirm TDD is actually enforced.&lt;/strong&gt; Ask the agent to implement one task and check whether a failing test appears before the implementation. If code lands first, the skill did not trigger for that step, and it is worth re-running the discovery check from the install section.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch task granularity.&lt;/strong&gt; The two-to-five-minute task sizing is aggressive for some codebases. If tasks feel needlessly fragmented for your project, that is worth raising during the writing-plans review checkpoint rather than after the fact.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Inside: The Skills Library
&lt;/h2&gt;

&lt;p&gt;Beyond the seven workflow skills, Superpowers ships supporting skills grouped by area:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Skills&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Testing&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;test-driven-development&lt;/code&gt; (includes testing anti-patterns reference)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debugging&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;systematic-debugging&lt;/code&gt; (4-phase root cause process, includes root-cause-tracing, defense-in-depth, condition-based-waiting), &lt;code&gt;verification-before-completion&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collaboration&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;brainstorming&lt;/code&gt;, &lt;code&gt;writing-plans&lt;/code&gt;, &lt;code&gt;executing-plans&lt;/code&gt;, &lt;code&gt;dispatching-parallel-agents&lt;/code&gt;, &lt;code&gt;requesting-code-review&lt;/code&gt;, &lt;code&gt;receiving-code-review&lt;/code&gt;, &lt;code&gt;using-git-worktrees&lt;/code&gt;, &lt;code&gt;finishing-a-development-branch&lt;/code&gt;, &lt;code&gt;subagent-driven-development&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meta&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;writing-skills&lt;/code&gt; (create new skills with testing methodology), &lt;code&gt;using-superpowers&lt;/code&gt; (bootstrap and introduction)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;dispatching-parallel-agents&lt;/code&gt; is worth calling out separately: it covers concurrent subagent workflows, which is the same territory as &lt;a href="https://www.glukhov.org/ai-devtools/claude-code/claude-code-subagents/" rel="noopener noreferrer"&gt;Claude Code subagents&lt;/a&gt; -- isolated context, independent review, and a summary reported back to the main session instead of flooding it with intermediate output.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Superpowers Fits (and When It Doesn't)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Good fit:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You already like the Claude Code skills approach to &lt;a href="https://www.glukhov.org/ai-devtools/vibe-coding/spec-driven-development-vs-vibe-coding/" rel="noopener noreferrer"&gt;Spec-Driven Development vs Vibe Coding&lt;/a&gt; but keep sliding back to unstructured prompting because nothing enforces the review gates.&lt;/li&gt;
&lt;li&gt;You work across multiple coding agents and want one methodology that travels with you instead of a Claude Code-only skill.&lt;/li&gt;
&lt;li&gt;You want strict TDD enforcement without writing the enforcement logic yourself.&lt;/li&gt;
&lt;li&gt;Multi-session features where architectural drift and forgotten constraints are a real risk -- the exact failure mode described in &lt;a href="https://www.glukhov.org/ai-devtools/vibe-coding/spec-driven-development-vs-vibe-coding/" rel="noopener noreferrer"&gt;Spec-Driven Development vs Vibe Coding&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaker fit:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quick scripts, throwaway prototypes, and one-off automation -- the brainstorming and planning overhead is real, and it is not worth paying on a fifty-line change.&lt;/li&gt;
&lt;li&gt;Teams with an existing, well-tuned project-specific SDD skill. Superpowers trades that customization for enforced, opinionated ceremony; you may prefer to keep what already works and borrow only individual skill ideas.&lt;/li&gt;
&lt;li&gt;Environments where the strict two-to-five-minute task granularity or mandatory TDD would conflict with an established team process you are not ready to change.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Superpowers Differs from Other SDD Tools
&lt;/h2&gt;

&lt;p&gt;Superpowers is not a competitor to GitHub Spec Kit or AWS Kiro in the usual sense -- it operates at the Claude Code / cross-agent skills layer rather than as a standalone CLI or IDE. Spec Kit gives you portable markdown artifacts and agent independence; Kiro gives you a guided, integrated IDE experience; a hand-rolled Claude Code skill gives you maximum flexibility with zero enforcement. Superpowers sits between the last two: same skills-based mechanism as a DIY Claude Code workflow, but with mandatory-feeling enforcement and cross-agent plugin distribution that a repo-local skill does not provide.&lt;/p&gt;

&lt;p&gt;For the full breakdown of portability, setup friction, review gates, and lock-in across Spec Kit, Kiro, Claude Code skills, OpenSpec, BMAD-METHOD, and Tessl -- including where Superpowers lands in the comparison table and decision framework -- see &lt;a href="https://www.glukhov.org/ai-devtools/ai-coding-assistants/spec-kit-vs-kiro-vs-claude-code/" rel="noopener noreferrer"&gt;GitHub Spec Kit vs Kiro vs Claude Code SDD Workflows&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting and Things to Know
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Updates are mostly automatic but agent-dependent.&lt;/strong&gt; How current your install stays depends on the harness -- Antigravity re-runs the session-start hook on reinstall, npm- or Git-based installs need an explicit update command, and marketplace-based installs typically follow the host agent's own plugin update flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Telemetry is on by default but easy to disable.&lt;/strong&gt; The brainstorming skill's optional visual companion loads the Prime Radiant logo from their website, which includes your Superpowers version but no project, prompt, or click data. Set &lt;code&gt;SUPERPOWERS_DISABLE_TELEMETRY&lt;/code&gt; to any true value to turn it off. Superpowers also honors Claude Code's own &lt;code&gt;DISABLE_TELEMETRY&lt;/code&gt; and &lt;code&gt;CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC&lt;/code&gt; opt-outs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contributions require the &lt;code&gt;dev&lt;/code&gt; branch.&lt;/strong&gt; If you want to modify or add skills, fork the repository, switch to &lt;code&gt;dev&lt;/code&gt;, and follow the &lt;code&gt;writing-skills&lt;/code&gt; skill for creating and testing changes -- new skill contributions specifically are generally not accepted, since every skill must work identically across all supported agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If a skill does not trigger, treat it like any other Claude Skill problem.&lt;/strong&gt; Ask "What skills are available?" to confirm discovery, check that you installed for the specific agent you are currently using (not a different one on the same machine), and remember that Hermes sessions need a fresh start after long-session compaction if the bootstrap silently drops.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Superpowers is worth installing when the gap between your SDD intentions and what actually happens in a session is the problem you are trying to solve. It does not introduce a new specify-plan-implement idea -- that loop is already well covered by &lt;a href="https://www.glukhov.org/app-architecture/documentation/what-is-spec-driven-development/" rel="noopener noreferrer"&gt;What Is Spec-Driven Development?&lt;/a&gt; -- it makes an existing idea harder to skip, and it does that across more agents than a repo-local skill can reach. Install it on the one harness you use daily first, run a small bounded task through the full brainstorm-to-finish cycle, and decide from there whether the enforced ceremony earns its keep on your actual workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/obra/superpowers" rel="noopener noreferrer"&gt;Superpowers repository&lt;/a&gt; -- source, skills, and plugin manifests for every supported agent&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/obra/superpowers-marketplace" rel="noopener noreferrer"&gt;Superpowers marketplace&lt;/a&gt; -- Claude Code marketplace for Superpowers and related plugins&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.glukhov.org/ai-devtools/ai-coding-assistants/spec-kit-vs-kiro-vs-claude-code/" rel="noopener noreferrer"&gt;GitHub Spec Kit vs Kiro vs Claude Code SDD Workflows&lt;/a&gt; -- full tool comparison and decision framework&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.glukhov.org/ai-devtools/claude-code/claude-skills-for-developers/" rel="noopener noreferrer"&gt;Claude Skills and SKILL.md for Developers&lt;/a&gt; -- the underlying skill mechanics Superpowers builds on&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.glukhov.org/ai-devtools/claude-code/claude-code-subagents/" rel="noopener noreferrer"&gt;Claude Code Subagents: Setup, Config, and When to Use Them&lt;/a&gt; -- isolated-context review, the mechanism behind subagent-driven-development&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.glukhov.org/app-architecture/documentation/spec-driven-development-workflow/" rel="noopener noreferrer"&gt;Spec-Driven Development Workflow From Requirements to Code&lt;/a&gt; -- the tool-neutral five-phase process Superpowers automates&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.glukhov.org/app-architecture/documentation/what-is-spec-driven-development/" rel="noopener noreferrer"&gt;What Is Spec-Driven Development? The Spec as Source of Truth&lt;/a&gt; -- core SDD concepts and terminology&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.glukhov.org/ai-devtools/claude-code/" rel="noopener noreferrer"&gt;Claude Code install and config for Ollama, llama.cpp, pricing&lt;/a&gt; -- install Claude Code itself before adding Superpowers&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aicoding</category>
      <category>llm</category>
      <category>ai</category>
      <category>dev</category>
    </item>
    <item>
      <title>Pi Coding Agent Review: Minimal, Hackable AI Coding CLI</title>
      <dc:creator>Rost</dc:creator>
      <pubDate>Sun, 23 Aug 2026 10:56:24 +0000</pubDate>
      <link>https://dev.to/rosgluk/pi-coding-agent-review-minimal-hackable-ai-coding-cli-4ge8</link>
      <guid>https://dev.to/rosgluk/pi-coding-agent-review-minimal-hackable-ai-coding-cli-4ge8</guid>
      <description>&lt;p&gt;Pi Coding Agent is a minimal, open-source terminal coding harness that ships with four default tools and leaves most of the behaviour to extensions, skills and your own workflow.&lt;/p&gt;

&lt;p&gt;Most AI coding agents are becoming bigger products. They add planning modes, subagents, permission layers, IDE integrations, background workers and increasingly elaborate orchestration around the model. Pi takes almost the opposite approach: the default agent starts with four basic tools — &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, &lt;code&gt;edit&lt;/code&gt; and &lt;code&gt;bash&lt;/code&gt; — while most of the interesting behaviour is deliberately left to extensions, skills, packages and your own workflow.&lt;/p&gt;

&lt;p&gt;This review is aligned with Pi v0.84.2, released on 14 August 2026. Mario Zechner created the project; it moved to Earendil Works in May 2026, which is why current packages use the &lt;code&gt;@earendil-works&lt;/code&gt; scope rather than the older &lt;code&gt;@mariozechner&lt;/code&gt; names. Pi has been changing quickly, so configuration and extension APIs deserve a version check before you standardise them across a team.&lt;/p&gt;

&lt;p&gt;The catch is equally important: Pi gives you considerably more control than many coding agents, and correspondingly more responsibility. It does not provide a built-in security sandbox or the kind of permission system developers may expect after using Claude Code or similar tools. Below I cover the quickstart, architecture, model support, extensions, session model, security implications and where I think Pi fits among modern AI coding tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Pi Coding Agent?
&lt;/h2&gt;

&lt;p&gt;Pi is best understood as an agent harness rather than a finished, opinionated coding environment. That distinction matters: a coding model answers prompts, while an agent harness decides what context that model sees, which tools it can call, how tool results return to it, how sessions persist and what happens between turns. Pi makes almost all of those layers accessible, which is why it sits at the kit-building end of the &lt;a href="https://www.glukhov.org/ai-devtools/" rel="noopener noreferrer"&gt;AI developer tools&lt;/a&gt; field rather than among the batteries-included coding products.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;My rating&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;Small core with unusually clear extension points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model flexibility&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;Broad provider support and easy model switching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extensibility&lt;/td&gt;
&lt;td&gt;10/10&lt;/td&gt;
&lt;td&gt;TypeScript extensions can alter tools, events, context and UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session handling&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;Tree-based sessions make experimentation unusually natural&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Initial usability&lt;/td&gt;
&lt;td&gt;8/10&lt;/td&gt;
&lt;td&gt;Easy to start, but advanced use expects technical confidence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safety defaults&lt;/td&gt;
&lt;td&gt;5/10&lt;/td&gt;
&lt;td&gt;No built-in sandbox or comprehensive permission boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team governance&lt;/td&gt;
&lt;td&gt;6/10&lt;/td&gt;
&lt;td&gt;Possible to build, but much is intentionally not built in&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The strongest reason to use Pi is not that it has more features than competing coding agents — it does not. The reason is that Pi exposes more of the agent itself. That makes it particularly attractive to senior developers, platform engineers, AI tooling teams and anyone who has reached the point where the limitations of their coding agent are caused by the harness rather than the underlying model. Pi is less convincing for someone who wants to install an agent, approve a few safe defaults and never think about its architecture again: its minimalism is productive only if you value the control that minimalism creates.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Pi actually gives you
&lt;/h3&gt;

&lt;p&gt;The default model-facing tool set is intentionally small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;read
write
edit
bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additional read-only tools including &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt; and &lt;code&gt;ls&lt;/code&gt; are available, and recent Pi releases allow the initial built-in tool selection to be configured.&lt;/p&gt;

&lt;p&gt;The default looks sparse, and that is the point. Every additional tool increases the number of decisions the model has to make, expands the system prompt and creates another behavioural surface that may need debugging. Pi instead starts from a capable primitive set and lets you add specialised tools when your workflow actually needs them.&lt;/p&gt;

&lt;p&gt;The philosophy extends further than tools. Pi deliberately does not make built-in subagents or a mandatory plan mode central to the product; those behaviours can be implemented through extensions or installed packages instead. That makes Pi less convenient out of the box, but it gives developers more control over how those mechanisms are implemented. If you are used to Claude Code's built-in subagents, the &lt;a href="https://www.glukhov.org/ai-devtools/claude-code/claude-code-subagents/" rel="noopener noreferrer"&gt;Claude Code subagents guide&lt;/a&gt; is a useful reference for what you would be reimplementing. I find that approach slightly refreshing: Pi does not pretend there is one correct way to operate an AI coding agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to install Pi Coding Agent and start a session
&lt;/h2&gt;

&lt;p&gt;Install the current package from the &lt;code&gt;@earendil-works&lt;/code&gt; scope. Older tutorials may still show &lt;code&gt;@mariozechner&lt;/code&gt; packages from before the May 2026 move; those names are stale for new installs.&lt;/p&gt;

&lt;p&gt;The name also collides with &lt;code&gt;oh-my-pi&lt;/code&gt;, a community fork of the Oh My Opencode harness. That project is unrelated to this coding agent; the &lt;a href="https://www.glukhov.org/ai-devtools/opencode/oh-my-opencode-experience/" rel="noopener noreferrer"&gt;Oh My Opencode review&lt;/a&gt; explains the fork if you landed on the wrong Pi.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install and authenticate
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Install Pi globally with npm:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="nt"&gt;--ignore-scripts&lt;/span&gt; @earendil-works/pi-coding-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--ignore-scripts&lt;/code&gt; disables dependency lifecycle scripts during install. Pi does not need those scripts for a normal npm install, and skipping them reduces a common supply-chain risk.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify the installation:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   pi &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Enter a project and start it:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/project
   pi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pi expects a bash-capable environment. On Windows, use WSL or Git Bash rather than &lt;code&gt;cmd.exe&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pi supports interactive subscription authentication as well as API-key based providers. Inside Pi, the simplest route is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After authentication, select a model with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also supply provider credentials through environment variables. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-api-key"&lt;/span&gt;
pi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pi currently supports a broad collection of model providers, including OpenAI, Anthropic, Google, Azure OpenAI, Amazon Bedrock, NVIDIA NIM, DeepSeek, Mistral, Groq, Cerebras, Cloudflare, xAI, OpenRouter and several others. A &lt;code&gt;llama.cpp&lt;/code&gt; router is also supported for locally served models: the &lt;a href="https://www.glukhov.org/llm-hosting/llama-cpp/" rel="noopener noreferrer"&gt;llama.cpp quickstart&lt;/a&gt; covers serving GGUF models with an OpenAI-compatible API, and the &lt;a href="https://www.glukhov.org/llm-hosting/" rel="noopener noreferrer"&gt;LLM hosting guide&lt;/a&gt; maps the surrounding local, self-hosted and cloud runtimes if you want to compare that path with Ollama, vLLM or a hosted provider. Custom providers that speak a supported API can be added through &lt;code&gt;~/.pi/agent/models.json&lt;/code&gt;, and custom APIs or OAuth flows can be wired up with extensions.&lt;/p&gt;

&lt;p&gt;This multi-provider design is one of Pi's practical advantages. The model and the coding harness are separate variables: with Pi, you can change the model while keeping essentially the same tooling and session environment, which makes model comparisons considerably more meaningful than comparing completely different coding products.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start with a controlled first session
&lt;/h3&gt;

&lt;p&gt;I would not make your first Pi prompt "refactor my application". Start by asking it to inspect rather than modify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pi &lt;span class="nt"&gt;--tools&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt;,grep,find,ls &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Inspect this repository. Explain its architecture, identify the main entry points, and list the commands you would run before making a change. Do not modify files."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I ran that read-only prompt on an existing repository, the useful result was not a clever architecture essay. It was whether the model named real entry points and the commands I would actually run before asking for write access. That gives you a look at how the selected model navigates your tree without immediately granting it a write path through the normal tool set.&lt;/p&gt;

&lt;p&gt;For interactive work, I would also create a disposable Git branch first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch &lt;span class="nt"&gt;-c&lt;/span&gt; ai/pi-evaluation
pi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pi can modify files in the working directory and can execute shell commands through &lt;code&gt;bash&lt;/code&gt;, so Git remains one of the simplest practical rollback layers when evaluating it. If you want a test session that does not persist, &lt;code&gt;pi --no-session&lt;/code&gt; runs in ephemeral mode.&lt;/p&gt;

&lt;p&gt;Project instructions belong in &lt;code&gt;AGENTS.md&lt;/code&gt;. A useful starting file might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Project Instructions&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Read the existing implementation before modifying files.
&lt;span class="p"&gt;-&lt;/span&gt; Keep changes narrowly scoped to the requested task.
&lt;span class="p"&gt;-&lt;/span&gt; Run &lt;span class="sb"&gt;`npm test`&lt;/span&gt; after code changes.
&lt;span class="p"&gt;-&lt;/span&gt; Run &lt;span class="sb"&gt;`npm run lint`&lt;/span&gt; before declaring the task complete.
&lt;span class="p"&gt;-&lt;/span&gt; Do not modify database migrations unless explicitly requested.
&lt;span class="p"&gt;-&lt;/span&gt; Do not access production infrastructure.
&lt;span class="p"&gt;-&lt;/span&gt; Explain any destructive command before running it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pi also understands &lt;code&gt;CLAUDE.md&lt;/code&gt; while walking project directories, and &lt;code&gt;AGENTS.override.md&lt;/code&gt; can override the normal project instructions for a directory.&lt;/p&gt;

&lt;p&gt;Then give it a concrete task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read the authentication module and its tests.

Find one maintainability problem that can be fixed without changing public behaviour. Explain the proposed change first, then implement it and run the relevant tests.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a better agent evaluation than asking it to generate a new toy application. Existing code forces the agent to discover constraints, preserve behaviour, choose relevant files and verify its work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Pi Coding Agent is an editable harness
&lt;/h2&gt;

&lt;p&gt;Pi's most important architectural idea is that the coding agent itself should remain editable. The extension system uses TypeScript modules that can register tools, subscribe to lifecycle events, intercept tool calls, inject or transform context, add commands and modify the terminal interface. Project-local extensions can also be reloaded with &lt;code&gt;/reload&lt;/code&gt;, which makes experimenting with the harness surprisingly immediate.&lt;/p&gt;

&lt;p&gt;In simplified form, Pi looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    U[Developer] --&amp;gt; P[Pi Agent Harness]
    P --&amp;gt; M[Selected LLM Provider]

    M --&amp;gt; P

    P --&amp;gt; R[read]
    P --&amp;gt; W[write]
    P --&amp;gt; E[edit]
    P --&amp;gt; B[bash]

    X[TypeScript Extensions] --&amp;gt; P
    S[Skills] --&amp;gt; P
    C[Project Context] --&amp;gt; P
    PKG[Pi Packages] --&amp;gt; P

    R --&amp;gt; FS[Project Workspace]
    W --&amp;gt; FS
    E --&amp;gt; FS
    B --&amp;gt; OS[Shell and Toolchain]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The diagram is simple because Pi is trying to keep the control plane simple. That gives you an unusual option: when the agent lacks a capability, you do not necessarily have to wait for the Pi maintainers to add it — you can add the behaviour yourself. An extension can create a new model-callable tool, inspect a proposed &lt;code&gt;bash&lt;/code&gt; call and block commands matching your own policy, insert dynamic project information before a turn, alter compaction behaviour or expose a custom command inside the terminal UI. That is a much deeper extension surface than changing a system prompt.&lt;/p&gt;

&lt;p&gt;MCP integration is not treated as a mandatory built-in part of Pi's minimal core. It can be added through the same extensibility model, which is consistent with the broader design: specialised integrations live above the small default harness rather than permanently expanding it.&lt;/p&gt;

&lt;p&gt;The same harness is also reusable outside the interactive terminal. Pi ships an SDK for embedding the agent session in Node.js applications, and an RPC mode (&lt;code&gt;pi --mode rpc&lt;/code&gt;) that speaks JSON over stdin/stdout for non-Node hosts, IDEs and custom UIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  The four-tool idea is more serious than it looks
&lt;/h3&gt;

&lt;p&gt;I initially thought about coding-agent tools in two groups. The first contains primitives: read a file, modify a file and execute a command. The second contains conveniences: repository search, test runners, issue trackers, browser tools, deployment tools, database helpers, code intelligence and specialised planners. Pi keeps the primitive layer small and allows the convenience layer to remain optional.&lt;/p&gt;

&lt;p&gt;There is an architectural advantage to this. A team working on Kubernetes infrastructure can build a very different Pi from a team maintaining a TypeScript monorepo, without both teams carrying the other's tools in the same default agent. This is where Pi stops looking like a stripped-down Claude Code alternative and starts looking like a framework for building your own coding agent.&lt;/p&gt;

&lt;p&gt;The price is maintenance. Once you depend on custom extensions, their behaviour becomes part of your developer platform: you need to review them, version them and occasionally update them as Pi evolves. That is the hidden Pi tax — you gain ownership of the agent, and ownership comes with maintenance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skills and packages keep customisation modular
&lt;/h3&gt;

&lt;p&gt;Not every custom behaviour needs to become an extension. Pi supports skills that package instructions, scripts and reference material around a capability. Skills use progressive disclosure: Pi can advertise the available skill to the model while loading the full instructions only when the capability is needed. That is useful for workflows such as deployment reviews, database migrations, release preparation or framework-specific code review; instead of permanently injecting pages of instructions into every conversation, the agent can load specialised guidance on demand. If you already build Claude Skills, the mechanics will feel familiar — see &lt;a href="https://www.glukhov.org/ai-devtools/claude-code/claude-skills-for-developers/" rel="noopener noreferrer"&gt;Claude Skills and SKILL.md for developers&lt;/a&gt; for the SKILL.md layout and trigger tuning.&lt;/p&gt;

&lt;p&gt;Pi Packages go further by bundling extensions, skills, prompt templates and themes. Packages can be installed from npm or Git repositories and can be pinned to specific versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pi &lt;span class="nb"&gt;install &lt;/span&gt;npm:@foo/pi-tools@1.2.3
pi list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package model is powerful, but treat packages more like executable developer tooling than harmless prompt collections. A Pi extension can execute code with the permissions of the Pi process, and a skill can also instruct the model to execute code, so installing an unknown package deserves essentially the same suspicion as installing an unknown development dependency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Session trees in Pi Coding Agent
&lt;/h2&gt;

&lt;p&gt;Many coding assistants treat a conversation as a mostly linear transcript. Pi stores sessions as a tree, which means you can return to an earlier point, try another approach and retain both paths inside the session rather than destroying one history or creating a pile of unrelated chats.&lt;/p&gt;

&lt;p&gt;The main commands are straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/resume
/tree
/fork
/clone
/compact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;/tree&lt;/code&gt; lets you navigate within the current session tree. &lt;code&gt;/fork&lt;/code&gt; creates a new session from an earlier user message, while &lt;code&gt;/clone&lt;/code&gt; duplicates the currently active branch into a separate session. From the command line, &lt;code&gt;pi -c&lt;/code&gt; continues the most recent session and &lt;code&gt;pi -r&lt;/code&gt; browses past ones.&lt;/p&gt;

&lt;p&gt;The distinction became obvious the first time I used Pi on a debugging pass rather than a greenfield prompt. I let it follow a caching-layer hypothesis for a failing integration test, then forked from the message where that theory had been chosen and started a second branch on transaction isolation. The failed investigation stayed available for contrast instead of contaminating the new path or forcing a fresh chat, which is a useful representation of how debugging actually works: multiple plausible explanations often exist at the same time.&lt;/p&gt;

&lt;p&gt;Pi persists sessions as JSONL and can automatically compact older context as conversations approach model limits; branches can also be summarised when you move between them. I would rank this above several more visible coding-agent features: good context management usually matters more than another button in the interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context engineering is a first-class capability
&lt;/h3&gt;

&lt;p&gt;Pi also makes it possible to customise what reaches the model. Extensions can inject information before turns, alter history and participate in compaction. The default compaction system summarises older material while retaining recent conversation, but the mechanism can be replaced or customised.&lt;/p&gt;

&lt;p&gt;For advanced teams, this opens interesting possibilities. A large monorepo could inject ownership information only for files the agent is currently touching. A backend project could surface schema metadata when database files enter the context. A long-running migration agent could maintain structured state outside the conversation rather than repeatedly asking the model to rediscover it. This is what I consider Pi's real long-term strength: coding-agent quality is increasingly constrained by context engineering, tool design and orchestration rather than raw model intelligence, and Pi gives developers unusually direct access to those layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pi Coding Agent security: no built-in sandbox
&lt;/h2&gt;

&lt;p&gt;The most important negative point in this review is simple: Pi does not provide a built-in security sandbox. By default, Pi runs with the permissions of the user and process that started it. Its tools can read and modify accessible files and execute shell commands within that operating-system context.&lt;/p&gt;

&lt;p&gt;Pi does have project trust, but project trust is not a sandbox. Project trust controls whether project-local Pi settings, extensions, skills, prompts and similar resources are loaded. Once you are working in a project, it does not create a filesystem, network, process or credential boundary around model-directed tool calls. I actually appreciate that the Pi documentation is explicit about this distinction: a weak pseudo-sandbox can be more dangerous than no sandbox because users may trust a boundary that does not really exist. Pi instead expects meaningful isolation to come from containers, virtual machines, micro-VMs or another policy-controlled runtime.&lt;/p&gt;

&lt;p&gt;For disposable personal projects, you may decide that Git and normal operating-system permissions are sufficient. For untrusted repositories, unattended agents, workstations containing production credentials or agents processing externally supplied content, I would use stronger isolation. The Pi documentation describes three patterns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;What is isolated&lt;/th&gt;
&lt;th&gt;Where credentials live&lt;/th&gt;
&lt;th&gt;When to use it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://github.com/earendil-works/gondolin" rel="noopener noreferrer"&gt;Gondolin&lt;/a&gt; extension&lt;/td&gt;
&lt;td&gt;Built-in tools and &lt;code&gt;!&lt;/code&gt; commands inside a local Linux micro-VM; the &lt;code&gt;pi&lt;/code&gt; process stays on the host&lt;/td&gt;
&lt;td&gt;Provider auth can remain on the host&lt;/td&gt;
&lt;td&gt;Local isolation when you want the TUI and keys on the host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plain Docker&lt;/td&gt;
&lt;td&gt;The whole &lt;code&gt;pi&lt;/code&gt; process&lt;/td&gt;
&lt;td&gt;API keys enter the container unless you proxy them&lt;/td&gt;
&lt;td&gt;Simple local isolation of the entire agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.nvidia.com/openshell/about/overview" rel="noopener noreferrer"&gt;NVIDIA OpenShell&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The whole &lt;code&gt;pi&lt;/code&gt; process inside a policy-controlled sandbox&lt;/td&gt;
&lt;td&gt;The gateway can keep raw model keys outside the sandbox&lt;/td&gt;
&lt;td&gt;Local or remote policy over filesystem, network and credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The important boundary is not just the source directory. An agent that can run shell commands may potentially interact with SSH configuration, cloud credentials, package registries, local services, browser state and anything else available to the process. A production-quality setup therefore needs to think about filesystem mounts, environment variables, network access and credentials together. My preferred evaluation pattern is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    H[Host] --&amp;gt; T[Terminal and Git]
    H --&amp;gt; SB[Sandbox]
    SB --&amp;gt; P[Pi]
    SB --&amp;gt; RC[Repository copy]
    SB --&amp;gt; BT[Build tools]
    SB --&amp;gt; MC[Minimum provider credentials]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mounting the real repository read/write into a container still allows the agent to modify that repository; containers help only to the extent that their mounts, credentials, capabilities and network access are actually constrained. Gondolin has a related caveat: custom extension tools still run on the host unless they also delegate into the VM.&lt;/p&gt;

&lt;p&gt;There is another security surface worth remembering: extensions and packages. A third-party extension is code running inside the agent environment. Review it before installation, pin important dependencies and avoid treating a public package gallery as an implicit security review.&lt;/p&gt;

&lt;p&gt;Two practical checks before you run Pi on a machine you care about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Confirm the version you actually have&lt;/span&gt;
pi &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# Disable startup network operations (update checks and install telemetry)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PI_OFFLINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
pi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;PI_OFFLINE=1&lt;/code&gt; disables all startup network operations, including update checks, package update checks and install/update telemetry. If you want a finer split, &lt;code&gt;PI_SKIP_VERSION_CHECK=1&lt;/code&gt; turns off only the version check, and &lt;code&gt;PI_TELEMETRY=0&lt;/code&gt; disables install/update telemetry while leaving update checks enabled.&lt;/p&gt;

&lt;p&gt;This security model is the main reason I would hesitate before giving Pi to a large engineering organisation as an unmanaged default. Pi provides the pieces to build a strong environment, but it intentionally leaves much of that environment to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pi Coding Agent vs Claude Code and OpenCode
&lt;/h2&gt;

&lt;p&gt;Pi makes the most sense when compared by philosophy rather than by feature count. Among terminal agents, &lt;a href="https://www.glukhov.org/ai-devtools/openhands/" rel="noopener noreferrer"&gt;OpenHands&lt;/a&gt; is the closer sibling: it also runs from the CLI, but it ships sandboxing and a larger built-in workflow rather than asking you to assemble one. &lt;a href="https://www.glukhov.org/ai-devtools/claude-code/" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; has increasingly sophisticated built-in permissions, hooks and subagents. &lt;a href="https://www.glukhov.org/ai-devtools/opencode/" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt; exposes configurable allow, ask and deny rules plus built-in agent or subagent concepts. Pi starts lower in the stack.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Pi&lt;/th&gt;
&lt;th&gt;Claude Code&lt;/th&gt;
&lt;th&gt;OpenCode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core approach&lt;/td&gt;
&lt;td&gt;Minimal extensible harness&lt;/td&gt;
&lt;td&gt;Opinionated coding-agent product&lt;/td&gt;
&lt;td&gt;Configurable coding agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default coding tools&lt;/td&gt;
&lt;td&gt;Small primitive set&lt;/td&gt;
&lt;td&gt;Larger integrated tool surface&lt;/td&gt;
&lt;td&gt;Integrated tool surface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permission system&lt;/td&gt;
&lt;td&gt;Not a core security boundary&lt;/td&gt;
&lt;td&gt;Built-in permission modes and rules&lt;/td&gt;
&lt;td&gt;Built-in allow, ask and deny rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Built-in subagents&lt;/td&gt;
&lt;td&gt;No, add them through customisation&lt;/td&gt;
&lt;td&gt;Isolated subagents with tool allowlists&lt;/td&gt;
&lt;td&gt;Built-in primary agents and subagents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom lifecycle behaviour&lt;/td&gt;
&lt;td&gt;TypeScript extensions and events&lt;/td&gt;
&lt;td&gt;Hooks, plugins and &lt;code&gt;settings.json&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Agent and tool configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session branching&lt;/td&gt;
&lt;td&gt;Tree sessions with &lt;code&gt;/fork&lt;/code&gt; and &lt;code&gt;/clone&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Linear sessions plus subagent workflows&lt;/td&gt;
&lt;td&gt;Session continuity plus built-in agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Developers building their own agent workflow&lt;/td&gt;
&lt;td&gt;Developers wanting stronger built-ins&lt;/td&gt;
&lt;td&gt;Developers wanting configurable agents with integrated policy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is why calling Pi a "Claude Code clone" misses the interesting part. If you are happy with Claude Code's workflow, Pi does not automatically improve it: Claude Code gives you more security and agent orchestration behaviour without requiring you to assemble those pieces yourself. Pi becomes interesting when you want to change the assumptions underneath the product. Maybe you want the same harness across Anthropic, OpenAI and local models. Maybe your organisation has its own sandbox. Maybe you need a tool that injects internal architecture metadata before every turn. Maybe you want compaction to produce structured engineering state instead of a generic conversation summary. That is Pi territory.&lt;/p&gt;

&lt;p&gt;OpenCode is arguably closer philosophically because it also offers substantial configuration. Even there, Pi feels more like a kit for constructing the harness itself, while OpenCode exposes more ready-made agent and permission concepts.&lt;/p&gt;

&lt;p&gt;There is no universal winner here. For many developers, Claude Code's opinionated defaults are an advantage. For others, OpenCode may offer a better middle ground. Pi is the option I would investigate when the sentence "I wish my coding agent worked differently" has become more important than "I wish my coding agent had another feature".&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you use Pi Coding Agent?
&lt;/h2&gt;

&lt;p&gt;Pi Coding Agent is one of the more technically interesting coding agents because it refuses to hide the harness. The default implementation is deliberately small, but the extension API, skills, packages, provider layer, SDK, RPC mode and session tree give it a much larger effective surface than its four default tools suggest.&lt;/p&gt;

&lt;p&gt;I would recommend Pi most strongly to experienced developers who like constructing their own tooling. It is particularly compelling for AI platform work, experimental coding-agent research, specialised engineering workflows and teams that already have secure execution infrastructure. It is also useful for developers who want to compare models without changing their entire coding environment every time. I would be more cautious for beginners and for organisations expecting enterprise-style policy controls immediately after installation.&lt;/p&gt;

&lt;p&gt;The missing sandbox changes how Pi should be deployed, especially when an agent is allowed to operate unattended or can encounter untrusted instructions. Treat Pi as a powerful local developer process rather than a security boundary: for untrusted repositories, unattended operation or environments containing valuable credentials, use an operating-system, container, VM or policy-based sandbox and expose only the resources required for the task.&lt;/p&gt;

&lt;p&gt;My overall rating is &lt;strong&gt;8.4/10&lt;/strong&gt;. That is an editorial verdict rather than the average of the table above, and it could easily become higher for the right user. Pi's greatest weakness and greatest strength are the same thing: it does not finish the agent for you.&lt;/p&gt;

&lt;p&gt;Should you switch from Claude Code? Not automatically. Use Claude Code when its built-in permissions, subagents, hooks and workflow fit the way you already work. Evaluate Pi when you want broader ownership of the harness, provider selection, context construction and tool architecture. For advanced developers, I would not even assume the choice has to be exclusive: Claude Code can remain the polished daily driver while Pi becomes the programmable agent laboratory. After enough customisation, that laboratory may turn into the tool you actually prefer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Licensing and cost
&lt;/h3&gt;

&lt;p&gt;Pi itself is open source and released under the MIT licence. That does not make model inference free: your actual cost depends on the model provider, API usage, supported subscription authentication, local compute and any sandbox infrastructure you choose to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Pi Coding Agent repository: &lt;a href="https://github.com/earendil-works/pi" rel="noopener noreferrer"&gt;https://github.com/earendil-works/pi&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pi website and installer: &lt;a href="https://pi.dev" rel="noopener noreferrer"&gt;https://pi.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm package: &lt;a href="https://www.npmjs.com/package/@earendil-works/pi-coding-agent" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@earendil-works/pi-coding-agent&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Gondolin micro-VM: &lt;a href="https://github.com/earendil-works/gondolin" rel="noopener noreferrer"&gt;https://github.com/earendil-works/gondolin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;NVIDIA OpenShell: &lt;a href="https://docs.nvidia.com/openshell/about/overview" rel="noopener noreferrer"&gt;https://docs.nvidia.com/openshell/about/overview&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Licence: MIT&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aicoding</category>
      <category>llm</category>
      <category>ai</category>
      <category>dev</category>
    </item>
    <item>
      <title>GFM vs CommonMark vs Pandoc Markdown: Syntax Compared</title>
      <dc:creator>Rost</dc:creator>
      <pubDate>Sat, 08 Aug 2026 10:05:11 +0000</pubDate>
      <link>https://dev.to/rosgluk/gfm-vs-commonmark-vs-pandoc-markdown-syntax-compared-2ifn</link>
      <guid>https://dev.to/rosgluk/gfm-vs-commonmark-vs-pandoc-markdown-syntax-compared-2ifn</guid>
      <description>&lt;p&gt;Markdown looks like one language until the same file renders differently on GitHub, Hugo, Obsidian, or Pandoc. And the problem is not that Markdown is unreliable.&lt;/p&gt;

&lt;p&gt;It is that "Markdown" describes a family of related syntaxes, parsers, and platform features rather than a single universal document format. CommonMark defines a precise portable core, GitHub Flavored Markdown adds features useful for software collaboration, and Pandoc Markdown expands the language into a serious document-authoring format.&lt;/p&gt;

&lt;p&gt;Choosing between them depends on where the document must render. A README file, a Hugo blog post, and an academic paper each have different requirements. This comparison is part of the broader &lt;a href="https://www.glukhov.org/documentation-tools/" rel="noopener noreferrer"&gt;documentation tooling&lt;/a&gt; picture and covers the formal dialects, platform-specific extensions, and practical portability rules so you can pick the right syntax for your target environment. For a quick syntax reference, the &lt;a href="https://www.glukhov.org/documentation-tools/markdown/markdown-cheatsheet/" rel="noopener noreferrer"&gt;Markdown cheatsheet&lt;/a&gt; covers the essential formatting elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markdown Is Not One Language
&lt;/h2&gt;

&lt;p&gt;The original Markdown syntax was intentionally small and loosely specified. That made it easy to read and implement, but different parsers began interpreting ambiguous input differently.&lt;/p&gt;

&lt;p&gt;CommonMark was created to define consistent parsing rules for the fundamental Markdown structures. GitHub Flavored Markdown, usually called GFM, builds on that foundation with several widely used extensions.&lt;/p&gt;

&lt;p&gt;Pandoc Markdown takes a different approach. Instead of remaining a small web-oriented syntax, it adds document features such as citations, metadata, footnotes, definition lists, attributes, and mathematical notation.&lt;/p&gt;

&lt;p&gt;A simplified relationship looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    M[Markdown family] --&amp;gt; C[CommonMark core]
    C --&amp;gt; G[GitHub Flavored Markdown]
    C --&amp;gt; X[Other CommonMark-based renderers]
    M --&amp;gt; P[Pandoc Markdown]
    G --&amp;gt; GH[GitHub platform features]
    X --&amp;gt; H[Hugo with Goldmark]
    X --&amp;gt; GL[GitLab Flavored Markdown]
    P --&amp;gt; PDF[PDF and academic workflows]
    P --&amp;gt; DOCX[DOCX and publishing workflows]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This hierarchy is useful, but it is not exact inheritance in every implementation. Each renderer can enable, disable, or add syntax independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Short Answer
&lt;/h2&gt;

&lt;p&gt;Use CommonMark-compatible syntax when portability matters most.&lt;/p&gt;

&lt;p&gt;Use GFM when writing README files, pull requests, issue templates, and technical documentation intended primarily for GitHub-compatible platforms.&lt;/p&gt;

&lt;p&gt;Use Pandoc Markdown when the source document must become PDF, DOCX, EPUB, LaTeX, slides, or an academic paper with citations and metadata.&lt;/p&gt;

&lt;p&gt;For a Hugo technical blog, use the CommonMark core plus the Goldmark extensions that your site explicitly enables. Do not assume every feature visible on GitHub will work merely because Hugo is described as GFM-compatible.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Opinionated take:&lt;/strong&gt; if you only remember one rule for a Hugo technical blog, treat CommonMark plus GFM-style tables and task lists as the default, and treat everything else — footnotes, math, callouts, header attributes — as an explicit, tested extension rather than an assumed default. That single habit prevents most of the portability failures described below.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  CommonMark: The Portable Core
&lt;/h2&gt;

&lt;p&gt;CommonMark is a formal specification for the basic Markdown language. Its main contribution is not a large collection of features, but consistent parsing.&lt;/p&gt;

&lt;p&gt;It defines how parsers should interpret:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Paragraphs&lt;/li&gt;
&lt;li&gt;ATX and Setext headings&lt;/li&gt;
&lt;li&gt;Block quotes&lt;/li&gt;
&lt;li&gt;Ordered and unordered lists&lt;/li&gt;
&lt;li&gt;Fenced and indented code blocks&lt;/li&gt;
&lt;li&gt;Emphasis and strong emphasis&lt;/li&gt;
&lt;li&gt;Links and images&lt;/li&gt;
&lt;li&gt;Reference-style links&lt;/li&gt;
&lt;li&gt;Inline code&lt;/li&gt;
&lt;li&gt;Thematic breaks&lt;/li&gt;
&lt;li&gt;Raw HTML blocks&lt;/li&gt;
&lt;li&gt;Hard and soft line breaks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A CommonMark document can still behave differently at the presentation layer. CSS, syntax highlighting, heading anchors, HTML sanitization, and link policies are outside the core parsing rules.&lt;/p&gt;

&lt;p&gt;CommonMark should therefore be treated as a reliable structural baseline, not a promise that every renderer will produce an identical page.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Portable CommonMark Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Service Deployment&lt;/span&gt;

The service exposes a small HTTP API.

&lt;span class="gu"&gt;## Requirements&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Linux
&lt;span class="p"&gt;-&lt;/span&gt; Docker
&lt;span class="p"&gt;-&lt;/span&gt; 8 GB of memory

&lt;span class="gu"&gt;## Start the service&lt;/span&gt;

&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;bash
&lt;/span&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;

See the &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;configuration guide&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;configuration.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; for details.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This type of document works across almost every modern Markdown environment. It uses headings, paragraphs, lists, fenced code, and ordinary links without relying on dialect-specific extensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Flavored Markdown: CommonMark for Software Projects
&lt;/h2&gt;

&lt;p&gt;GitHub Flavored Markdown is a formal dialect based on CommonMark. It preserves the CommonMark parsing model and adds features commonly needed in repository documentation and collaboration.&lt;/p&gt;

&lt;p&gt;The formal GFM specification adds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pipe tables&lt;/li&gt;
&lt;li&gt;Task list items&lt;/li&gt;
&lt;li&gt;Strikethrough&lt;/li&gt;
&lt;li&gt;Extended autolinks&lt;/li&gt;
&lt;li&gt;Restrictions around some raw HTML tags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These extensions are now so common that many users think they are part of standard Markdown. They are not part of the CommonMark core.&lt;/p&gt;

&lt;h3&gt;
  
  
  GFM Tables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Backend | Best use |
|---|---|
| Ollama | Local experiments |
| vLLM | Shared inference |
| SGLang | Structured workloads |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A strict CommonMark parser is allowed to treat this as ordinary paragraph text. A GFM-compatible parser recognizes it as a table. For a deeper look at table syntax and alignment options, see &lt;a href="https://www.glukhov.org/documentation-tools/markdown/tables-in-markdown/" rel="noopener noreferrer"&gt;Tables in Markdown&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  GFM Task Lists
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; [x] Install Docker
&lt;span class="p"&gt;-&lt;/span&gt; [x] Download the model
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Add monitoring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Task list syntax is useful in issues, pull requests, and project documentation. Outside a supporting renderer, it may appear as an ordinary list containing literal square brackets.&lt;/p&gt;

&lt;h3&gt;
  
  
  GFM Strikethrough
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Use the ~~old endpoint~~ new endpoint.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Strikethrough is widely supported, but it is still an extension rather than portable CommonMark syntax.&lt;/p&gt;

&lt;h3&gt;
  
  
  GFM Autolinks
&lt;/h3&gt;

&lt;p&gt;GFM recognizes more URL-like and email-like text without requiring angle brackets or explicit link syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Visit https://example.com/docs for details.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In strict CommonMark, explicit autolinks use angle brackets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nv"&gt;&amp;lt;https://example.com/docs&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The explicit form is safer when a document must travel through unknown Markdown processors.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub.com Supports More Than Formal GFM
&lt;/h2&gt;

&lt;p&gt;A frequent source of confusion is the assumption that every Markdown feature visible on GitHub belongs to the GFM specification.&lt;/p&gt;

&lt;p&gt;It does not.&lt;/p&gt;

&lt;p&gt;GitHub.com adds platform-level processing and features around the GFM parser. Depending on the context, GitHub can support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mathematical expressions&lt;/li&gt;
&lt;li&gt;Mermaid diagrams&lt;/li&gt;
&lt;li&gt;Alerts&lt;/li&gt;
&lt;li&gt;Issue and pull request references&lt;/li&gt;
&lt;li&gt;User and team mentions&lt;/li&gt;
&lt;li&gt;Commit references&lt;/li&gt;
&lt;li&gt;Emoji shortcodes&lt;/li&gt;
&lt;li&gt;Collapsible HTML sections&lt;/li&gt;
&lt;li&gt;Color previews&lt;/li&gt;
&lt;li&gt;Repository-relative links&lt;/li&gt;
&lt;li&gt;Automatic heading anchors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of these features are syntax extensions. Others are post-processing behavior or integrations with GitHub data.&lt;/p&gt;

&lt;p&gt;This distinction matters because another renderer may accurately claim GFM compatibility without implementing GitHub's math renderer, Mermaid integration, issue references, or alert styling.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Mermaid Diagrams
&lt;/h3&gt;

&lt;p&gt;GitHub renders a fenced code block marked &lt;code&gt;mermaid&lt;/code&gt; as a diagram:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;mermaid
&lt;/span&gt;&lt;span class="sb"&gt;flowchart LR
    A[Markdown] --&amp;gt; B[Rendered diagram]&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A generic GFM renderer may display the same block as highlighted source code. The Markdown remains valid, but the enhanced rendering is platform-specific. For a practical introduction to Mermaid syntax, see the &lt;a href="https://www.glukhov.org/documentation-tools/diagrams/mermaid-diagrams-quickstart-cheatsheet/" rel="noopener noreferrer"&gt;Mermaid Diagrams Quickstart&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Mathematical Expressions
&lt;/h3&gt;

&lt;p&gt;GitHub supports inline and block mathematical expressions using dollar delimiters and additional escaping forms.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;The cache size is approximately $2nlhd$ bytes.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;$$
C = 2nlhd
$$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Math is not part of formal GFM. Moving this content to another renderer requires a compatible math extension such as KaTeX, MathJax, or Pandoc math support.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Alerts
&lt;/h3&gt;

&lt;p&gt;GitHub supports alert-style block quotes such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gt"&gt;&amp;gt; [!WARNING]&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; Changing this setting clears the cache.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On GitHub, this can appear as a styled warning. On a plain CommonMark renderer, it usually appears as an ordinary block quote containing &lt;code&gt;[!WARNING]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That fallback is readable, which makes GitHub alerts less dangerous than extensions that disappear completely. They are still not portable presentation elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pandoc Markdown: Markdown as a Document Language
&lt;/h2&gt;

&lt;p&gt;Pandoc Markdown is designed for document conversion rather than one particular website. It uses Markdown as the source syntax for producing HTML, PDF, DOCX, EPUB, LaTeX, presentations, and other formats.&lt;/p&gt;

&lt;p&gt;Its default Markdown reader includes a large extension set. Important capabilities include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;YAML metadata blocks&lt;/li&gt;
&lt;li&gt;Footnotes&lt;/li&gt;
&lt;li&gt;Citations&lt;/li&gt;
&lt;li&gt;Multiple table formats&lt;/li&gt;
&lt;li&gt;Definition lists&lt;/li&gt;
&lt;li&gt;Mathematical notation&lt;/li&gt;
&lt;li&gt;Header identifiers and attributes&lt;/li&gt;
&lt;li&gt;Code block attributes&lt;/li&gt;
&lt;li&gt;Fenced divisions&lt;/li&gt;
&lt;li&gt;Bracketed spans&lt;/li&gt;
&lt;li&gt;Superscript and subscript&lt;/li&gt;
&lt;li&gt;Strikeout&lt;/li&gt;
&lt;li&gt;Line blocks&lt;/li&gt;
&lt;li&gt;Numbered example lists&lt;/li&gt;
&lt;li&gt;Raw LaTeX&lt;/li&gt;
&lt;li&gt;Raw HTML&lt;/li&gt;
&lt;li&gt;Automatic section numbering&lt;/li&gt;
&lt;li&gt;Bibliography processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pandoc Markdown is much more expressive than CommonMark or formal GFM. That expressiveness makes it powerful for publishing, but less safe as an interchange format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pandoc Footnotes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Markdown has several incompatible dialects.[^dialects]

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;^dialects&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="sx"&gt;CommonMark,&lt;/span&gt; GFM, and Pandoc Markdown are three
    important examples.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Footnote syntax is supported by many modern tools, but it is not part of CommonMark or formal GFM.&lt;/p&gt;

&lt;p&gt;GitHub currently renders footnotes in several content contexts, but that is a GitHub platform feature rather than a formal GFM guarantee. A renderer claiming only CommonMark or GFM compatibility may not support them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pandoc Citations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;PagedAttention improves KV cache memory management
[@kwon2023pagedattention].
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a bibliography file and citation style, Pandoc can resolve this into a formatted academic citation and bibliography.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pandoc article.md &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--citeproc&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bibliography&lt;/span&gt; references.bib &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--csl&lt;/span&gt; ieee.csl &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; article.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The citation syntax remains readable in an unsupported renderer, but it will not become a formatted reference without Pandoc or another compatible citation processor. Pandoc's reader-side flexibility also underpins conversion workflows in the other direction — see &lt;a href="https://www.glukhov.org/documentation-tools/markdown/converting-word-document-to-markdown/" rel="noopener noreferrer"&gt;converting Word documents to Markdown&lt;/a&gt; for a practical example of using Pandoc's extended dialect as an intermediate format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pandoc Definition Lists
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;CommonMark
: A precise specification for core Markdown.

GFM
: A CommonMark-based dialect with software-oriented extensions.

Pandoc Markdown
: An extended authoring format for document conversion.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Definition lists are useful in manuals, glossaries, and technical books. They normally degrade poorly in renderers that do not support them because the colon lines remain visible as plain text.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pandoc Header Attributes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Cache Configuration {#cache-config .deployment}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pandoc interprets the braces as an explicit identifier and class list. Many other Markdown renderers show the attribute text directly in the heading.&lt;/p&gt;

&lt;p&gt;This is one of the clearest examples of useful syntax that should not be placed in a document expected to render everywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pandoc Fenced Divisions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;::: warning
Changing this option restarts the server.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pandoc converts this into a structural division with a class. Templates, CSS, filters, or output writers can decide how that structure should appear.&lt;/p&gt;

&lt;p&gt;Most CommonMark and GFM renderers do not recognize the fence. They display the colons and content as ordinary text.&lt;/p&gt;

&lt;h2&gt;
  
  
  CommonMark vs GFM vs Pandoc Markdown
&lt;/h2&gt;

&lt;p&gt;The following matrix describes the formal dialects, not every feature added by GitHub.com, Hugo, Obsidian, GitLab, or another platform.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;CommonMark&lt;/th&gt;
&lt;th&gt;Formal GFM&lt;/th&gt;
&lt;th&gt;Pandoc Markdown&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Headings&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Emphasis&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Links and images&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block quotes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ordered and unordered lists&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fenced code blocks&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw HTML syntax&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Restricted in some contexts&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipe tables&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task lists&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strikethrough&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extended autolinks&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Footnotes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Citations&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YAML metadata&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Definition lists&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mathematical notation&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Header attributes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fenced divisions&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw LaTeX&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bibliography processing&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The word "No" does not mean a platform can never support the feature. It means the feature is not guaranteed by that dialect's formal specification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Syntax Works on GitHub?
&lt;/h2&gt;

&lt;p&gt;For README files, issues, pull requests, discussions, and wikis, GFM is the natural baseline.&lt;/p&gt;

&lt;p&gt;You can generally use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CommonMark syntax&lt;/li&gt;
&lt;li&gt;Tables&lt;/li&gt;
&lt;li&gt;Task lists&lt;/li&gt;
&lt;li&gt;Strikethrough&lt;/li&gt;
&lt;li&gt;Extended autolinks&lt;/li&gt;
&lt;li&gt;Syntax-highlighted code fences&lt;/li&gt;
&lt;li&gt;GitHub-specific references&lt;/li&gt;
&lt;li&gt;GitHub-supported math&lt;/li&gt;
&lt;li&gt;GitHub-supported diagrams&lt;/li&gt;
&lt;li&gt;GitHub alerts&lt;/li&gt;
&lt;li&gt;Footnotes where supported by the content surface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The portability risk begins when GitHub performs additional rendering beyond formal GFM. Mermaid diagrams, mathematical notation, issue references, and alert presentation may not survive outside GitHub.&lt;/p&gt;

&lt;p&gt;For repository files that are also published elsewhere, test the source in the second renderer rather than treating GitHub preview as authoritative.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Syntax Works in Hugo?
&lt;/h2&gt;

&lt;p&gt;Hugo uses Goldmark as its default Markdown renderer. Goldmark conforms to CommonMark and provides extensions compatible with important parts of GFM.&lt;/p&gt;

&lt;p&gt;In a typical Hugo configuration, the following work well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CommonMark structure&lt;/li&gt;
&lt;li&gt;Fenced code blocks&lt;/li&gt;
&lt;li&gt;Pipe tables&lt;/li&gt;
&lt;li&gt;Strikethrough&lt;/li&gt;
&lt;li&gt;Task lists&lt;/li&gt;
&lt;li&gt;Automatic heading IDs&lt;/li&gt;
&lt;li&gt;Syntax highlighting&lt;/li&gt;
&lt;li&gt;Footnotes when the extension is enabled&lt;/li&gt;
&lt;li&gt;Definition lists when enabled&lt;/li&gt;
&lt;li&gt;Typographic substitutions when enabled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hugo also adds features outside Markdown through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Front matter&lt;/li&gt;
&lt;li&gt;Shortcodes&lt;/li&gt;
&lt;li&gt;Render hooks&lt;/li&gt;
&lt;li&gt;Page resources&lt;/li&gt;
&lt;li&gt;Internal reference functions&lt;/li&gt;
&lt;li&gt;Template processing&lt;/li&gt;
&lt;li&gt;Site configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These Hugo features do not travel with the Markdown file. For a practical example of Hugo deployment, see &lt;a href="https://www.glukhov.org/web-infrastructure/hugo/deploy-hugo-s3/" rel="noopener noreferrer"&gt;Deploy Hugo to AWS S3&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hugo Front Matter Is Not Markdown Content
&lt;/h3&gt;

&lt;p&gt;A Hugo page commonly starts with YAML, TOML, or JSON metadata:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Markdown&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Compatibility"&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Compare&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Markdown&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;dialects&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;renderers."&lt;/span&gt;
&lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2026-07-31&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Markdown&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;documentation&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pandoc can also recognize YAML metadata blocks, but it interprets fields according to its own templates and writers. GitHub normally displays the block as a YAML-like section or treats it as repository metadata only in specific systems.&lt;/p&gt;

&lt;p&gt;The same syntax can therefore be recognized in more than one tool without having the same semantics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Raw HTML in Hugo
&lt;/h3&gt;

&lt;p&gt;Goldmark does not render potentially unsafe raw HTML by default in a standard Hugo configuration.&lt;/p&gt;

&lt;p&gt;A block such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"notice"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Restart the service after changing this value.
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;may be omitted unless raw HTML rendering is enabled or the content is implemented through a shortcode or render hook.&lt;/p&gt;

&lt;p&gt;For a controlled technical blog, enabling raw HTML can be reasonable. It still makes the source less portable and should be a deliberate site-level decision.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mermaid in Hugo
&lt;/h3&gt;

&lt;p&gt;A fenced &lt;code&gt;mermaid&lt;/code&gt; block is still just a code block unless the Hugo theme, render hook, shortcode, or JavaScript pipeline transforms it into a diagram.&lt;/p&gt;

&lt;p&gt;GitHub and Hugo may therefore accept identical Mermaid source while using completely different rendering mechanisms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Syntax Works in Pandoc?
&lt;/h2&gt;

&lt;p&gt;Pandoc can read several Markdown dialects explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pandoc &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;markdown input.md
pandoc &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;commonmark input.md
pandoc &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gfm input.md
pandoc &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;commonmark_x input.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of Pandoc's most useful portability features. The operator can tell Pandoc which dialect the source claims to use instead of relying on a vague &lt;code&gt;.md&lt;/code&gt; file extension.&lt;/p&gt;

&lt;p&gt;Pandoc also lets you enable or disable individual extensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pandoc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;markdown-footnotes-pipe_tables &lt;span class="se"&gt;\&lt;/span&gt;
  input.md &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; output.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or start from a narrower format and add one feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pandoc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;commonmark+footnotes &lt;span class="se"&gt;\&lt;/span&gt;
  input.md &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; output.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can inspect available extensions with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pandoc &lt;span class="nt"&gt;--list-extensions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;markdown
pandoc &lt;span class="nt"&gt;--list-extensions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;commonmark
pandoc &lt;span class="nt"&gt;--list-extensions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gfm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This extension model is powerful, but it means "Pandoc Markdown" is not always one fixed configuration. Build commands and defaults files are part of the document specification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Syntax Works in Obsidian?
&lt;/h2&gt;

&lt;p&gt;Obsidian stores notes as Markdown files, but its authoring model includes several application-specific features.&lt;/p&gt;

&lt;p&gt;Common examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wiki links&lt;/li&gt;
&lt;li&gt;Embedded notes&lt;/li&gt;
&lt;li&gt;Embedded files&lt;/li&gt;
&lt;li&gt;Callouts&lt;/li&gt;
&lt;li&gt;Block references&lt;/li&gt;
&lt;li&gt;Tags&lt;/li&gt;
&lt;li&gt;Properties&lt;/li&gt;
&lt;li&gt;Highlighting&lt;/li&gt;
&lt;li&gt;Comments&lt;/li&gt;
&lt;li&gt;Dataview queries from plugins&lt;/li&gt;
&lt;li&gt;Application-specific URI links&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A wiki link such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;[[Markdown Compatibility]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is meaningful inside an Obsidian vault. GitHub, CommonMark, and a default Pandoc reader normally display it as literal bracketed text.&lt;/p&gt;

&lt;p&gt;An embed is even more application-specific:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;![[compatibility-table]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The referenced content is not present in the file itself. Exporting or publishing the note therefore requires an expansion step that resolves the embed.&lt;/p&gt;

&lt;p&gt;Obsidian is a good example of why storage in &lt;code&gt;.md&lt;/code&gt; files does not guarantee Markdown portability. For a practical look at Obsidian as a knowledge management tool, see &lt;a href="https://www.glukhov.org/knowledge-management/tools/obsidian-for-personal-knowledge-management/" rel="noopener noreferrer"&gt;Obsidian for Personal Knowledge Management&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Syntax Works in GitLab?
&lt;/h2&gt;

&lt;p&gt;GitLab Flavored Markdown uses CommonMark as its core and includes GFM features such as tables and task lists. It then adds GitLab-specific behavior including cross-references, mathematical notation, diagrams, and other collaboration features.&lt;/p&gt;

&lt;p&gt;A README written in conservative GFM usually moves between GitHub and GitLab without major damage.&lt;/p&gt;

&lt;p&gt;Platform integrations do not travel as reliably. Issue references, user mentions, diagrams, math handling, and special block syntax can behave differently even when the basic Markdown remains readable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Platform Support Matrix
&lt;/h2&gt;

&lt;p&gt;This matrix describes common default behavior. Themes, plugins, extensions, and configuration can change individual cells.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;GitHub&lt;/th&gt;
&lt;th&gt;Hugo Goldmark&lt;/th&gt;
&lt;th&gt;Pandoc&lt;/th&gt;
&lt;th&gt;Obsidian&lt;/th&gt;
&lt;th&gt;GitLab&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CommonMark core&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Mostly&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipe tables&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task lists&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strikethrough&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Footnotes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YAML metadata&lt;/td&gt;
&lt;td&gt;Context-dependent&lt;/td&gt;
&lt;td&gt;Front matter&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Properties&lt;/td&gt;
&lt;td&gt;Context-dependent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Math&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Requires setup&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mermaid&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Requires setup&lt;/td&gt;
&lt;td&gt;Output-dependent&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Citations&lt;/td&gt;
&lt;td&gt;No native bibliography&lt;/td&gt;
&lt;td&gt;Requires tooling&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Plugin-dependent&lt;/td&gt;
&lt;td&gt;No native bibliography&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Definition lists&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Header attributes&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Renderer-dependent&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wiki links&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No by default&lt;/td&gt;
&lt;td&gt;No by default&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Wiki-dependent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Callouts or alerts&lt;/td&gt;
&lt;td&gt;GitHub syntax&lt;/td&gt;
&lt;td&gt;Theme or shortcode&lt;/td&gt;
&lt;td&gt;Template-dependent&lt;/td&gt;
&lt;td&gt;Obsidian syntax&lt;/td&gt;
&lt;td&gt;GitLab syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw HTML&lt;/td&gt;
&lt;td&gt;Sanitized or restricted&lt;/td&gt;
&lt;td&gt;Disabled by default&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Context-dependent&lt;/td&gt;
&lt;td&gt;Sanitized or restricted&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"Yes" still does not guarantee identical HTML or visual presentation. It means the environment recognizes the general feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Syntax That Is Usually Safe Everywhere
&lt;/h2&gt;

&lt;p&gt;The safest portable subset includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ATX headings using &lt;code&gt;#&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ordinary paragraphs&lt;/li&gt;
&lt;li&gt;Blank lines between blocks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-&lt;/code&gt; for unordered lists&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1.&lt;/code&gt; for ordered lists&lt;/li&gt;
&lt;li&gt;Fenced code blocks using backticks&lt;/li&gt;
&lt;li&gt;Inline code using backticks&lt;/li&gt;
&lt;li&gt;Emphasis using &lt;code&gt;*text*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Strong emphasis using &lt;code&gt;**text**&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ordinary links&lt;/li&gt;
&lt;li&gt;Ordinary images&lt;/li&gt;
&lt;li&gt;Block quotes&lt;/li&gt;
&lt;li&gt;Thematic breaks&lt;/li&gt;
&lt;li&gt;Explicit angle-bracket autolinks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An intentionally conservative document might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Deployment Guide&lt;/span&gt;

This guide explains how to deploy the service.

&lt;span class="gu"&gt;## Requirements&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Docker
&lt;span class="p"&gt;-&lt;/span&gt; Linux
&lt;span class="p"&gt;-&lt;/span&gt; A supported GPU

&lt;span class="gu"&gt;## Configuration&lt;/span&gt;

Create a file named &lt;span class="sb"&gt;`compose.yaml`&lt;/span&gt;.

&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;yaml
&lt;/span&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;application&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example/application:1.0&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;

For more information, see the &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;configuration reference&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;config.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;.
&lt;span class="gt"&gt;
&amp;gt; Back up existing data before upgrading.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This syntax travels well because it does not depend on tables, footnotes, attributes, callouts, or platform processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Syntax That Commonly Breaks
&lt;/h2&gt;

&lt;p&gt;Portability problems tend to cluster around a small number of features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pipe Tables
&lt;/h3&gt;

&lt;p&gt;Pipe tables are well supported by GFM-oriented tools, but not by strict CommonMark.&lt;/p&gt;

&lt;p&gt;A table can degrade into unreadable text when passed through a parser that does not recognize it. For highly portable documents, consider short lists or semantic HTML generated during a build step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Footnotes
&lt;/h3&gt;

&lt;p&gt;Footnote syntax has become common, but it remains an extension.&lt;/p&gt;

&lt;p&gt;Different tools may:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support only one footnote format&lt;/li&gt;
&lt;li&gt;Place footnotes differently&lt;/li&gt;
&lt;li&gt;Generate different identifiers&lt;/li&gt;
&lt;li&gt;Reject multi-paragraph footnotes&lt;/li&gt;
&lt;li&gt;Render the source literally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use footnotes when the publishing pipeline is known. Avoid depending on them in README files that must render across arbitrary systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Heading IDs and Attributes
&lt;/h3&gt;

&lt;p&gt;This Pandoc syntax is not portable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Installation {#installation .procedure}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use an ordinary heading and let the renderer generate its own anchor when portability matters.&lt;/p&gt;

&lt;p&gt;Also avoid hard-coding links to auto-generated heading IDs unless every target uses the same slugification rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Callouts and Alerts
&lt;/h3&gt;

&lt;p&gt;GitHub, Obsidian, GitLab, MkDocs, Docusaurus, and Hugo themes can all support callout-like blocks, but they often use different syntax.&lt;/p&gt;

&lt;p&gt;A portable fallback is an ordinary block quote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gt"&gt;&amp;gt; Warning: Back up the database before upgrading.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is less visually impressive, but it preserves meaning everywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wiki Links
&lt;/h3&gt;

&lt;p&gt;Wiki links are concise inside knowledge-management tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;[[KV Cache]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They are poor interchange syntax because the target path, file name, heading rules, and resolution behavior belong to the application.&lt;/p&gt;

&lt;p&gt;Use standard Markdown links in content intended for publication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;KV cache&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;kv-cache.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Raw HTML
&lt;/h3&gt;

&lt;p&gt;Raw HTML is the usual escape hatch when Markdown cannot express a layout. It is also a common portability and security failure.&lt;/p&gt;

&lt;p&gt;A renderer may:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove the HTML&lt;/li&gt;
&lt;li&gt;Escape it&lt;/li&gt;
&lt;li&gt;Sanitize selected elements&lt;/li&gt;
&lt;li&gt;Allow blocks but not inline elements&lt;/li&gt;
&lt;li&gt;Refuse Markdown parsing inside HTML&lt;/li&gt;
&lt;li&gt;Pass it unchanged only in trusted mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use raw HTML only when the publishing target is controlled.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mathematical Notation
&lt;/h3&gt;

&lt;p&gt;Dollar-delimited math is popular but not universally interpreted.&lt;/p&gt;

&lt;p&gt;The source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;The complexity is $O(n^2)$.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;may become:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rendered mathematics&lt;/li&gt;
&lt;li&gt;Ordinary text with dollar signs&lt;/li&gt;
&lt;li&gt;Incorrect emphasis&lt;/li&gt;
&lt;li&gt;Input to a different math parser&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choose one math pipeline and test it in every target environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mermaid and Other Diagram Blocks
&lt;/h3&gt;

&lt;p&gt;A Mermaid code fence is syntactically safe because unsupported renderers normally display it as code.&lt;/p&gt;

&lt;p&gt;The semantic result is still different. Readers may see a rendered architecture diagram on GitHub and raw Mermaid source in another environment.&lt;/p&gt;

&lt;p&gt;This is graceful degradation, not true compatibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Layers of Markdown Compatibility
&lt;/h2&gt;

&lt;p&gt;It helps to separate compatibility into three layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 1: Parsing Compatibility
&lt;/h3&gt;

&lt;p&gt;Does the parser recognize the structure?&lt;/p&gt;

&lt;p&gt;Examples include headings, tables, footnotes, and fenced divisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 2: Transformation Compatibility
&lt;/h3&gt;

&lt;p&gt;Does the platform apply additional processing?&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rendering Mermaid&lt;/li&gt;
&lt;li&gt;Resolving citations&lt;/li&gt;
&lt;li&gt;Expanding wiki links&lt;/li&gt;
&lt;li&gt;Linking issue numbers&lt;/li&gt;
&lt;li&gt;Processing shortcodes&lt;/li&gt;
&lt;li&gt;Generating a table of contents&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Layer 3: Presentation Compatibility
&lt;/h3&gt;

&lt;p&gt;Does the result look and behave appropriately?&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Table styling&lt;/li&gt;
&lt;li&gt;Syntax highlighting&lt;/li&gt;
&lt;li&gt;Alert colors&lt;/li&gt;
&lt;li&gt;Heading anchors&lt;/li&gt;
&lt;li&gt;Responsive images&lt;/li&gt;
&lt;li&gt;Footnote placement&lt;/li&gt;
&lt;li&gt;Math fonts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two platforms can parse identical syntax while producing substantially different presentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Better Portability Model
&lt;/h2&gt;

&lt;p&gt;Instead of asking whether a file is "valid Markdown," ask four narrower questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which dialect is the source written in?&lt;/li&gt;
&lt;li&gt;Which parser reads it?&lt;/li&gt;
&lt;li&gt;Which extensions are enabled?&lt;/li&gt;
&lt;li&gt;Which platform transformations run afterward?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dialect: CommonMark plus GFM tables
Parser: Goldmark
Extensions: tables, strikethrough, task lists, footnotes
Platform: Hugo
Additional processing: render hooks and Mermaid JavaScript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That description is much more useful than saying "the site uses Markdown."&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a Dialect by Use Case
&lt;/h2&gt;

&lt;h3&gt;
  
  
  README Files
&lt;/h3&gt;

&lt;p&gt;Use GFM.&lt;/p&gt;

&lt;p&gt;README files benefit from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tables&lt;/li&gt;
&lt;li&gt;Task lists&lt;/li&gt;
&lt;li&gt;Fenced code&lt;/li&gt;
&lt;li&gt;Autolinks&lt;/li&gt;
&lt;li&gt;Strikethrough&lt;/li&gt;
&lt;li&gt;GitHub references&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid excessive dependence on GitHub-only features when the repository is mirrored to GitLab, rendered on a package registry, or included in generated documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hugo Technical Articles
&lt;/h3&gt;

&lt;p&gt;Use CommonMark-compatible Markdown with a documented Goldmark extension set.&lt;/p&gt;

&lt;p&gt;Tables, code fences, footnotes, and Mermaid can be reasonable because you control the build pipeline. Prefer Hugo shortcodes or render hooks over embedding large amounts of raw HTML.&lt;/p&gt;

&lt;p&gt;Keep Hugo-specific syntax isolated and easy to find.&lt;/p&gt;

&lt;h3&gt;
  
  
  Academic Documents
&lt;/h3&gt;

&lt;p&gt;Use Pandoc Markdown.&lt;/p&gt;

&lt;p&gt;Citations, bibliography processing, footnotes, metadata, mathematical notation, cross-references, and conversion to PDF or DOCX justify the reduced portability.&lt;/p&gt;

&lt;p&gt;Store the Pandoc command, defaults file, filters, bibliography, and templates beside the source. The source file alone does not fully describe the build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Books and Long-Form Documentation
&lt;/h3&gt;

&lt;p&gt;Pandoc Markdown is usually the strongest of the three options when multiple output formats matter.&lt;/p&gt;

&lt;p&gt;Definition lists, citations, attributes, metadata, and structured transformations become more important as document complexity grows.&lt;/p&gt;

&lt;p&gt;For web-only documentation hosted in a Git repository, GFM or a CommonMark-based documentation generator may remain simpler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Notes and Personal Knowledge Bases
&lt;/h3&gt;

&lt;p&gt;Use the native syntax of the selected notes application when application features provide real value.&lt;/p&gt;

&lt;p&gt;Obsidian wiki links, embeds, and callouts are useful inside a vault. Treat export as a compilation process rather than assuming the raw files are already portable publications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shared Documentation Across Unknown Systems
&lt;/h3&gt;

&lt;p&gt;Use a conservative CommonMark subset.&lt;/p&gt;

&lt;p&gt;Avoid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wiki links&lt;/li&gt;
&lt;li&gt;Platform alerts&lt;/li&gt;
&lt;li&gt;Header attributes&lt;/li&gt;
&lt;li&gt;Citations&lt;/li&gt;
&lt;li&gt;Raw HTML&lt;/li&gt;
&lt;li&gt;Custom containers&lt;/li&gt;
&lt;li&gt;Application embeds&lt;/li&gt;
&lt;li&gt;Shortcodes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Portability usually requires giving up convenience features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Rules for Portable Markdown
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Start with CommonMark Structure
&lt;/h3&gt;

&lt;p&gt;Use CommonMark for the document skeleton:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Headings&lt;/li&gt;
&lt;li&gt;Paragraphs&lt;/li&gt;
&lt;li&gt;Lists&lt;/li&gt;
&lt;li&gt;Links&lt;/li&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;Block quotes&lt;/li&gt;
&lt;li&gt;Code blocks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures that the main meaning survives even when optional extensions fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add GFM Features Deliberately
&lt;/h3&gt;

&lt;p&gt;Tables and task lists are reasonable when all important targets support them.&lt;/p&gt;

&lt;p&gt;Do not assume "most tools support GFM" without testing the exact target. Some claim GFM compatibility while enabling only selected extensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Isolate Platform Extensions
&lt;/h3&gt;

&lt;p&gt;Keep platform-specific syntax in clearly identifiable blocks.&lt;/p&gt;

&lt;p&gt;For example, centralize Hugo shortcodes, Pandoc citations, or Obsidian embeds rather than scattering them through every paragraph.&lt;/p&gt;

&lt;p&gt;Isolation makes later conversion easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prefer Graceful Degradation
&lt;/h3&gt;

&lt;p&gt;A Mermaid block degrades into readable source code. A GitHub alert degrades into a block quote.&lt;/p&gt;

&lt;p&gt;A wiki embed may degrade into an unexplained file name, while a Pandoc fenced division may expose punctuation around the content.&lt;/p&gt;

&lt;p&gt;Choose extensions whose fallback remains understandable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do Not Depend on Auto-Generated Heading IDs
&lt;/h3&gt;

&lt;p&gt;Heading anchor algorithms differ between GitHub, Hugo, Pandoc, and documentation generators.&lt;/p&gt;

&lt;p&gt;For cross-document links, use renderer-supported explicit IDs only when the target pipeline is controlled. Otherwise, link to the document rather than a generated fragment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep Build Configuration with the Content
&lt;/h3&gt;

&lt;p&gt;Pandoc extensions, Hugo settings, plugins, filters, and JavaScript integrations determine how Markdown behaves.&lt;/p&gt;

&lt;p&gt;Commit relevant configuration files with the source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;content/
  article.md
pandoc.yaml
references.bib
config/
  _default/
    markup.yaml
layouts/
  _default/
    _markup/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;.md&lt;/code&gt; extension alone does not capture the publishing environment. For a structured approach to documenting these decisions, see &lt;a href="https://www.glukhov.org/app-architecture/documentation/decision-records-ai-driven-development/" rel="noopener noreferrer"&gt;Decision Records for AI-Driven Development&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Markdown Against Every Important Target
&lt;/h2&gt;

&lt;p&gt;Visual preview in one editor is not enough. The editor may support a richer dialect than the production renderer.&lt;/p&gt;

&lt;p&gt;For Pandoc, test explicit input formats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pandoc &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;commonmark article.md &lt;span class="nt"&gt;-o&lt;/span&gt; commonmark.html
pandoc &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gfm article.md &lt;span class="nt"&gt;-o&lt;/span&gt; gfm.html
pandoc &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;markdown article.md &lt;span class="nt"&gt;-o&lt;/span&gt; pandoc.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Warnings and visible source punctuation reveal which features are dialect-specific.&lt;/p&gt;

&lt;p&gt;For Hugo, build the production site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hugo &lt;span class="nt"&gt;--gc&lt;/span&gt; &lt;span class="nt"&gt;--minify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then inspect the generated HTML rather than relying only on an editor preview.&lt;/p&gt;

&lt;p&gt;For repositories, view the committed file on the actual hosting platform. Local Markdown extensions in VS Code may not match GitHub or GitLab.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting Common Rendering Mismatches
&lt;/h2&gt;

&lt;p&gt;When a file that worked on one platform breaks on another, the failure usually falls into one of a handful of repeatable patterns. The table below lists the symptom as you would actually see it, the most likely cause, and a concrete command or check to confirm and fix it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;Confirm and fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A pipe table renders as one long paragraph with visible `\&lt;/td&gt;
&lt;td&gt;` characters&lt;/td&gt;
&lt;td&gt;Renderer is strict CommonMark without a tables extension&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;[^note]&lt;/code&gt; stays inline as literal text instead of becoming a superscript footnote marker&lt;/td&gt;
&lt;td&gt;The footnote Goldmark extension is not enabled&lt;/td&gt;
&lt;td&gt;In Hugo, check for &lt;code&gt;footnote&lt;/code&gt; under &lt;code&gt;markup.goldmark.extensions&lt;/code&gt; in &lt;code&gt;hugo.yaml&lt;/code&gt;, rebuild with &lt;code&gt;hugo --gc --minify&lt;/code&gt;, and look for &lt;code&gt;&amp;lt;sup&amp;gt;&lt;/code&gt; in the generated HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A &lt;code&gt;&lt;/code&gt;`&lt;code&gt;mermaid&lt;/code&gt; fence shows as plain grey source code instead of a diagram&lt;/td&gt;
&lt;td&gt;The platform performs no post-processing on the fenced block&lt;/td&gt;
&lt;td&gt;GitHub renders it natively; Hugo needs a render hook, shortcode, or JS pipeline — check the built HTML for &lt;code&gt;&amp;lt;pre&amp;gt;&amp;lt;code class="language-mermaid"&amp;gt;&lt;/code&gt; versus an &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;## Heading {#id}&lt;/code&gt; shows the literal curly braces in the rendered heading text&lt;/td&gt;
&lt;td&gt;Header attribute syntax is Pandoc-specific, not CommonMark or GFM&lt;/td&gt;
&lt;td&gt;Remove the attribute syntax for portable output, or pre-convert with &lt;code&gt;pandoc --from=markdown --to=gfm file.md -o out.md&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;[[Note Name]]&lt;/code&gt; displays as literal double square brackets&lt;/td&gt;
&lt;td&gt;Wiki link syntax is application-specific to tools like Obsidian&lt;/td&gt;
&lt;td&gt;Replace with a standard Markdown link, &lt;code&gt;[Note Name](note-name.md)&lt;/code&gt;, before exporting outside the vault&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;[@kwon2023pagedattention]&lt;/code&gt; stays as plain bracketed text instead of a formatted citation&lt;/td&gt;
&lt;td&gt;No bibliography or citeproc pass was applied&lt;/td&gt;
&lt;td&gt;Re-run with &lt;code&gt;pandoc --citeproc --bibliography=refs.bib input.md -o output.pdf&lt;/code&gt; and confirm the CSL style is specified&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;&amp;gt; [!WARNING]&lt;/code&gt; renders as an ordinary quoted paragraph instead of a styled alert&lt;/td&gt;
&lt;td&gt;Alert styling is a GitHub.com platform feature, not part of formal GFM&lt;/td&gt;
&lt;td&gt;Expected outside GitHub; keep the wording readable as a plain block quote rather than depending on the color styling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is the fastest first pass before assuming a Markdown "bug" — most of these mismatches are a missing extension or a platform-only feature, not broken syntax. For code-fence-specific issues such as missing syntax highlighting or unsupported language identifiers, see the dedicated guide on &lt;a href="https://www.glukhov.org/documentation-tools/markdown/markdown-codeblocks/" rel="noopener noreferrer"&gt;Markdown code blocks&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lint the Portable Subset
&lt;/h2&gt;

&lt;p&gt;A Markdown linter cannot guarantee renderer compatibility, but it can remove avoidable ambiguity.&lt;/p&gt;

&lt;p&gt;Useful rules include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use consistent heading styles&lt;/li&gt;
&lt;li&gt;Add blank lines around lists and code blocks&lt;/li&gt;
&lt;li&gt;Use fenced rather than indented code&lt;/li&gt;
&lt;li&gt;Specify code fence languages&lt;/li&gt;
&lt;li&gt;Avoid skipped heading levels&lt;/li&gt;
&lt;li&gt;Use consistent list markers&lt;/li&gt;
&lt;li&gt;Avoid ambiguous emphasis around punctuation&lt;/li&gt;
&lt;li&gt;Keep line endings consistent&lt;/li&gt;
&lt;li&gt;Validate links and images&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For multi-target publishing, add a build test for each important renderer rather than relying only on syntax linting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Converting Between Dialects with Pandoc
&lt;/h2&gt;

&lt;p&gt;Pandoc can normalize documents from one dialect to another:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;`bash&lt;br&gt;
pandoc \&lt;br&gt;
  --from=markdown \&lt;br&gt;
  --to=gfm \&lt;br&gt;
  article.md \&lt;br&gt;
  -o article-gfm.md&lt;br&gt;
`&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Or convert GFM into Pandoc Markdown:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;`bash&lt;br&gt;
pandoc \&lt;br&gt;
  --from=gfm \&lt;br&gt;
  --to=markdown \&lt;br&gt;
  README.md \&lt;br&gt;
  -o document.md&lt;br&gt;
`&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is useful, but conversion is not guaranteed to preserve every feature.&lt;/p&gt;

&lt;p&gt;Potential losses include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Platform-specific references&lt;/li&gt;
&lt;li&gt;Callout styling&lt;/li&gt;
&lt;li&gt;Complex tables&lt;/li&gt;
&lt;li&gt;Embedded application objects&lt;/li&gt;
&lt;li&gt;Custom attributes&lt;/li&gt;
&lt;li&gt;Raw HTML behavior&lt;/li&gt;
&lt;li&gt;Plugin syntax&lt;/li&gt;
&lt;li&gt;Diagram rendering&lt;/li&gt;
&lt;li&gt;Exact whitespace and formatting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pandoc preserves document structure better than original source formatting. Treat conversion as a build step, not a reversible text formatter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended Strategy for Hugo Sites
&lt;/h2&gt;

&lt;p&gt;For a Hugo technical blog, the most practical policy is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use CommonMark for core prose and structure.&lt;/li&gt;
&lt;li&gt;Enable a small documented set of Goldmark extensions.&lt;/li&gt;
&lt;li&gt;Use GFM-style tables and task lists where they improve readability.&lt;/li&gt;
&lt;li&gt;Implement Mermaid through one consistent render hook or shortcode.&lt;/li&gt;
&lt;li&gt;Handle math through one documented KaTeX or MathJax pipeline.&lt;/li&gt;
&lt;li&gt;Use Hugo front matter only at the start of content files.&lt;/li&gt;
&lt;li&gt;Prefer render hooks and shortcodes over raw HTML.&lt;/li&gt;
&lt;li&gt;Keep source links as standard Markdown links where possible.&lt;/li&gt;
&lt;li&gt;Test migrated or externally sourced documents through Hugo.&lt;/li&gt;
&lt;li&gt;Document any syntax that will not render correctly on GitHub.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach accepts that Hugo content is not universally portable while keeping the portability boundary visible.&lt;/p&gt;

&lt;p&gt;The worst approach is accidental dialect mixing: GitHub alerts, Obsidian embeds, Pandoc attributes, and Hugo shortcodes placed in the same document without a defined build pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Recommended syntax&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Portable plain-text document&lt;/td&gt;
&lt;td&gt;CommonMark&lt;/td&gt;
&lt;td&gt;Smallest reliable baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub README&lt;/td&gt;
&lt;td&gt;GFM&lt;/td&gt;
&lt;td&gt;Tables, tasks, and repository workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub issue template&lt;/td&gt;
&lt;td&gt;GFM plus GitHub features&lt;/td&gt;
&lt;td&gt;Platform is the intended target&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hugo blog post&lt;/td&gt;
&lt;td&gt;CommonMark plus configured Goldmark extensions&lt;/td&gt;
&lt;td&gt;Controlled publishing pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Academic paper&lt;/td&gt;
&lt;td&gt;Pandoc Markdown&lt;/td&gt;
&lt;td&gt;Citations, math, metadata, PDF output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-format book&lt;/td&gt;
&lt;td&gt;Pandoc Markdown&lt;/td&gt;
&lt;td&gt;Structured conversion to many outputs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Obsidian vault&lt;/td&gt;
&lt;td&gt;Obsidian Markdown&lt;/td&gt;
&lt;td&gt;Backlinks, embeds, and knowledge workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub and GitLab mirror&lt;/td&gt;
&lt;td&gt;Conservative GFM&lt;/td&gt;
&lt;td&gt;Strong shared feature set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unknown renderer&lt;/td&gt;
&lt;td&gt;CommonMark subset&lt;/td&gt;
&lt;td&gt;Lowest compatibility risk&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;CommonMark, GitHub Flavored Markdown, and Pandoc Markdown are not competing versions of the same product. They solve different problems.&lt;/p&gt;

&lt;p&gt;CommonMark provides a dependable parsing foundation. GFM adds practical features for software collaboration, while Pandoc Markdown turns Markdown into a rich source language for publishing and conversion.&lt;/p&gt;

&lt;p&gt;The safest rule is simple: write the smallest dialect that satisfies the real destination. Use CommonMark when content must travel, GFM when GitHub-style collaboration is the target, and Pandoc Markdown when document structure and output formats matter more than universal rendering.&lt;/p&gt;

&lt;p&gt;Markdown portability is not achieved by avoiding every extension. It is achieved by knowing which extensions are part of the source contract and testing them in every renderer that matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://spec.commonmark.org/0.31.2/" rel="noopener noreferrer"&gt;CommonMark Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.github.com/gfm/" rel="noopener noreferrer"&gt;GitHub Flavored Markdown Spec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pandoc.org/MANUAL.html#pandocs-markdown" rel="noopener noreferrer"&gt;Pandoc Markdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/yuin/goldmark" rel="noopener noreferrer"&gt;Goldmark Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gohugo.io/getting-started/configuration-markup/" rel="noopener noreferrer"&gt;Hugo Goldmark Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/writing-mathematical-expressions" rel="noopener noreferrer"&gt;Writing Mathematical Expressions on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>markdown</category>
      <category>hugo</category>
      <category>documentation</category>
      <category>wiki</category>
    </item>
    <item>
      <title>Data Gravity: The Real Cost of API-First AI</title>
      <dc:creator>Rost</dc:creator>
      <pubDate>Fri, 07 Aug 2026 09:54:30 +0000</pubDate>
      <link>https://dev.to/rosgluk/data-gravity-the-real-cost-of-api-first-ai-1p88</link>
      <guid>https://dev.to/rosgluk/data-gravity-the-real-cost-of-api-first-ai-1p88</guid>
      <description>&lt;p&gt;Every API call feels like a simple transaction - until enough of them accumulate that your fine-tuning data, evaluation harnesses, and tool schemas are all shaped around one vendor, and switching stops being a routing change.&lt;/p&gt;

&lt;p&gt;That's data gravity: the same force that made pulling data out of AWS S3 expensive long before AI existed, now operating one layer up the &lt;a href="https://www.glukhov.org/llm-hosting/" rel="noopener noreferrer"&gt;LLM hosting&lt;/a&gt; stack. It doesn't require a bad contract or a malicious vendor. It's compounding integration debt - every fine-tuned checkpoint, cached embedding, and evaluation harness tuned to one provider's output format makes the next one cheaper to add and the whole pile more expensive to move.&lt;/p&gt;

&lt;p&gt;The mechanism has four stages, and none of them announce themselves. Most teams don't decide to become dependent - they drift from Exploration into Integration, then Optimization, until Dependency feels less like a choice and more like the ground truth of their architecture. Recognizing which stage you're in, and what it costs to reverse, is the point of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mechanism: Four Stages of Lock-In
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph LR
    A[Exploration&amp;lt;br/&amp;gt;swap a base URL] --&amp;gt; B[Integration&amp;lt;br/&amp;gt;workflows assume&amp;lt;br/&amp;gt;the API's shape]
    B --&amp;gt; C[Optimization&amp;lt;br/&amp;gt;fine-tunes, caches,&amp;lt;br/&amp;gt;vector stores]
    C --&amp;gt; D[Dependency&amp;lt;br/&amp;gt;product quality =&amp;lt;br/&amp;gt;vendor's model]
    style A fill:#e8f4fd
    style B fill:#cfe8fb
    style C fill:#a8d4f5
    style D fill:#6fb3ea
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Exploration.&lt;/strong&gt; You call an API, prototype, iterate. Switching cost is low - changing a base URL and a key covers most of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integration.&lt;/strong&gt; You build workflows around the API's shape. Error handling assumes its rate-limit headers. Retry logic matches its backoff curves. Your evaluation harness is tuned to its output format. Switching now means refactoring, not routing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimization.&lt;/strong&gt; You fine-tune. You cache. You build vector stores and custom pipelines that depend on that provider's embedding space, tokenization, or tool-calling schema. Your data is embedded in their ecosystem. Switching means rebuilding, not refactoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependency.&lt;/strong&gt; Your product's performance depends on that provider's model quality. Downgrading to a self-hosted alternative means accepting lower capability. The trade-off stops being architectural and becomes product-level.&lt;/p&gt;

&lt;p&gt;Each stage compounds the previous one. The transition from Exploration to Dependency rarely feels like a decision - it feels like progress, right up until the moment a vendor changes the terms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It Matters Right Now
&lt;/h2&gt;

&lt;p&gt;Three forces are making data gravity urgent instead of theoretical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open-weight models are closing the capability gap.&lt;/strong&gt; Moonshot AI's Kimi K3, a 2.8-trillion-parameter sparse mixture-of-experts model released in July 2026, scored 57 on the &lt;a href="https://artificialanalysis.ai/articles/kimi-k3-achieves-3-in-the-artificial-analysis-intelligence-index-comparable-to-opus-4-8-and-gpt-5-5" rel="noopener noreferrer"&gt;Artificial Analysis Intelligence Index&lt;/a&gt; - third overall, comparable to Claude Opus 4.8 and GPT-5.5, and still behind Claude Fable 5 and GPT-5.6 Sol, but close enough that the gap is now a deliberate trade-off rather than a forced compromise. Qwen and DeepSeek ship under permissive licenses with native support across &lt;a href="https://www.glukhov.org/llm-hosting/vllm/vllm-quickstart/" rel="noopener noreferrer"&gt;vLLM&lt;/a&gt; and &lt;a href="https://www.glukhov.org/llm-hosting/sglang/" rel="noopener noreferrer"&gt;SGLang&lt;/a&gt;. For coding and infrastructure tasks specifically, open-weight models routinely land within 5-15% of frontier API quality - close enough that the lock-in cost, not the capability gap, becomes the deciding factor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Geopolitics is fragmenting data flows.&lt;/strong&gt; In July 2026, security researchers found that Claude Code had shipped hidden detection code since version 2.1.91 (April 2, 2026) that checked a user's system timezone against &lt;code&gt;Asia/Shanghai&lt;/code&gt; and &lt;code&gt;Asia/Urumqi&lt;/code&gt; and scanned proxy hostnames against a list of Chinese corporate and AI-lab domains - including Alibaba, Baidu, ByteDance, and Moonshot AI - encoding the match invisibly into the tool's own system prompt. Anthropic called it an anti-distillation experiment; Alibaba responded by banning Claude Code for its employees effective July 10, 2026, and ordering Claude models deleted from company infrastructure. Whatever the intent, the episode is a preview of a world where cross-border AI data flows carry protocol-level risk, not just contractual risk. If your data and your tool's behavior live in someone else's runtime, you're subject to decisions you can't audit - which is the same conclusion &lt;a href="https://www.glukhov.org/llm-hosting/self-hosting/llm-selfhosting-and-ai-sovereignty/" rel="noopener noreferrer"&gt;LLM Self-Hosting and AI Sovereignty&lt;/a&gt; reaches from the policy and jurisdiction side rather than the switching-cost side covered here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory economics are tightening.&lt;/strong&gt; SK Hynix CEO Kwak Noh-jung told Reuters in July 2026 that 2027 will be the memory industry's worst-ever supply shortage, with customer demand expected to outstrip production capacity "even beyond 2030." SambaNova closed the first tranche of a $1B Series F at an $11B valuation the same month, explicitly to scale inference hardware manufacturing. The narrative that API costs fall indefinitely was already shaky; a multi-year hardware shortage makes owning your inference stack a strategic hedge rather than a hobbyist preference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Cost Isn't Tokens
&lt;/h2&gt;

&lt;p&gt;The pricing comparison is the wrong frame. It's not "$0.01 per 1K input tokens vs. $0.002 self-hosted" - it's architectural dependency, and the clearest recent proof is &lt;a href="https://www.glukhov.org/ai-systems/openclaw/openclaw-rise-and-fall-timeline/" rel="noopener noreferrer"&gt;OpenClaw's collapse&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;OpenClaw grew to roughly 247,000 GitHub stars on the strength of running Claude through flat-rate Pro and Max subscriptions rather than metered API billing. On April 4, 2026, Anthropic revoked the ability to use those subscription OAuth tokens in third-party tools. Users who wanted to keep running OpenClaw with Claude had to switch to pay-as-you-go billing at 10 to 50 times the effective cost of their old plan. That is Dependency, stage four, made visible almost overnight: a huge community had optimized its entire workflow around one provider's specific pricing mechanism, and when that mechanism disappeared, the workflow's economics didn't degrade gracefully - they broke. The &lt;a href="https://www.glukhov.org/ai-systems/comparisons/openclaw-hermes-alternatives-popularity/" rel="noopener noreferrer"&gt;OpenClaw vs. Hermes usage data&lt;/a&gt; shows a meaningful share of that traffic migrating toward self-hosted and open-weight alternatives in the months after.&lt;/p&gt;

&lt;p&gt;The same pattern shows up quietly inside individual companies. A team that builds a code-review agent against one provider's API accumulates fine-tuning data in that provider's format, an evaluation harness tuned to that provider's output shape, and tool-calling integrations built around that provider's schema. None of that is measured in tokens. It's measured in engineering weeks the day you try to leave - the same architectural-dependency problem that the &lt;a href="https://www.glukhov.org/llm-architecture/" rel="noopener noreferrer"&gt;LLM Architecture&lt;/a&gt; cluster covers at the routing, cost, and guardrail layer above hosting.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Score Your Lock-In
&lt;/h2&gt;

&lt;p&gt;Count how many of these your team has accumulated for a given provider:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dependency&lt;/th&gt;
&lt;th&gt;Do you have this?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fine-tuning datasets stored in a provider-specific format&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cached embeddings tied to a provider's embedding space&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evaluation harnesses tuned to a provider's output shape&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom tool schemas built around a provider's tool-calling API&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team knowledge specific to a provider's failure modes and workarounds&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product features that assume a specific model's capability ceiling&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Billing or usage patterns tied to a provider-specific plan (subscription vs. metered)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;0-2 checked: Exploration - switching cost is still close to zero. 3-5: Integration - expect a real refactor. 6+: Optimization or Dependency - you are not choosing your AI provider anymore; you are renting your architecture from them. The count itself is the warning signal, and it costs nothing to calculate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Self-Hosting Actually Costs - and Doesn't
&lt;/h2&gt;

&lt;p&gt;The economics are real but secondary to the lock-in question. &lt;a href="https://www.glukhov.org/llm-architecture/cost-optimization/cost-optimization-for-llm-systems/" rel="noopener noreferrer"&gt;Cost Optimization for LLM Systems&lt;/a&gt; works through the hardware break-even math in detail - at roughly an hour or more of daily local usage, a consumer GPU like an RTX 4090 typically pays for itself against equivalent API spend within 4-8 months. That analysis is the right place for the $/token comparison; the point worth repeating here is that the break-even calculation only matters once you've decided portability is worth optimizing for. Teams deep in the Dependency stage often find the migration cost dwarfs any hardware savings, which is exactly the trap this article is about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Antidote: Portability as a Strategy
&lt;/h2&gt;

&lt;p&gt;The goal isn't to avoid APIs. It's to keep your data layer portable long enough to make deliberate choices instead of drifting into a stage you didn't choose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start local, go remote deliberately.&lt;/strong&gt; Prototype with self-hosted models - &lt;a href="https://www.glukhov.org/llm-hosting/llama-cpp/" rel="noopener noreferrer"&gt;llama.cpp&lt;/a&gt;, GGUF quantization, or a full comparison of &lt;a href="https://www.glukhov.org/llm-hosting/comparisons/hosting-llms-ollama-localai-jan-lmstudio-vllm-comparison/" rel="noopener noreferrer"&gt;local hosting tools&lt;/a&gt; to pick a stack. When a task genuinely needs frontier capability, use the API for that task specifically - but keep the data layer decoupled from which model answered it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prefer open-weight to closed-API when the quality gap is close.&lt;/strong&gt; When a model ships as open weights - Kimi K3, Qwen, Gemma, DeepSeek - you can run it, fine-tune it, quantize it, and own the relationship end to end. The capability gap is a known, shrinking, task-dependent trade-off. The lock-in gap is a slow-moving trap that doesn't announce its size until you try to leave.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build abstraction where it actually matters.&lt;/strong&gt; Not "wrap everything behind an interface" - that's a rule that delays the problem without solving it. Build the abstraction around data formats, evaluation logic, and tool schemas specifically: fine-tuning datasets in framework-agnostic formats (JSONL, parquet), evaluation harnesses that score model output rather than a specific API's response shape, and tool-calling logic that translates to and from provider-specific schemas rather than being written against one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Route deliberately instead of committing to one provider.&lt;/strong&gt; &lt;a href="https://www.glukhov.org/llm-architecture/model-routing/model-routing-strategies/" rel="noopener noreferrer"&gt;Model routing strategies&lt;/a&gt; - capability-based, cost-aware, latency-aware - let you send routine traffic to a local model and edge cases to a frontier API, which keeps you inside the Integration stage indefinitely instead of drifting into Optimization around a single vendor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quantify your lock-in on a schedule.&lt;/strong&gt; Re-run the scoring table above quarterly per provider. When the count climbs, that's data gravity doing its work whether or not anyone made an explicit decision to let it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Looks Like in Practice
&lt;/h2&gt;

&lt;p&gt;A practical stack that resists data gravity by design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inference:&lt;/strong&gt; llama.cpp for local, single-machine serving; vLLM or SGLang for production-grade self-hosted throughput. All three expose OpenAI-compatible APIs, so application code doesn't need to know which one is behind it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-tuning:&lt;/strong&gt; datasets stored in standard formats - JSONL, parquet - never a provider's proprietary fine-tuning-job format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluation:&lt;/strong&gt; framework-agnostic harnesses that score outputs, not API response envelopes, so the same eval suite runs whether the model is local or remote.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool calling:&lt;/strong&gt; a provider-agnostic JSON schema translated to and from each vendor's tool-calling format, rather than application logic written directly against one vendor's shape.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector stores:&lt;/strong&gt; local-first options such as Qdrant, Milvus, or Chroma, with embeddings computed through a portable library rather than tied to one provider's embedding endpoint - see &lt;a href="https://www.glukhov.org/rag/retrieval/chunking-strategies-in-rag/" rel="noopener noreferrer"&gt;chunking strategies in RAG&lt;/a&gt; for how this fits the retrieval layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn't a self-hosting manifesto. Plenty of workloads belong on a frontier API, permanently. It's a recognition that the engineers who can measure and manage data gravity - rather than discover it the day a vendor changes its pricing - end up with more options, not fewer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Data gravity is why open-weight capability matters more than a single benchmark score. A model that runs locally at 85-95% of a frontier model's quality is often the better architectural choice, because you keep the data relationship. The frontier race between GPT-5.6 Sol, Fable 5, Kimi K3, and Qwen is genuinely interesting, but the infrastructure layer underneath it - who holds the fine-tuning data, whose schema the tools speak, whose pricing model the workflow assumes - is what actually determines which teams have options in three years and which ones are renting their architecture from someone else.&lt;/p&gt;

&lt;p&gt;Score your lock-in before a vendor's pricing page forces the question for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://artificialanalysis.ai/articles/kimi-k3-achieves-3-in-the-artificial-analysis-intelligence-index-comparable-to-opus-4-8-and-gpt-5-5" rel="noopener noreferrer"&gt;Kimi K3 achieves #3 in the Artificial Analysis Intelligence Index&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thenextweb.com/news/alibaba-bans-claude-code-anthropic-tracking-chinese-users" rel="noopener noreferrer"&gt;Alibaba bans Claude Code after Anthropic is caught tracking Chinese users with hidden code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.tomshardware.com/pc-components/dram/sk-hynix-says-2027-will-be-the-worst-year-for-memory-shortage-forecasts-crunch-to-last-until-2030-ceo-shares-grim-outlook-on-the-day-sk-hynix-gets-listed-on-nasdaq" rel="noopener noreferrer"&gt;SK Hynix says 2027 will be the 'worst year' for memory shortage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sambanova.ai/press/sambanova-completes-first-close-of-1b-financing-at-11b-valuation" rel="noopener noreferrer"&gt;SambaNova Completes First Close of $1 Billion Financing at $11 Billion Valuation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>llm</category>
      <category>ai</category>
      <category>selfhosting</category>
    </item>
    <item>
      <title>Claude Code Subagents: Setup, Config, and When to Use Them</title>
      <dc:creator>Rost</dc:creator>
      <pubDate>Wed, 05 Aug 2026 09:07:21 +0000</pubDate>
      <link>https://dev.to/rosgluk/claude-code-subagents-setup-config-and-when-to-use-them-44m7</link>
      <guid>https://dev.to/rosgluk/claude-code-subagents-setup-config-and-when-to-use-them-44m7</guid>
      <description>&lt;p&gt;Most Claude Code sessions get slow and cluttered for the same reason: every exploratory grep, every log dump, and every "let me check one more file" stays in the main conversation forever.&lt;/p&gt;

&lt;p&gt;Subagents exist to fix exactly that problem. They are one of the agent primitives built into &lt;a href="https://www.glukhov.org/ai-devtools/claude-code/" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; for handling noisy, parallelizable work — a way to push the mess into an isolated window and bring back only the summary that matters.&lt;/p&gt;

&lt;p&gt;A subagent is not a smarter Claude, and it is not the same thing as a Skill. It is a separate reasoning agent with its own context window, its own tool allowlist, and no memory of your current conversation unless you explicitly fork it. Understanding that distinction is the difference between a subagent setup that quietly saves you context budget and one that just adds latency for no benefit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subagents vs Skills vs MCP
&lt;/h2&gt;

&lt;p&gt;Claude Code gives you three extension points that solve different problems, and they get conflated constantly because all three can technically "help with a task."&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;th&gt;When to reach for it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Skill&lt;/td&gt;
&lt;td&gt;Instructions loaded into the &lt;em&gt;main&lt;/em&gt; agent's context on demand&lt;/td&gt;
&lt;td&gt;Reusable procedures, checklists, playbooks — see &lt;a href="https://www.glukhov.org/ai-devtools/claude-code/claude-skills-for-developers/" rel="noopener noreferrer"&gt;Claude Skills for developers&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subagent&lt;/td&gt;
&lt;td&gt;A separate agent with its own context window, dispatched for delegated work&lt;/td&gt;
&lt;td&gt;Noisy exploration, parallelizable research, anything you want kept &lt;em&gt;out&lt;/em&gt; of the main session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP server&lt;/td&gt;
&lt;td&gt;An external tool/data connector exposed over a protocol&lt;/td&gt;
&lt;td&gt;Reaching systems outside the local session — APIs, databases, remote services&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A useful rule of thumb: a hook enforces a hard constraint deterministically, a Skill gives the main agent a capability inline, and a subagent is for work you want to delegate and keep out of the main context entirely. If a Skill's job is to orchestrate a tool that doesn't exist yet, that is usually a sign you need an MCP server, not a subagent. Claude Code isn't alone in this shape — OpenCode's ecosystem has a comparable idea in its &lt;a href="https://www.glukhov.org/ai-devtools/opencode/oh-my-opencode-agents/" rel="noopener noreferrer"&gt;specialised agents&lt;/a&gt;, which split planning, research, and review across dedicated roles in a similar way.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a subagent actually is
&lt;/h2&gt;

&lt;p&gt;Three properties define a Claude Code subagent, and all three matter for how you use it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolated context.&lt;/strong&gt; A subagent starts with a fresh window. It does not see your conversation history unless you explicitly fork it, which keeps its output from being polluted by whatever you discussed three turns ago.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A restricted tool allowlist.&lt;/strong&gt; Subagents can only use a subset of what the parent session already has — they cannot grant themselves new capabilities, and a well-designed subagent should get only the tools its job requires (read-only tools for a research agent, for example).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No cross-subagent visibility.&lt;/strong&gt; Subagents cannot see each other's work in progress. If task B genuinely needs task A's output, that is a sequential dependency, not something you can parallelize across two subagents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trigger for reaching for one is not "this task is hard." It is "this task is noisy" — the kind of work that generates a lot of intermediate output (dozens of file reads, a long log, an exploratory grep across the whole repo) where none of that intermediate material needs to survive into your next conversation turn.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use a subagent (and when not to)
&lt;/h2&gt;

&lt;p&gt;Good fits: codebase exploration before a big change, automated test runs where you only care about pass/fail and failure summaries, security or style reviews, and any multi-step research task whose raw output would otherwise flood your main session.&lt;/p&gt;

&lt;p&gt;Bad fits: two-second lookups ("what does this function return"), anything requiring tight back-and-forth refinement, and dependent tasks you're tempted to "parallelize" even though the second one needs the first one's answer. Using a subagent for a trivial lookup just adds the overhead of spinning up a fresh context window for no real isolation benefit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring the payoff: context and cost math
&lt;/h2&gt;

&lt;p&gt;The pitch for subagents is abstract until you put numbers on a real task. Take a common one: grep a ~500-file service for every place a deprecated config key is still read, then report the exact file:line matches.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Main-session context consumed&lt;/th&gt;
&lt;th&gt;What survives into your next turn&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Direct exploration, no subagent&lt;/td&gt;
&lt;td&gt;~35-45K tokens — every grep hit, every file you opened to double-check, every dead end&lt;/td&gt;
&lt;td&gt;All of it, including the wrong turns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delegated to an Explore subagent&lt;/td&gt;
&lt;td&gt;~1.5-3K tokens — one summarized report&lt;/td&gt;
&lt;td&gt;Only the findings that mattered&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's roughly a 15-20x reduction in what your main session has to carry for that step, which is the actual mechanism behind "subagents keep sessions faster" — it is not magic, it is context that never gets loaded in the first place.&lt;/p&gt;

&lt;p&gt;The cost side compounds the same way. Using the pricing from the &lt;a href="https://www.glukhov.org/ai-devtools/claude-code/" rel="noopener noreferrer"&gt;Claude Code pricing breakdown&lt;/a&gt;, running that same exploration pass on Opus (\$5/MTok input, \$25/MTok output) costs roughly \$0.20-0.25 for the ~40K input tokens alone. Routing it to Haiku (\$1/MTok input, \$5/MTok output) drops that to \$0.04-0.05 — and the main session's Opus budget is never touched by the exploration tokens at all, since it only ever sees the ~2K-token summary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining a custom subagent
&lt;/h2&gt;

&lt;p&gt;Custom subagents live as Markdown files with YAML frontmatter, either project-scoped in &lt;code&gt;.claude/agents/&lt;/code&gt; (committed to the repo, shared by the whole team) or user-scoped in &lt;code&gt;~/.claude/agents/&lt;/code&gt; (personal tools you bring to every project).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;code-reviewer&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;Reviews staged changes for bugs, security issues, and style violations&lt;/span&gt;
  &lt;span class="s"&gt;before commit. Use when the user asks to review, audit, or check&lt;/span&gt;
  &lt;span class="s"&gt;changes prior to committing or opening a PR.&lt;/span&gt;
&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Read, Grep, Glob&lt;/span&gt;
&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sonnet&lt;/span&gt;
&lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;security-checklist&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="s"&gt;You are a careful code reviewer. Read the staged diff, flag concrete&lt;/span&gt;
&lt;span class="s"&gt;issues with file:line references, and end with a short pass/fail summary.&lt;/span&gt;
&lt;span class="s"&gt;Do not modify any files.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;description&lt;/code&gt; field is the most important line in the file. It is what the parent session's routing logic reads to decide whether this subagent fits the current task. Write it like a job posting — name the trigger condition explicitly, not a vague "helps with code." Vague descriptions get skipped or misapplied by automatic dispatch.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;tools&lt;/code&gt; field is your isolation boundary. Give a research subagent &lt;code&gt;Read&lt;/code&gt;, &lt;code&gt;Grep&lt;/code&gt;, and &lt;code&gt;Glob&lt;/code&gt; and nothing else; giving it every tool available defeats the entire point of running it in a restricted sandbox. The optional &lt;code&gt;skills&lt;/code&gt; field preloads the full content of named Skills into the subagent's startup context — useful when a subagent needs domain knowledge without spending a turn discovering and loading it mid-task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model routing: cheap models for grunt work
&lt;/h2&gt;

&lt;p&gt;Subagents are also where cost control gets real. Route file discovery, log scanning, and other cheap-to-verify work to Haiku, and reserve Sonnet or Opus for the reasoning-heavy steps — architecture decisions, ambiguous debugging, anything where getting it wrong is expensive. Haiku is roughly 15x cheaper per token than Opus, and on the kind of noisy exploration subagents are built for, that gap adds up fast across a real working session.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Explore, Plan, Execute pattern
&lt;/h2&gt;

&lt;p&gt;For complex, multi-step work, the pattern that holds up in practice is Explore, Plan, Execute — using cheap subagents for the parts that generate noise, and keeping the human review gate in the one place it actually matters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant You
    participant Main as Main session
    participant Explore as Explore subagent (Haiku)
    participant Execute as Execute agent (Sonnet/Opus)

    You-&amp;gt;&amp;gt;Main: Describe the task
    Main-&amp;gt;&amp;gt;Explore: Delegate codebase exploration
    Explore--&amp;gt;&amp;gt;Main: Return summarized findings
    Main-&amp;gt;&amp;gt;Main: Enter Plan mode, propose approach
    Main-&amp;gt;&amp;gt;You: Show plan for review
    You-&amp;gt;&amp;gt;Main: Approve or adjust
    Main-&amp;gt;&amp;gt;Execute: Hand off approved plan
    Execute--&amp;gt;&amp;gt;Main: Apply changes, run tests
    Main--&amp;gt;&amp;gt;You: Report results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key detail people get backwards is where the review gate belongs. Exploration is cheap, so let a subagent read freely without asking permission first. Planning is analytical, so let the agent design the approach on its own. But before any agent modifies files, you want to see the plan and approve it — that is what Claude Code's plan mode (&lt;code&gt;permissionMode: plan&lt;/code&gt;) is for, and it is the same principle discussed in the broader &lt;a href="https://www.glukhov.org/ai-devtools/vibe-coding/" rel="noopener noreferrer"&gt;vibe coding best practices&lt;/a&gt; around reviewing every diff before it lands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;A handful of mistakes show up repeatedly once teams start writing custom subagents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vague descriptions.&lt;/strong&gt; "Helps with code" will never route correctly. Name the exact trigger condition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Over-broad tool access.&lt;/strong&gt; Giving a read-only research subagent write and bash access removes the isolation guarantee that made it worth creating in the first place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallelizing dependent tasks.&lt;/strong&gt; If task B needs task A's finished output, run them sequentially — subagents cannot coordinate mid-task the way a shared orchestrator can. For workflows that genuinely need agents talking to each other mid-task, that's a different shape of problem; see &lt;a href="https://www.glukhov.org/ai-systems/architecture/multi-agent-orchestration-patterns/" rel="noopener noreferrer"&gt;multi-agent orchestration patterns&lt;/a&gt; if you're building a production system rather than a single-repo workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using a subagent for trivial work.&lt;/strong&gt; "Format this JSON" or "run this one command" doesn't need a fresh context window; just do it directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Worked example: a code-review subagent end to end
&lt;/h2&gt;

&lt;p&gt;Say you want every non-trivial commit reviewed before it lands. Drop the &lt;code&gt;code-reviewer&lt;/code&gt; definition shown earlier into &lt;code&gt;.claude/agents/code-reviewer.md&lt;/code&gt;, commit it so the whole team shares the same reviewer, and invoke it with a natural request like "review my staged changes before I commit." Claude Code matches your request against the subagent's &lt;code&gt;description&lt;/code&gt;, spins it up with only &lt;code&gt;Read&lt;/code&gt;, &lt;code&gt;Grep&lt;/code&gt;, and &lt;code&gt;Glob&lt;/code&gt; access, and it comes back with file:line-referenced findings and a pass/fail summary — none of the file-by-file noise from getting there ever touches your main session.&lt;/p&gt;

&lt;p&gt;What that looks like in the main transcript, annotated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You:  review my staged changes before I commit

Main: [dispatches code-reviewer subagent — 6 files read, 1 grep pass,
       zero of it shown here]

Main: code-reviewer findings:
      - auth/session.go:142 — token refresh path doesn't handle expired
        refresh token; falls through to nil dereference
      - auth/session.go:203 — style: error wrapped without %w
      PASS/FAIL: FAIL (1 blocking issue)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six file reads and a grep pass happened, and your main session paid for exactly four lines of it. That gap — everything the subagent did versus the three-line summary you actually see — is the entire value proposition in one transcript.&lt;/p&gt;

&lt;p&gt;If your team also uses Spec-Driven Development scaffolds, a review subagent slots naturally into the validation step; see &lt;a href="https://www.glukhov.org/ai-devtools/ai-coding-assistants/spec-kit-vs-kiro-vs-claude-code/" rel="noopener noreferrer"&gt;GitHub Spec Kit vs Kiro vs Claude Code SDD Workflows&lt;/a&gt; for how that review gate compares across portable and IDE-integrated SDD setups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it worth setting up custom subagents?
&lt;/h2&gt;

&lt;p&gt;Not on day one. The built-in general-purpose subagent already covers most exploration and research delegation without you writing a single YAML file, and a single Explore-Plan-Execute pass is enough for most day-to-day work. Write a custom &lt;code&gt;.claude/agents/*.md&lt;/code&gt; file only once you've delegated the &lt;em&gt;same&lt;/em&gt; task by hand three times — a code reviewer, a test-runner triager, a docs-lookup agent for one specific internal library. Teams that write five subagents in their first week usually end up with five stale &lt;code&gt;description&lt;/code&gt; fields nobody updates when the actual trigger condition drifts, which quietly breaks automatic routing months later. Start with zero custom subagents, add one at a time, and only when repetition — not theoretical usefulness — demands it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Known limitations
&lt;/h2&gt;

&lt;p&gt;A few rough edges are worth knowing before you build around subagents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No recursive delegation.&lt;/strong&gt; A subagent cannot spawn its own subagents. If a task genuinely needs a second layer of delegation, that is a sign you want a different orchestration shape — see &lt;a href="https://www.glukhov.org/ai-systems/architecture/multi-agent-orchestration-patterns/" rel="noopener noreferrer"&gt;multi-agent orchestration patterns&lt;/a&gt; for what that looks like outside a single Claude Code session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No memory across invocations.&lt;/strong&gt; Every dispatch starts from zero, even if you called the same subagent five minutes ago on a related task. There is no built-in mechanism for a subagent to remember its last run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation is a tool allowlist, not a sandbox.&lt;/strong&gt; A subagent with &lt;code&gt;Bash&lt;/code&gt; access can still touch the filesystem and network like any other tool call. Restricting &lt;code&gt;tools&lt;/code&gt; reduces blast radius; it does not create a hard security boundary.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Subagent never triggers.&lt;/strong&gt; The description is almost always the problem. Rewrite it around the specific trigger condition instead of a general capability statement, and double-check the file lives in &lt;code&gt;.claude/agents/&lt;/code&gt; (project) or &lt;code&gt;~/.claude/agents/&lt;/code&gt; (personal) with the right extension.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subagent burns too much context anyway.&lt;/strong&gt; Check the &lt;code&gt;tools&lt;/code&gt; allowlist — an overly broad toolset invites overly broad exploration. Also check whether the task should have been split into two subagents instead of one doing everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A listed skill doesn't load inside the subagent.&lt;/strong&gt; Claude Code skips a missing or disabled skill named in the &lt;code&gt;skills&lt;/code&gt; field rather than failing the run, and logs a line to that effect in the debug output (&lt;code&gt;/debug&lt;/code&gt; from the main session, then reproduce the dispatch) — something like &lt;code&gt;skill "security-checklist" not found, skipping&lt;/code&gt;. Run &lt;code&gt;/doctor&lt;/code&gt; afterward to confirm the rest of your setup is healthy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Results feel inconsistent between runs.&lt;/strong&gt; This is often a model-routing issue, not a subagent-design issue — reasoning-heavy work assigned to a cheap model will vary more. Move it to Sonnet or Opus and keep Haiku for the deterministic, low-ambiguity steps.&lt;/p&gt;

&lt;p&gt;Subagents are one piece of a much larger toolbox; if you're comparing Claude Code against the rest of the &lt;a href="https://www.glukhov.org/ai-devtools/" rel="noopener noreferrer"&gt;AI developer tools ecosystem&lt;/a&gt; before committing to this workflow, that overview is a good next stop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.glukhov.org/ai-devtools/claude-code/" rel="noopener noreferrer"&gt;Claude Code install and config for Ollama, llama.cpp, pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.glukhov.org/ai-devtools/claude-code/claude-skills-for-developers/" rel="noopener noreferrer"&gt;Claude Skills and SKILL.md for Developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.glukhov.org/ai-devtools/ai-coding-assistants/spec-kit-vs-kiro-vs-claude-code/" rel="noopener noreferrer"&gt;GitHub Spec Kit vs Kiro vs Claude Code SDD Workflows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.glukhov.org/ai-devtools/vibe-coding/" rel="noopener noreferrer"&gt;What is Vibe Coding?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.glukhov.org/ai-systems/architecture/multi-agent-orchestration-patterns/" rel="noopener noreferrer"&gt;Multi-Agent Orchestration Patterns: A Practical Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.glukhov.org/ai-devtools/opencode/oh-my-opencode-agents/" rel="noopener noreferrer"&gt;Oh My Opencode Specialised Agents Deep Dive&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.glukhov.org/ai-devtools/" rel="noopener noreferrer"&gt;AI Developer Tools: The Complete Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>llm</category>
      <category>ai</category>
      <category>aicoding</category>
      <category>dev</category>
    </item>
    <item>
      <title>Ollama to vLLM: When to Migrate Your Local LLM Server</title>
      <dc:creator>Rost</dc:creator>
      <pubDate>Sun, 02 Aug 2026 13:59:56 +0000</pubDate>
      <link>https://dev.to/rosgluk/ollama-to-vllm-when-to-migrate-your-local-llm-server-26dd</link>
      <guid>https://dev.to/rosgluk/ollama-to-vllm-when-to-migrate-your-local-llm-server-26dd</guid>
      <description>&lt;p&gt;Ollama is one of the easiest ways to run a local language model, but convenience can conceal the moment when a local experiment becomes a shared inference service that needs better scheduling and observability.&lt;/p&gt;

&lt;p&gt;That is where vLLM becomes relevant. Migrating from Ollama to vLLM is not an automatic upgrade, however. It is a trade: you exchange some of Ollama's simplicity for greater control over batching, memory management, concurrency, distributed inference, and production operations.&lt;/p&gt;

&lt;p&gt;This guide covers the practical signals that indicate migration is warranted, the risks of moving too early, and a staged approach that keeps both servers running side by side during validation. The goal is to help you decide based on measurements rather than feature lists. For the wider landscape of local, self-hosted, and cloud options beyond just these two runtimes, see &lt;a href="https://www.glukhov.org/llm-hosting/" rel="noopener noreferrer"&gt;LLM Hosting in 2026: Local, Self-Hosted &amp;amp; Cloud Infrastructure Compared&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ollama and vLLM Solve Different Problems
&lt;/h2&gt;

&lt;p&gt;Ollama is primarily optimized for convenient model consumption. It gives developers a &lt;a href="https://www.glukhov.org/llm-hosting/ollama/ollama-cheatsheet/" rel="noopener noreferrer"&gt;concise command-line interface&lt;/a&gt;, a local API, a model library, Modelfiles, and straightforward support for common desktop and workstation configurations.&lt;/p&gt;

&lt;p&gt;vLLM is an inference engine and serving platform. Its central concerns are high-throughput request scheduling, efficient KV cache management, continuous batching, model parallelism, and compatibility with applications built for OpenAI-style APIs.&lt;/p&gt;

&lt;p&gt;The distinction matters because the two servers can look similar from the outside. Both can expose a chat API, stream tokens, run quantized models, and serve local applications. Their operating models become visibly different only when the server is placed under sustained or concurrent load.&lt;/p&gt;

&lt;p&gt;A useful summary:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Ollama&lt;/th&gt;
&lt;th&gt;vLLM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fast local setup&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;More involved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Curated model downloads&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Usually Hugging Face based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GGUF workflow&lt;/td&gt;
&lt;td&gt;First-class&lt;/td&gt;
&lt;td&gt;Supported, not main strength&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single-user chat&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Often unnecessary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrent API traffic&lt;/td&gt;
&lt;td&gt;Limited but configurable&lt;/td&gt;
&lt;td&gt;Core use case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Continuous batching&lt;/td&gt;
&lt;td&gt;Not the primary model&lt;/td&gt;
&lt;td&gt;Core feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prefix cache reuse&lt;/td&gt;
&lt;td&gt;Limited operational control&lt;/td&gt;
&lt;td&gt;Built-in optimization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-GPU model serving&lt;/td&gt;
&lt;td&gt;Limited compared with vLLM&lt;/td&gt;
&lt;td&gt;Tensor and pipeline parallelism&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production metrics&lt;/td&gt;
&lt;td&gt;Basic response timing data&lt;/td&gt;
&lt;td&gt;Prometheus metrics endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment tuning&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;td&gt;Extensive&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The question is not which server is universally better. It is whether your workload still matches the operating model that makes Ollama attractive. If you want the fuller picture across more than these two runtimes, &lt;a href="https://www.glukhov.org/llm-hosting/comparisons/hosting-llms-ollama-localai-jan-lmstudio-vllm-comparison/" rel="noopener noreferrer"&gt;our comparison of Ollama, vLLM, LocalAI, Jan, LM Studio and other local LLM tools&lt;/a&gt; covers the broader field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signs You Have Outgrown Ollama
&lt;/h2&gt;

&lt;p&gt;A slow response does not, by itself, justify a migration. Generation speed is often constrained by model size, quantization, memory bandwidth, prompt length, or GPU capability rather than the serving engine, and the stronger migration signals only appear once workload shape itself starts to matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple Users Cause Unstable Latency
&lt;/h3&gt;

&lt;p&gt;A local LLM server can feel fast during an isolated test and then degrade sharply when several clients connect. Requests begin waiting behind long generations, time to first token becomes inconsistent, and a single large prompt can affect everyone sharing the model.&lt;/p&gt;

&lt;p&gt;Ollama can process parallel requests, and &lt;code&gt;OLLAMA_NUM_PARALLEL&lt;/code&gt; controls how many requests a loaded model may handle concurrently — see &lt;a href="https://www.glukhov.org/llm-performance/ollama/how-ollama-handles-parallel-requests/" rel="noopener noreferrer"&gt;how Ollama handles parallel requests&lt;/a&gt; for the queuing and memory mechanics behind that setting. That parallelism is not free: memory requirements grow with both the configured parallel request count and context length.&lt;/p&gt;

&lt;p&gt;This is often the first practical warning. A configuration that works for one 8K conversation may become impossible when four clients each reserve a much larger context.&lt;/p&gt;

&lt;p&gt;vLLM is designed to combine work from active requests through continuous batching. Instead of treating each request as an isolated inference job, it continuously updates the batch as sequences arrive, generate tokens, and finish — a scheduling model that generally becomes more valuable as concurrency increases.&lt;/p&gt;

&lt;h3&gt;
  
  
  GPU Utilization Is Low While Requests Are Queued
&lt;/h3&gt;

&lt;p&gt;A queue does not necessarily mean that the GPU is fully used. In a simple serving arrangement, work may be serialized even though additional requests could have contributed useful computation to the current decode step.&lt;/p&gt;

&lt;p&gt;vLLM's scheduler is designed to keep more useful work in flight. PagedAttention manages KV cache memory in blocks, while continuous batching allows active sequences to enter and leave the execution batch dynamically.&lt;/p&gt;

&lt;p&gt;The result is not guaranteed to be lower latency for every individual request. Under load, however, it can produce substantially better aggregate throughput and more predictable resource utilization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Long Prompts Dominate Time to First Token
&lt;/h3&gt;

&lt;p&gt;Long-context coding assistants, RAG pipelines, and agent sessions can repeatedly send large system prompts or shared document prefixes. Processing those input tokens is the prefill stage, and it can dominate time to first token.&lt;/p&gt;

&lt;p&gt;vLLM supports chunked prefill and automatic prefix caching. Prefix caching allows later requests to reuse KV cache blocks when their initial token sequence matches an already processed prefix.&lt;/p&gt;

&lt;p&gt;This is particularly useful when requests share:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A long system prompt&lt;/li&gt;
&lt;li&gt;The same tool definitions&lt;/li&gt;
&lt;li&gt;A stable repository summary&lt;/li&gt;
&lt;li&gt;Repeated few-shot examples&lt;/li&gt;
&lt;li&gt;A common RAG document prefix&lt;/li&gt;
&lt;li&gt;A shared conversation history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prefix caching does not make output generation faster. It reduces repeated prompt computation, so its benefit depends on whether requests actually contain identical reusable prefixes.&lt;/p&gt;

&lt;h3&gt;
  
  
  You Need More Than One GPU
&lt;/h3&gt;

&lt;p&gt;A model that does not fit on one GPU is a strong reason to consider vLLM. It supports tensor parallelism across GPUs and pipeline parallelism across multiple nodes or devices.&lt;/p&gt;

&lt;p&gt;This does not make multi-GPU inference effortless. GPU interconnect bandwidth, PCIe topology, model architecture, container shared memory, and communication overhead still affect performance.&lt;/p&gt;

&lt;p&gt;Nevertheless, vLLM provides a deliberate path for distributed inference. Ollama is usually a better match for a single desktop or workstation where the chosen model already fits comfortably.&lt;/p&gt;

&lt;h3&gt;
  
  
  You Need Production-Level Observability
&lt;/h3&gt;

&lt;p&gt;Ollama API responses expose useful timing fields such as model load duration, prompt evaluation duration, generated token count, and generation duration. These values are enough for local benchmarking and application-level logging.&lt;/p&gt;

&lt;p&gt;vLLM exposes Prometheus-compatible metrics through its &lt;code&gt;/metrics&lt;/code&gt; endpoint. That makes it easier to track request volume, queueing, time to first token, inter-token latency, cache usage, preemptions, throughput, and request outcomes over time.&lt;/p&gt;

&lt;p&gt;Once users depend on the service, observability stops being optional. Without queue, cache, and latency metrics, it is difficult to distinguish an undersized GPU from an oversized context limit, poor scheduling, cold model loading, or simply too many simultaneous requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where vLLM Actually Wins
&lt;/h2&gt;

&lt;p&gt;vLLM's most important advantage is not that it can produce one response faster than Ollama on every machine. The meaningful advantage is that it gives the operator more mechanisms for using expensive accelerator memory and compute efficiently across many requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continuous Batching
&lt;/h3&gt;

&lt;p&gt;Traditional static batching works best when requests have similar input and output lengths. Interactive LLM traffic rarely behaves that way: one user asks for a short classification, another submits a 20K-token prompt, and a third generates several thousand tokens of code.&lt;/p&gt;

&lt;p&gt;Continuous batching changes the active batch as requests progress. Completed sequences leave, new sequences enter, and the engine attempts to avoid wasting batch capacity on requests that have already finished.&lt;/p&gt;

&lt;p&gt;This improves throughput when traffic is concurrent and uneven. It provides little benefit when a single user sends one request at a time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paged KV Cache Management
&lt;/h3&gt;

&lt;p&gt;During generation, the server stores attention keys and values for previously processed tokens. This KV cache can consume a large amount of GPU memory, especially with long contexts and multiple active sequences.&lt;/p&gt;

&lt;p&gt;vLLM manages this cache in blocks instead of requiring each sequence to reserve one large contiguous allocation. The approach reduces memory fragmentation and allows available cache capacity to be used more flexibly.&lt;/p&gt;

&lt;p&gt;The practical value is higher concurrency within the same memory budget. It does not remove the underlying cost of long context, but it reduces avoidable waste around that cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prefix Caching
&lt;/h3&gt;

&lt;p&gt;Many production requests share a substantial beginning. Tool-enabled agents may send identical function schemas, support bots may use the same policy documents, and coding assistants may repeatedly include the same repository instructions.&lt;/p&gt;

&lt;p&gt;Automatic prefix caching can reuse the computed cache for matching prefixes. It is especially useful when a stable, large prefix is followed by a relatively small request-specific suffix.&lt;/p&gt;

&lt;p&gt;It is less useful when templates, timestamps, document ordering, or dynamically generated metadata change near the beginning of every prompt. Small differences in tokenization can prevent the prefix from matching.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parallel and Distributed Inference
&lt;/h3&gt;

&lt;p&gt;vLLM supports several forms of parallelism, including tensor, pipeline, data, expert, and context parallelism. Not every deployment needs these modes, but their availability matters when a service grows beyond one GPU.&lt;/p&gt;

&lt;p&gt;For a workstation with two suitable GPUs, tensor parallelism may allow a larger model to run across both devices. For a replicated service, data parallelism can create multiple engine replicas for additional throughput.&lt;/p&gt;

&lt;p&gt;These features introduce operational complexity. They should be adopted because measurements demonstrate a capacity problem, not because distributed inference appears more sophisticated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Broader Production Controls
&lt;/h3&gt;

&lt;p&gt;vLLM exposes controls for GPU memory utilization, maximum model length, maximum active sequences, quantization, cache data types, &lt;a href="https://www.glukhov.org/llm-performance/optimization/speculative-decoding/" rel="noopener noreferrer"&gt;speculative decoding&lt;/a&gt;, tool calling, structured output, model aliases, authentication keys, and distributed execution.&lt;/p&gt;

&lt;p&gt;That flexibility makes the server easier to tune for a particular workload, but it also creates more opportunities for an invalid or inefficient configuration. Migrating to vLLM means taking responsibility for those decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Ollama Still Wins
&lt;/h2&gt;

&lt;p&gt;A migration guide should not treat Ollama as an inferior preliminary tool. For many local deployments, it remains the better server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Personal Workstations
&lt;/h3&gt;

&lt;p&gt;For one developer using a chat interface, code assistant, or occasional local API, the operational advantages of vLLM may never compensate for its additional setup.&lt;/p&gt;

&lt;p&gt;Ollama installs quickly, downloads models through a simple registry, and hides many model-specific details. It is well suited to experimentation and private desktop use.&lt;/p&gt;

&lt;h3&gt;
  
  
  GGUF Model Collections
&lt;/h3&gt;

&lt;p&gt;Ollama has a natural workflow around GGUF models and Modelfiles. Existing users may have curated quantizations, adapters, templates, system prompts, and parameters that work reliably with their hardware.&lt;/p&gt;

&lt;p&gt;vLLM supports GGUF, but its strongest path is generally through supported Hugging Face model repositories and quantization formats such as AWQ, GPTQ, BitsAndBytes, FP8, or vendor-specific formats. Moving an existing GGUF deployment to vLLM without evaluating a more native checkpoint format can preserve the inconvenience of migration while missing some of the performance advantages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mixed CPU and GPU Offloading
&lt;/h3&gt;

&lt;p&gt;Desktop inference sometimes relies on partial GPU offloading because the entire model does not fit in VRAM. This can be practical for occasional use, particularly when latency is not critical.&lt;/p&gt;

&lt;p&gt;vLLM is generally most compelling when the model and required KV cache capacity can be served effectively by the available accelerator configuration. A workload that depends heavily on system RAM and CPU offloading may be better suited to Ollama or llama.cpp.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rapid Model Switching
&lt;/h3&gt;

&lt;p&gt;Ollama makes it easy to pull, run, stop, and switch among many local models. That is useful for evaluation, writing, coding, embeddings, vision, and ad hoc experimentation.&lt;/p&gt;

&lt;p&gt;A vLLM deployment is more commonly built around a deliberately selected model that remains loaded as a service. Multi-model deployment is possible, but it requires more explicit resource planning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minimal Administration
&lt;/h3&gt;

&lt;p&gt;Ollama is intentionally opinionated. That can be a limitation under load, but it is an advantage when nobody wants to maintain an inference platform, and if the local server has one user, acceptable latency, and no meaningful queue, migration is likely to create work rather than remove it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do Not Migrate Based on Tokens per Second Alone
&lt;/h2&gt;

&lt;p&gt;Single-request token generation speed is an incomplete benchmark. Two servers may produce similar decode throughput for one sequence while behaving very differently with eight concurrent clients.&lt;/p&gt;

&lt;p&gt;A useful evaluation should measure at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Time to first token&lt;/li&gt;
&lt;li&gt;Inter-token latency&lt;/li&gt;
&lt;li&gt;End-to-end request latency&lt;/li&gt;
&lt;li&gt;Prompt processing throughput&lt;/li&gt;
&lt;li&gt;Output token throughput&lt;/li&gt;
&lt;li&gt;Requests completed per minute&lt;/li&gt;
&lt;li&gt;Queue wait time&lt;/li&gt;
&lt;li&gt;GPU memory consumption&lt;/li&gt;
&lt;li&gt;GPU utilization&lt;/li&gt;
&lt;li&gt;Failure and timeout rate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run the same model family, precision, context length, prompt set, output limit, and concurrency level on both servers. Otherwise, the test is more likely to compare model packaging and configuration than serving engines.&lt;/p&gt;

&lt;p&gt;The most useful comparison is a small load test that represents your real traffic. For a shared coding assistant, that might include long system prompts, repeated prefixes, streaming responses, and two to eight simultaneous sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plan the Model Migration First
&lt;/h2&gt;

&lt;p&gt;Ollama model names do not automatically map to equivalent vLLM model identifiers. An Ollama package may contain a particular GGUF quantization, prompt template, stop-token configuration, and default parameters.&lt;/p&gt;

&lt;p&gt;Before changing the server, identify:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The original model family and version&lt;/li&gt;
&lt;li&gt;Whether it is a base or instruction-tuned model&lt;/li&gt;
&lt;li&gt;The current quantization and effective precision&lt;/li&gt;
&lt;li&gt;The prompt or chat template&lt;/li&gt;
&lt;li&gt;The configured context length&lt;/li&gt;
&lt;li&gt;Stop tokens and generation defaults&lt;/li&gt;
&lt;li&gt;Tool-calling or structured-output requirements&lt;/li&gt;
&lt;li&gt;Any LoRA adapters or custom system prompts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then choose a vLLM-supported checkpoint that matches the intended behavior. Do not assume that an AWQ or FP8 checkpoint will behave identically to the GGUF build previously used in Ollama — the model migration is often more significant than the API migration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check VRAM Before Starting vLLM
&lt;/h2&gt;

&lt;p&gt;A model fitting into GPU memory does not mean that it can serve the required workload. VRAM must cover more than model weights.&lt;/p&gt;

&lt;p&gt;The practical memory budget includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model weights
+ KV cache
+ CUDA graphs and runtime allocations
+ temporary workspace
+ multimodal processor caches, if used
+ safety margin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Long contexts and concurrent sequences primarily expand the KV cache requirement. Increasing the maximum context length therefore reduces the number of simultaneous requests that can fit, even if most requests never use the full limit.&lt;/p&gt;

&lt;p&gt;Start with a realistic &lt;code&gt;--max-model-len&lt;/code&gt; rather than the largest value advertised by the model, and avoid setting GPU memory utilization so aggressively that minor workload variation causes out-of-memory failures. A stable service with slightly less theoretical capacity is more useful than one that fails at its first traffic spike.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Minimal vLLM Docker Compose Deployment
&lt;/h2&gt;

&lt;p&gt;The following example starts an OpenAI-compatible vLLM server on port 8000:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;vllm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vllm/vllm-openai:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vllm&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:8000"&lt;/span&gt;
    &lt;span class="na"&gt;ipc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host&lt;/span&gt;
    &lt;span class="na"&gt;gpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${HOME}/.cache/huggingface:/root/.cache/huggingface&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;HF_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${HF_TOKEN:-}&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--model&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Qwen/Qwen3-8B&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--served-model-name&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;local-model&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--max-model-len&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;16384"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--gpu-memory-utilization&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.90"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--api-key&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${VLLM_API_KEY:-change-me}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an environment file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
HF_TOKEN=
VLLM_API_KEY=replace-with-a-long-random-value
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; vllm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test the models endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8000/v1/models &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer replace-with-a-long-random-value"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Send a chat request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8000/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer replace-with-a-long-random-value"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "local-model",
    "messages": [
      {
        "role": "user",
        "content": "Explain continuous batching in two paragraphs."
      }
    ],
    "temperature": 0.2,
    "max_tokens": 300,
    "stream": false
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a maintained deployment, pin the image to a tested vLLM release instead of leaving it on &lt;code&gt;latest&lt;/code&gt;. Review release notes before upgrading because command-line options, model implementations, metrics, and engine behavior can evolve. This Compose file is intentionally minimal; for the fuller setup guide — OpenAI API compatibility, PagedAttention tuning, and a deeper vLLM-vs-Ollama comparison — see the &lt;a href="https://www.glukhov.org/llm-hosting/vllm/vllm-quickstart/" rel="noopener noreferrer"&gt;vLLM Quickstart&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenAI API Compatibility Is Not Complete Interchangeability
&lt;/h2&gt;

&lt;p&gt;Both Ollama and vLLM provide OpenAI-compatible endpoints, which can make the application migration relatively small. In many clients, changing the base URL, API key, and model name is enough to establish a connection.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8000/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace-with-a-long-random-value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local-model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What should I monitor on an LLM server?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compatibility should still be tested at the feature level. Examine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Streaming event behavior&lt;/li&gt;
&lt;li&gt;Supported request parameters&lt;/li&gt;
&lt;li&gt;Chat template selection&lt;/li&gt;
&lt;li&gt;Tool-call parsing&lt;/li&gt;
&lt;li&gt;Reasoning output handling&lt;/li&gt;
&lt;li&gt;JSON or schema-constrained output&lt;/li&gt;
&lt;li&gt;Embeddings endpoints&lt;/li&gt;
&lt;li&gt;Multimodal inputs&lt;/li&gt;
&lt;li&gt;Token usage reporting&lt;/li&gt;
&lt;li&gt;Error response formats&lt;/li&gt;
&lt;li&gt;Model name discovery&lt;/li&gt;
&lt;li&gt;Context-length enforcement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A client that only sends ordinary chat completions will usually be easier to migrate than an agent framework that depends on a particular tool-call parser or nonstandard extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chat Templates Are a Common Migration Failure
&lt;/h2&gt;

&lt;p&gt;Instruction-tuned models expect conversations to be serialized using a specific chat template. The template inserts role markers, separators, control tokens, and generation prompts in the format used during training.&lt;/p&gt;

&lt;p&gt;Ollama packages much of this behavior inside its model definition. With vLLM, the template is normally obtained from the model tokenizer configuration, although an operator can provide one explicitly.&lt;/p&gt;

&lt;p&gt;A server may start successfully even when the selected template is wrong. The symptoms appear in model behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The model repeats role labels&lt;/li&gt;
&lt;li&gt;Responses contain special tokens&lt;/li&gt;
&lt;li&gt;System instructions are ignored&lt;/li&gt;
&lt;li&gt;Tool calls are malformed&lt;/li&gt;
&lt;li&gt;The model continues the user message&lt;/li&gt;
&lt;li&gt;Output quality is much worse than expected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before blaming the inference engine, compare the fully rendered prompt used by each deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a Staged Migration
&lt;/h2&gt;

&lt;p&gt;Replacing a working local server in one step creates unnecessary risk. Ollama and vLLM can run side by side on different ports while you validate the new deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 1: Reproduce One Model
&lt;/h3&gt;

&lt;p&gt;Choose the model responsible for most API traffic and match its instruction tuning, context requirement, generation parameters, and chat behavior as closely as possible. Do not begin by moving every experimental model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 2: Validate API Behavior
&lt;/h3&gt;

&lt;p&gt;Run existing integration tests against the vLLM endpoint, including streaming, cancellation, timeouts, tool calls, malformed requests, context overflow, and concurrent access. Record behavioral differences rather than hiding them behind client retries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 3: Establish a Baseline
&lt;/h3&gt;

&lt;p&gt;Measure one-request performance first. This confirms that the model is loaded correctly and provides a reference for later tests.&lt;/p&gt;

&lt;p&gt;Record prompt tokens per second, output tokens per second, time to first token, total latency, and GPU memory usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 4: Add Realistic Concurrency
&lt;/h3&gt;

&lt;p&gt;Test the number of simultaneous requests expected in normal operation and during a plausible peak, using representative prompt and output lengths rather than identical synthetic requests. Watch queueing, cache use, preemptions, time to first token, and tail latency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 5: Move One Client
&lt;/h3&gt;

&lt;p&gt;Route a noncritical application or a small percentage of traffic to vLLM. Keep Ollama available as a fallback until the new server has operated reliably under real use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 6: Tune From Measurements
&lt;/h3&gt;

&lt;p&gt;Adjust model length, memory utilization, maximum active sequences, prefix caching, parallelism, and quantization only after identifying a measured constraint. Changing several parameters at once makes performance regressions difficult to explain.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Migration Checklist
&lt;/h2&gt;

&lt;p&gt;Before switching clients, verify the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] The target model is supported by vLLM
[ ] The selected checkpoint and quantization fit in VRAM
[ ] Enough VRAM remains for the required KV cache
[ ] The maximum context length reflects real usage
[ ] The correct chat template is available
[ ] Stop tokens and generation defaults are tested
[ ] Streaming works with existing clients
[ ] Tool calls and structured output are validated
[ ] The public model alias remains stable
[ ] Authentication is enabled
[ ] The server is not exposed directly to the internet
[ ] Prometheus metrics are collected
[ ] GPU metrics are collected separately
[ ] Load tests include realistic concurrency
[ ] Timeouts and cancellations are handled
[ ] A rollback path to Ollama exists
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This list is deliberately operational. Installing vLLM is usually easier than proving that it behaves correctly for an existing application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security and Network Exposure
&lt;/h2&gt;

&lt;p&gt;Neither a local Ollama endpoint nor a vLLM endpoint should be casually exposed to the public internet. An unauthenticated inference server can consume expensive GPU capacity, reveal model behavior, and become a route for denial-of-service attacks through very long prompts or outputs.&lt;/p&gt;

&lt;p&gt;vLLM can require an API key for its OpenAI-compatible endpoints, but an API key is not a complete security boundary. For shared or remote access, place the service behind a reverse proxy or API gateway that provides TLS, network restrictions, request-size limits, rate limits, access logging, and appropriate authentication — the same pattern covered in &lt;a href="https://www.glukhov.org/llm-hosting/ollama/ollama-behind-reverse-proxy/" rel="noopener noreferrer"&gt;Ollama behind a reverse proxy with Caddy or Nginx&lt;/a&gt; applies just as well in front of vLLM.&lt;/p&gt;

&lt;p&gt;Also consider model-specific risks. Multimodal URL loading, custom model code, remote files, and unrestricted tool execution can expand the attack surface beyond ordinary text generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Not to Migrate
&lt;/h2&gt;

&lt;p&gt;Stay with Ollama when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One or two users access the server&lt;/li&gt;
&lt;li&gt;Requests are mostly sequential&lt;/li&gt;
&lt;li&gt;The model already delivers acceptable latency&lt;/li&gt;
&lt;li&gt;Easy GGUF management is important&lt;/li&gt;
&lt;li&gt;CPU or partial GPU offloading is required&lt;/li&gt;
&lt;li&gt;Models are changed frequently&lt;/li&gt;
&lt;li&gt;Nobody wants to operate additional infrastructure&lt;/li&gt;
&lt;li&gt;There is no measured concurrency or throughput problem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A move to vLLM should solve a concrete limitation. "Production" is not a magic threshold that invalidates Ollama, especially for an internal service with modest traffic.&lt;/p&gt;

&lt;p&gt;Conversely, do not preserve Ollama merely because it was easier to install. If users regularly wait in a queue, repeated prefixes consume significant prefill time, or a larger model must be distributed across GPUs, the simpler server may have become the more expensive choice operationally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep Ollama for Development and Add vLLM for Shared Serving
&lt;/h2&gt;

&lt;p&gt;The most practical architecture is often not a complete replacement. Developers can keep &lt;a href="https://www.glukhov.org/llm-hosting/ollama/ollama-in-docker-compose/" rel="noopener noreferrer"&gt;Ollama running in Docker Compose&lt;/a&gt; on their workstations for model exploration, GGUF testing, and private interactive use while a shared vLLM instance serves a stable model to applications and teams. That split also matters for &lt;a href="https://www.glukhov.org/llm-hosting/self-hosting/llm-selfhosting-and-ai-sovereignty/" rel="noopener noreferrer"&gt;AI sovereignty&lt;/a&gt; — keeping both runtimes self-hosted means prompts, weights, and inference logs stay under your control regardless of which server handles a given request.&lt;/p&gt;

&lt;p&gt;This separates two different workflows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ollama:
experimentation -&amp;gt; model switching -&amp;gt; personal tools -&amp;gt; local chat

vLLM:
selected model -&amp;gt; shared endpoint -&amp;gt; concurrent traffic -&amp;gt; monitoring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The arrangement also lowers migration risk. Models can be tested locally before a suitable checkpoint is promoted to the shared vLLM deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration Decision Flow
&lt;/h2&gt;

&lt;p&gt;The following diagram summarizes the key decision points:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A[Ollama serving LLM] --&amp;gt; B{Multiple users&amp;lt;br&amp;gt;with unstable latency?}
    B --&amp;gt;|No| C[Stay with Ollama]
    B --&amp;gt;|Yes| D{Long shared&amp;lt;br&amp;gt;prefixes?}
    D --&amp;gt;|Yes| E[Strong vLLM signal]
    D --&amp;gt;|No| F{Need multi-GPU&amp;lt;br&amp;gt;or observability?}
    F --&amp;gt;|Yes| E
    F --&amp;gt;|No| G{Measured concurrency&amp;lt;br&amp;gt;problem?}
    G --&amp;gt;|No| C
    G --&amp;gt;|Yes| E
    E --&amp;gt; H[Plan staged migration]
    H --&amp;gt; I[Validate side by side]
    I --&amp;gt; J[Switch clients gradually]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Ollama is difficult to beat as a local model runner. It removes enough packaging and configuration work that developers can concentrate on the model and application rather than the inference stack.&lt;/p&gt;

&lt;p&gt;vLLM becomes the stronger choice when the server itself is the problem to be engineered. Concurrent traffic, queueing, repeated long prefixes, multi-GPU models, capacity planning, and production observability are the migration signals that matter.&lt;/p&gt;

&lt;p&gt;Do not migrate because vLLM has a longer feature list. Migrate when measurements show that Ollama's simpler operating model no longer matches the workload. Until that point, simplicity is not a technical weakness; it is an optimization.&lt;/p&gt;

</description>
      <category>ollama</category>
      <category>vllm</category>
      <category>llm</category>
      <category>ai</category>
    </item>
    <item>
      <title>Keeping Specs, Tests, And Code In Sync In AI Development</title>
      <dc:creator>Rost</dc:creator>
      <pubDate>Sat, 01 Aug 2026 10:43:20 +0000</pubDate>
      <link>https://dev.to/rosgluk/keeping-specs-tests-and-code-in-sync-in-ai-development-jm4</link>
      <guid>https://dev.to/rosgluk/keeping-specs-tests-and-code-in-sync-in-ai-development-jm4</guid>
      <description>&lt;p&gt;AI coding agents ship features fast, but specs, tests, and code quietly drift apart. This guide covers a traceability model, spec-to-test and spec-to-code mapping, and the CI checks that catch drift before a merge.&lt;/p&gt;

&lt;p&gt;A spec that nobody re-checks against the running system is worse than no spec at all, because it creates false confidence. Reviewers trust the document instead of the diff, and an AI agent asked to "follow the existing pattern" will happily follow whatever the code actually does, even when that contradicts the requirement it was supposed to satisfy.&lt;/p&gt;

&lt;p&gt;The fix is not more documentation. It is a small, enforceable link between four things that already exist in most repositories: the requirement, the design decision behind it, the tests that prove it, and the commits or pull requests that changed it.&lt;/p&gt;

&lt;p&gt;Once that link exists as data rather than as a shared understanding, you can query it. You can ask which requirements have no test coverage, which tests no longer map to any requirement, and which files changed in a pull request without a matching requirement ID. That query is the actual deliverable of this article, and the rest of the post walks through how to build it with tools you likely already run.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Drift Problem: Why Specs, Tests, And Code Fall Out Of Sync
&lt;/h2&gt;

&lt;p&gt;Drift shows up in four recognisable shapes, and AI-assisted teams tend to hit all four faster than teams that write every line by hand.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spec changes, code does not.&lt;/strong&gt; A requirement gets clarified in a follow-up conversation or a comment thread, but nobody regenerates or edits the implementation to match.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code changes, spec does not.&lt;/strong&gt; An agent or a developer fixes a bug or refactors a module, and the spec keeps describing the old behavior as if it were still current.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tests cover implementation, not intent.&lt;/strong&gt; Unit tests assert what the code currently does, which is circular: they pass by construction even when the code satisfies the wrong requirement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull requests do not reference requirements.&lt;/strong&gt; Reviewers approve a diff on the strength of "looks reasonable" because there is no explicit claim to check it against.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recent process research on AI development frameworks identifies specification drift as a recurring risk precisely because agents regenerate code quickly and repeatedly, and each regeneration is a fresh opportunity for spec and implementation to diverge a little further. The &lt;a href="https://www.glukhov.org/ai-devtools/vibe-coding/spec-driven-development-vs-vibe-coding/" rel="noopener noreferrer"&gt;Spec-Driven Development vs Vibe Coding&lt;/a&gt; debate is really an argument about this same failure mode: a spec that nobody enforces degenerates into the same drift you get without one, just with extra ceremony.&lt;/p&gt;

&lt;p&gt;Modern spec-kit-style workflows increasingly frame this as &lt;strong&gt;specification rot&lt;/strong&gt;: the spec keeps looking authoritative while quietly losing its connection to what the system actually does. The &lt;a href="https://www.glukhov.org/app-architecture/documentation/what-is-spec-driven-development/" rel="noopener noreferrer"&gt;core definition of spec-driven development&lt;/a&gt; treats the spec as the source of truth, but a source of truth only stays true if something keeps checking it against reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Traceability Model For AI-Assisted Development
&lt;/h2&gt;

&lt;p&gt;A workable traceability model needs six identifiers that connect a business requirement all the way down to the lines of code and the pull request that implemented it. Most teams already have three or four of these; the missing ones are usually the design decision ID and the explicit link back from tests and commits.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Identifier&lt;/th&gt;
&lt;th&gt;Lives in&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Requirement ID&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;requirements.md&lt;/code&gt; or spec tool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;REQ-014&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Design decision ID&lt;/td&gt;
&lt;td&gt;ADR / decision record&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ADR-0032&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task ID&lt;/td&gt;
&lt;td&gt;task breakdown or issue tracker&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TASK-014-3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test ID&lt;/td&gt;
&lt;td&gt;test file or test name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;test_req_014_password_reset&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commit / PR link&lt;/td&gt;
&lt;td&gt;Git history&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PR #482&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Changed files&lt;/td&gt;
&lt;td&gt;Git diff&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;auth/reset.go&lt;/code&gt;, &lt;code&gt;auth/reset_test.go&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The relationships between these identifiers form a graph rather than a straight line, because one requirement can spawn several tasks, and one pull request can touch several requirements at once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TD
    REQ["Requirement&amp;lt;br/&amp;gt;REQ-014"] --&amp;gt; ADR["Design Decision&amp;lt;br/&amp;gt;ADR-0032"]
    ADR --&amp;gt; TASK["Task&amp;lt;br/&amp;gt;TASK-014-3"]
    TASK --&amp;gt; CODE["Code Change&amp;lt;br/&amp;gt;auth/reset.go"]
    TASK --&amp;gt; TEST["Test&amp;lt;br/&amp;gt;test_req_014_password_reset"]
    CODE --&amp;gt; PR["Pull Request&amp;lt;br/&amp;gt;#482"]
    TEST --&amp;gt; PR
    PR --&amp;gt; COMMIT["Commit history"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Storing this graph as structured data, not prose, is what lets you query it later. GitHub's Spec Kit ecosystem has moved in exactly this direction: extensions like &lt;code&gt;spec-kit-trace&lt;/code&gt; scan &lt;code&gt;REQ-XXX&lt;/code&gt; tokens embedded in spec files and test files and generate a deterministic matrix from that literal text match, deliberately avoiding fuzzy name-based guessing that produces silent false positives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spec-To-Test Mapping: Turning Acceptance Criteria Into Test Cases
&lt;/h2&gt;

&lt;p&gt;Every acceptance criterion in a spec is, by construction, a behavioral assertion: given this state, when the actor does this, then the system should respond that way. That is already the shape of a test case, which is why the strongest SDD workflows generate tests from the same acceptance criteria that generate the code, instead of asking the code-generating agent to also invent its own tests after the fact.&lt;/p&gt;

&lt;p&gt;A widely used format for writing these criteria is EARS (Easy Approach to Requirements Syntax), which forces each requirement into an unambiguous, testable pattern such as "When &amp;lt;trigger&amp;gt;, the system shall &amp;lt;response&amp;gt;." That structure maps cleanly onto four categories of test that every requirement should carry:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Positive tests&lt;/strong&gt; — the happy path the requirement explicitly describes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Negative tests&lt;/strong&gt; — inputs or states the requirement says must be rejected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boundary tests&lt;/strong&gt; — the edges of ranges, limits, and thresholds mentioned in the acceptance criteria.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migration tests&lt;/strong&gt; — behavior for data or state that predates the requirement, so an old record does not silently bypass a new rule.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement type&lt;/th&gt;
&lt;th&gt;Test category to add&lt;/th&gt;
&lt;th&gt;Common miss&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"System shall reject X"&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Only the accept path is tested&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Limit is N items"&lt;/td&gt;
&lt;td&gt;Boundary&lt;/td&gt;
&lt;td&gt;N-1, N, and N+1 are not all covered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"New field replaces old field"&lt;/td&gt;
&lt;td&gt;Migration&lt;/td&gt;
&lt;td&gt;Old records with no new field crash silently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Within 60 seconds"&lt;/td&gt;
&lt;td&gt;Boundary + timing&lt;/td&gt;
&lt;td&gt;Test asserts logic, not the actual time budget&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Unit tests written this way still matter as the fast, cheap layer of the pyramid; the practical patterns for structuring them are covered in the &lt;a href="https://www.glukhov.org/app-architecture/testing-architecture/unit-testing-in-go/" rel="noopener noreferrer"&gt;Go unit testing guide&lt;/a&gt; and the &lt;a href="https://www.glukhov.org/app-architecture/testing-architecture/unit-testing-in-python/" rel="noopener noreferrer"&gt;Python unit testing guide&lt;/a&gt;. What traceability adds on top is a literal, stable requirement token embedded in the test name or a test comment, so a later query can prove — not assume — that &lt;code&gt;REQ-014&lt;/code&gt; has coverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spec-To-Code Mapping: From Design Plans To A Trace Table
&lt;/h2&gt;

&lt;p&gt;Spec-to-test mapping proves behavior; spec-to-code mapping proves scope. It answers a different question: which files were actually supposed to change for this requirement, and did the diff stay inside that boundary or spill into unrelated modules?&lt;/p&gt;

&lt;p&gt;A design plan that lists affected files up front — even a rough list — gives you something to diff the real pull request against later. Comments in code should only reference a requirement ID when doing so adds information a reviewer cannot get from the spec itself; a comment repeating the requirement text verbatim is noise, but &lt;code&gt;// enforces REQ-014 boundary: max 5 reset attempts per hour&lt;/code&gt; earns its place because the number is otherwise invisible in the diff.&lt;/p&gt;

&lt;p&gt;A generated trace table turns this into something reviewable in seconds rather than something a reviewer has to reconstruct by reading both documents side by side:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Design decision&lt;/th&gt;
&lt;th&gt;Files changed&lt;/th&gt;
&lt;th&gt;Tests&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;REQ-014&lt;/td&gt;
&lt;td&gt;ADR-0032&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;auth/reset.go&lt;/code&gt;, &lt;code&gt;auth/reset_test.go&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;test_req_014_*&lt;/code&gt; (4)&lt;/td&gt;
&lt;td&gt;Covered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;REQ-015&lt;/td&gt;
&lt;td&gt;ADR-0032&lt;/td&gt;
&lt;td&gt;&lt;code&gt;auth/reset.go&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Gap&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;REQ-016&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;auth/notify.go&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;test_notify_basic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Orphan spec link&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That single table surfaces two of the most common failure patterns at a glance: &lt;code&gt;REQ-015&lt;/code&gt; changed code with zero matching tests, and the test attached to &lt;code&gt;REQ-016&lt;/code&gt; does not actually reference a requirement ID, which means either the spec is missing or the test was misfiled.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pull Request Workflow: Reviewing Spec, Code, And Test Diffs Together
&lt;/h2&gt;

&lt;p&gt;A pull request built around traceability reviews three diffs side by side instead of one: what changed in the spec, what changed in the code, and what changed in the tests. The review question stops being "does this look right?" and becomes the far more specific "which requirement does this change satisfy, and does the evidence prove it?"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant Dev as Developer or Agent
    participant PR as Pull Request
    participant CI as CI Pipeline
    participant Rev as Reviewer

    Dev-&amp;gt;&amp;gt;PR: Open PR with spec diff + code diff + test diff
    PR-&amp;gt;&amp;gt;CI: Trigger traceability checks
    CI-&amp;gt;&amp;gt;CI: Verify REQ-ID present in PR description
    CI-&amp;gt;&amp;gt;CI: Run spec-to-test coverage check
    CI-&amp;gt;&amp;gt;CI: Run spec-to-code file-scope check
    CI--&amp;gt;&amp;gt;PR: Post trace report as PR comment
    Rev-&amp;gt;&amp;gt;PR: Review against "which requirement does this satisfy?"
    Rev-&amp;gt;&amp;gt;PR: Approve or request changes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A short, concrete reviewer checklist works better here than a long one, because reviewers skip long checklists under deadline pressure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does the PR description name the requirement ID(s) it satisfies?&lt;/li&gt;
&lt;li&gt;Does every changed file appear in the design plan's affected-files list, or is the extra scope explained?&lt;/li&gt;
&lt;li&gt;Does at least one new or existing test reference each requirement ID touched by this PR?&lt;/li&gt;
&lt;li&gt;If the spec changed, did the code and tests change in the same PR, or is there a tracked follow-up?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Automating Traceability In CI
&lt;/h2&gt;

&lt;p&gt;Manual review catches drift only as often as reviewers remember to look for it, which is why the checks above belong in CI rather than in a wiki page nobody re-reads. The same &lt;a href="https://www.glukhov.org/developer-tools/ci-cd/github-actions-cheatsheet/" rel="noopener noreferrer"&gt;GitHub Actions cheatsheet&lt;/a&gt; patterns you already use for build and test jobs apply directly here — traceability checks are just another job in the same pipeline.&lt;/p&gt;

&lt;p&gt;Practical automation ideas, roughly in order of effort:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CI checks for spec files&lt;/strong&gt; — fail the build if a spec file was edited without a corresponding code or test change in the same PR, or vice versa.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Require requirement IDs in PR titles or descriptions&lt;/strong&gt; — a lightweight regex check (&lt;code&gt;REQ-\d+&lt;/code&gt;) blocks merges that don't name what they implement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent-generated trace summaries&lt;/strong&gt; — have an agent produce a short summary of which requirements a PR touches, for a human to confirm rather than write from scratch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test coverage by acceptance criterion&lt;/strong&gt;, not just by line — line coverage tells you code ran; requirement coverage tells you a claim was checked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stale spec warnings&lt;/strong&gt; — flag specs that have not been touched in N commits touching their linked files, since long-silent specs are the ones most likely to have quietly rotted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Extensions built on top of GitHub's Spec Kit already implement several of these mechanically: one scans literal &lt;code&gt;REQ-XXX&lt;/code&gt; tokens across spec and test files to build a matrix and flag orphaned tests, and a stricter V-Model-oriented pack goes further, generating a paired test specification for every development specification and producing multiple traceability matrices for teams working under regulatory frameworks such as IEC 62304 or ISO 26262. You do not need that level of ceremony for most projects, but the underlying idea — a deterministic, script-generated matrix rather than a hand-maintained spreadsheet — scales down just as well as it scales up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using AI Agents For Traceability, Not As An Oracle
&lt;/h2&gt;

&lt;p&gt;AI agents are well suited to the mechanical parts of traceability and poorly suited to being the final judge of whether a requirement was actually satisfied. Three tasks fit an agent's strengths directly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compare spec and diff&lt;/strong&gt; — ask the agent to list every requirement mentioned in the spec files touched by a PR, and every one it did not find corresponding code for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find uncovered requirements&lt;/strong&gt; — ask the agent to scan the test suite for requirement tokens and report which requirements in the spec have none.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detect code not described by spec&lt;/strong&gt; — ask the agent to flag changed files or functions that touch requirement-bearing modules but do not correspond to any requirement ID in the diff.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The failure mode to guard against is trusting the agent's summary as ground truth instead of as a reviewer's starting point. An agent can misread a comment, miss a requirement token split across two files, or confidently declare coverage for a test that only exercises the code path superficially. Treat every agent-generated trace report the way you would treat a junior reviewer's pass: useful, fast, and still subject to a second look before it gates a merge. This is the same caution that applies to &lt;a href="https://www.glukhov.org/app-architecture/documentation/decision-records-ai-driven-development/" rel="noopener noreferrer"&gt;decision records for AI-driven development&lt;/a&gt; — the record only stays trustworthy if something other than the agent that wrote it eventually checks it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Minimal Traceability Template You Can Copy
&lt;/h2&gt;

&lt;p&gt;You do not need a heavyweight framework to start. A five-file template, checked into the repository next to the code it describes, covers the essentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docs/
  requirements.md     # REQ-IDs with EARS-style acceptance criteria
  design.md           # ADR-IDs, affected files, architecture decisions
  tasks.md            # TASK-IDs mapped to one or more REQ-IDs
  tests.md            # which test files/functions reference which REQ-IDs
  traceability.md     # generated table: REQ -&amp;gt; ADR -&amp;gt; TASK -&amp;gt; files -&amp;gt; tests -&amp;gt; PR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;requirements.md&lt;/code&gt;, &lt;code&gt;design.md&lt;/code&gt;, and &lt;code&gt;tasks.md&lt;/code&gt; are written or edited by humans and agents together, the same way the &lt;a href="https://www.glukhov.org/app-architecture/documentation/spec-driven-development-workflow/" rel="noopener noreferrer"&gt;spec-driven development workflow&lt;/a&gt; already describes. &lt;code&gt;tests.md&lt;/code&gt; and &lt;code&gt;traceability.md&lt;/code&gt; should be generated, not hand-maintained, even if the generator is a short script that just greps for &lt;code&gt;REQ-\d+&lt;/code&gt; across the test directory and the spec files — hand-maintained trace tables are themselves a form of drift risk, because nobody updates a spreadsheet under deadline pressure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Spec-driven development is not finished the moment code comes out of an agent; it is only useful once code, tests, and specs keep each other honest over time, through PRs, refactors, and requirement changes that arrive months apart. A traceability model built from six plain identifiers, enforced by a handful of CI checks, and reviewed with a short PR checklist gets you most of the benefit without the overhead of a full compliance framework. Start with the minimal template, wire the cheapest CI check first — requirement IDs in PR descriptions — and add the trace table and stale-spec warnings once that habit sticks.&lt;/p&gt;

&lt;p&gt;Traceability is one piece of a larger testing and documentation discipline covered across the &lt;a href="https://www.glukhov.org/app-architecture/" rel="noopener noreferrer"&gt;App Architecture in Production&lt;/a&gt; cluster, and it sits alongside the tooling questions explored in the &lt;a href="https://www.glukhov.org/ai-devtools/" rel="noopener noreferrer"&gt;AI developer tools&lt;/a&gt; cluster for teams choosing which agent workflows to standardize on.&lt;/p&gt;

</description>
      <category>aicoding</category>
      <category>architecture</category>
      <category>documentation</category>
      <category>llm</category>
    </item>
    <item>
      <title>Dead Letter Queues: Handling Poison Messages in Distributed Systems</title>
      <dc:creator>Rost</dc:creator>
      <pubDate>Fri, 31 Jul 2026 13:26:50 +0000</pubDate>
      <link>https://dev.to/rosgluk/dead-letter-queues-handling-poison-messages-in-distributed-systems-7p3</link>
      <guid>https://dev.to/rosgluk/dead-letter-queues-handling-poison-messages-in-distributed-systems-7p3</guid>
      <description>&lt;p&gt;A dead-letter queue is the safety net that catches messages your consumers cannot process, so one broken payload does not block or silently drop everything behind it in the queue.&lt;/p&gt;

&lt;p&gt;Every message-driven system eventually receives a message it cannot handle: a malformed payload, a schema that changed underneath the consumer, or a downstream call that fails no matter how many times you retry it. Without a dead-letter queue, that message either blocks the head of the queue forever or gets silently discarded, and both outcomes are worse than knowing about the failure.&lt;/p&gt;

&lt;p&gt;A DLQ turns an invisible failure into a visible, inspectable one. It gives you a place to quarantine the message, alert on it, and decide — deliberately, not by accident — whether to fix and replay it or discard it for good.&lt;/p&gt;

&lt;p&gt;The mechanics differ across brokers, but the underlying pattern is the same everywhere: a delivery-attempt counter, a threshold, and a destination for messages that cross it. This guide covers what a DLQ actually does, how to tell a poison message from a transient failure, when to retry versus discard, and how to replay safely once you have fixed the root cause. For the broader integration-patterns context this pattern sits inside, see the &lt;a href="https://www.glukhov.org/app-architecture/" rel="noopener noreferrer"&gt;App Architecture&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is a Dead Letter Queue
&lt;/h2&gt;

&lt;p&gt;A dead-letter queue is a separate, ordinary queue that a broker or consumer routes a message to after that message fails processing too many times. It is not a special construct — RabbitMQ's dead-letter queue is a regular queue bound to a regular exchange, and an SQS DLQ is a regular standard or FIFO queue. What makes a queue a "DLQ" is purely that something else points failed messages at it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    P[Producer] --&amp;gt; Q[Main Queue]
    Q --&amp;gt; C[Consumer]
    C -- ack: success --&amp;gt; Done[Message deleted]
    C -- fail / nack / timeout --&amp;gt; Q
    Q -- retry budget exhausted --&amp;gt; DLQ[Dead Letter Queue]
    DLQ --&amp;gt; I[Inspect / alert]
    I -- fix root cause --&amp;gt; R[Replay to main queue]
    I -- unrecoverable --&amp;gt; D[Archive / discard]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each broker implements the redirect differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon SQS&lt;/strong&gt; uses a redrive policy with a &lt;code&gt;maxReceiveCount&lt;/code&gt;. Once a message has been received that many times without being deleted, SQS moves it to the configured &lt;code&gt;deadLetterTargetArn&lt;/code&gt;. AWS explicitly recommends keeping the DLQ's message retention period longer than the source queue's, because the original enqueue timestamp — not the move time — still governs expiry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RabbitMQ&lt;/strong&gt; dead-letters a message when it is rejected with &lt;code&gt;requeue=false&lt;/code&gt;, its per-message TTL expires, the queue hits a length limit, or a quorum queue exceeds its &lt;code&gt;delivery-limit&lt;/code&gt;. You configure this with the &lt;code&gt;x-dead-letter-exchange&lt;/code&gt; (and optionally &lt;code&gt;x-dead-letter-routing-key&lt;/code&gt;) queue arguments, and RabbitMQ attaches &lt;code&gt;x-death&lt;/code&gt; headers recording the reason, the origin queue, and how many times it happened.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apache Kafka&lt;/strong&gt; has no broker-native DLQ. Kafka only tracks offsets; it has no concept of a "failed" message. The dead-letter topic pattern is something you build in the consumer, in a Kafka Streams topology, or in a Kafka Connect connector — commonly paired with a retry-topic tier before the terminal DLT, as Spring Kafka's &lt;code&gt;@RetryableTopic&lt;/code&gt; and &lt;code&gt;DeadLetterPublishingRecoverer&lt;/code&gt; do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure Service Bus&lt;/strong&gt; dead-letters automatically once a message's delivery count exceeds &lt;code&gt;MaxDeliveryCount&lt;/code&gt; (default 10), and also for a handful of system reasons such as &lt;code&gt;TTLExpiredException&lt;/code&gt;, &lt;code&gt;HeaderSizeExceeded&lt;/code&gt;, and &lt;code&gt;MaxTransferHopCountExceeded&lt;/code&gt;, each recorded in the message's &lt;code&gt;DeadLetterReason&lt;/code&gt; property.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a broader view of how brokers and streaming platforms fit together operationally rather than as a reliability pattern, &lt;a href="https://www.glukhov.org/data-infrastructure/stream-processing/apache-kafka/" rel="noopener noreferrer"&gt;Apache Kafka Quickstart&lt;/a&gt; and &lt;a href="https://www.glukhov.org/data-infrastructure/messaging/rabbitmq-on-eks-vs-sqs/" rel="noopener noreferrer"&gt;RabbitMQ on AWS EKS vs SQS&lt;/a&gt; cover the infrastructure side of running these brokers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Poison Messages
&lt;/h2&gt;

&lt;p&gt;A poison message is one that will never succeed no matter how many times a consumer retries it — a malformed JSON payload, a schema field that a producer renamed, a business rule violation, or a bug that throws on a specific input every single time. That is different from a transient failure, where the message is fine but the environment briefly is not: a downstream timeout, a database connection blip, a rate limit response.&lt;/p&gt;

&lt;p&gt;Treating both failure types the same way is the most common DLQ mistake. If you dead-letter on the first failure, you punish transient errors that would have succeeded on retry. If you retry poison messages dozens of times before giving up, you waste compute, delay unrelated messages behind them (on ordered queues and partitions), and flood your logs with the same stack trace.&lt;/p&gt;

&lt;p&gt;A few detection signals help separate the two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exception type.&lt;/strong&gt; Deserialization errors, validation errors, and &lt;code&gt;ClassCastException&lt;/code&gt;-style failures are almost always permanent. Spring Kafka's &lt;code&gt;DefaultErrorHandler&lt;/code&gt; explicitly treats certain exceptions as fatal and skips retries for them rather than exhausting the retry budget first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeat count with no variance.&lt;/strong&gt; RabbitMQ's &lt;code&gt;x-death&lt;/code&gt; header array lets you see exactly how many times a message has been dead-lettered and why; a message with a growing count and an identical &lt;code&gt;x-first-death-reason&lt;/code&gt; on every cycle is poison, not unlucky.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent failure across replicas.&lt;/strong&gt; If every consumer instance fails on the same message while succeeding on everything around it, the message itself is the problem, not the infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For distinguishing retryable from non-retryable failures at the code level — the same classification a DLQ policy depends on — see &lt;a href="https://www.glukhov.org/app-architecture/code-architecture/go-error-handling-architecture/" rel="noopener noreferrer"&gt;Go Error Handling Architecture: Boundaries and Patterns&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retry vs Discard
&lt;/h2&gt;

&lt;p&gt;The core policy decision behind every DLQ is the retry threshold: how many delivery attempts a message gets before it is quarantined. Get this too low and you dead-letter messages that would have succeeded after a brief downstream hiccup. Get it too high and a poison message sits in the main queue for a long time, consuming worker capacity and — on ordered systems — blocking everything queued behind it.&lt;/p&gt;

&lt;p&gt;Current guidance across the major brokers converges on similar numbers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Broker&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;Typical threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amazon SQS&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;maxReceiveCount&lt;/code&gt; in redrive policy&lt;/td&gt;
&lt;td&gt;3–5 for mixed transient/permanent workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RabbitMQ (quorum queues)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;delivery-limit&lt;/code&gt; policy argument&lt;/td&gt;
&lt;td&gt;3–5, tuned per queue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure Service Bus&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MaxDeliveryCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Default 10, often reduced for latency-sensitive queues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka (via retry topics)&lt;/td&gt;
&lt;td&gt;Retry-count header + retry-topic tier&lt;/td&gt;
&lt;td&gt;3–4 retry-topic hops before the terminal DLT&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A practical middle ground many teams land on is: start conservative (2–3 attempts), watch the actual failure mix in production, and raise the threshold only for queues where you can show most failures resolve within a few retries. Pair the retry count with &lt;strong&gt;exponential backoff and jitter&lt;/strong&gt; between attempts so a downstream outage does not turn into a retry storm — the same discipline covered in backoff and circuit-breaker design. A &lt;a href="https://www.glukhov.org/app-architecture/integration-patterns/circuit-breaker-pattern-in-go/" rel="noopener noreferrer"&gt;circuit breaker at the integration boundary&lt;/a&gt; complements this: it stops sending requests to an unhealthy dependency instead of letting every message in the queue individually discover the outage and dead-letter one by one.&lt;/p&gt;

&lt;p&gt;Once a message is in the DLQ, "discard" should still be a deliberate action, not neglect. Set a retention period on the DLQ itself — long enough to investigate (AWS recommends the DLQ retention exceed the source queue's; a week is a common floor for RabbitMQ DLQs) — and alert on DLQ depth and age so failures get triaged instead of silently expiring. A message that ages out of the DLQ unexamined is a message you decided to lose without deciding to lose it.&lt;/p&gt;

&lt;p&gt;Idempotency matters just as much here as it does anywhere else duplicates can occur: a message that gets redriven from a DLQ back to the main queue is, functionally, a duplicate delivery. If your consumer is not safe to run twice on the same message, redriving from a DLQ can create the exact duplicate-side-effect bug you were trying to avoid. See &lt;a href="https://www.glukhov.org/app-architecture/integration-patterns/idempotency-in-distributed-systems/" rel="noopener noreferrer"&gt;Idempotency in Distributed Systems That Actually Works&lt;/a&gt; for the consumer-side patterns that make redrive safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replay Strategies
&lt;/h2&gt;

&lt;p&gt;Getting a message out of the DLQ correctly is its own discipline, separate from getting it in.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fix the root cause first.&lt;/strong&gt; Deploying the consumer fix before replaying is the difference between a clean recovery and re-poisoning the queue with the same failure a second time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redrive deliberately, not automatically.&lt;/strong&gt; SQS supports a redrive-to-source feature that moves messages back to their original queue (or another destination) on demand; RabbitMQ and Kafka require you to build the equivalent consumer or tooling yourself. Either way, treat replay as an operator-triggered action with a record of what was replayed and when.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preserve ordering where it matters.&lt;/strong&gt; For Kafka, the dead-letter topic should have at least as many partitions as the source topic and should retain the original message key, so that replayed messages land back on the correct partition and preserve per-key ordering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cap replay attempts.&lt;/strong&gt; A message that fails again after a fix-and-replay cycle is not transient — route it to a permanent archive (a database table, an object-storage bucket) instead of looping it through the DLQ indefinitely. RabbitMQ's own docs warn that a dead-lettered message can be routed between queues only a limited number of times (16) before further TTL-based dead-lettering is disabled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never let a DLQ dead-letter into itself.&lt;/strong&gt; If your DLQ has its own &lt;code&gt;x-dead-letter-exchange&lt;/code&gt; (RabbitMQ) or its own redrive policy (SQS) pointed back at the same chain, a replay failure can create an infinite loop. Keep the DLQ's own dead-letter configuration empty, or point it at a strictly terminal archive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alert on volume, not just presence.&lt;/strong&gt; A single message in a DLQ is a data point; a sudden spike is an incident. Wire DLQ depth and message age into the same alerting pipeline you use for everything else — see &lt;a href="https://www.glukhov.org/observability/alerting/" rel="noopener noreferrer"&gt;Modern Alerting Systems Design for Observability Teams&lt;/a&gt; for routing and noise-reduction practices that apply directly to DLQ alerts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your workflow involves multi-step, long-running processes rather than single messages, the same dead-letter thinking applies at the workflow layer — a &lt;a href="https://www.glukhov.org/app-architecture/integration-patterns/saga-pattern-distributed-transactions/" rel="noopener noreferrer"&gt;saga's compensation logic&lt;/a&gt; needs the same "quarantine, inspect, decide" discipline when a step fails permanently instead of transiently. And when the events themselves originate from a database write, the &lt;a href="https://www.glukhov.org/app-architecture/integration-patterns/transactional-outbox-pattern-go/" rel="noopener noreferrer"&gt;transactional outbox pattern&lt;/a&gt; already builds dead-letter handling into the relay worker, so the pattern shows up one layer earlier than the broker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where DLQs Fit in the Bigger Picture
&lt;/h2&gt;

&lt;p&gt;A dead-letter queue does not make failures go away — it makes them survivable and reviewable instead of silent. It works best alongside retries with backoff for the transient case, idempotent consumers so redrive is safe, and a circuit breaker so a struggling dependency does not flood the main queue (and, eventually, the DLQ) with the same failure thousands of times over. Treat the DLQ threshold, retention, and alerting as first-class configuration decisions, not defaults you leave untouched, and dead letters become a diagnostic tool instead of a place where data quietly disappears.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html" rel="noopener noreferrer"&gt;Amazon SQS Developer Guide, Dead-letter queues&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rabbitmq.com/docs/dlx" rel="noopener noreferrer"&gt;RabbitMQ Documentation, Dead Letter Exchanges&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.enterprise.spring.io/spring-kafka/reference/retrytopic/dlt-strategies.html" rel="noopener noreferrer"&gt;Spring Kafka Documentation, DLT Strategies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dead-letter-queues" rel="noopener noreferrer"&gt;Microsoft Learn, Service Bus dead-letter queues&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://factorhouse.io/articles/dead-letter-queues-kafka" rel="noopener noreferrer"&gt;Factor House, Dead letter queues in Kafka: patterns and pitfalls&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dev</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
