<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Marc Nuri - Blogging about business and technology</title>
        <link>https://blog.marcnuri.com</link>
        <description>Blogging about business and technology</description>
        <lastBuildDate>Wed, 22 Jul 2026 10:06:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>www.marcnuri.com</generator>
        <language>en-US</language>
        <atom:link href="https://blog.marcnuri.com/feed.atom.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Eclipse JKube 1.20 is now available!]]></title>
            <link>https://blog.marcnuri.com/eclipse-jkube-1-20</link>
            <guid isPermaLink="false">https://blog.marcnuri.com/eclipse-jkube-1-20</guid>
            <pubDate>Wed, 22 Jul 2026 09:00:00 GMT</pubDate>
            <description><![CDATA[Eclipse JKube 1.20 is available! Check out the major changes and learn how you can contribute.]]></description>
            <content:encoded><![CDATA[
    <div><a href="https://blog.marcnuri.com/eclipse-jkube-1-20">Original post</a></div>
    <p>On behalf of the <a class="post-link " title="Eclipse JKube introduction: Java tools and plugins for Kubernetes and OpenShift" href="/eclipse-jkube-introduction-kubernetes-openshift">Eclipse JKube</a>
team and everyone who has contributed, I'm happy to announce that Eclipse JKube <code>1.20.0</code> has been
<a href="https://github.com/eclipse-jkube/jkube/releases/tag/v1.20.0" rel="noopener" title="Link to https://github.com/eclipse-jkube/jkube/releases/tag/v1.20.0" aria-label="released" target="_blank">released</a> and is now available from
<a href="https://repo1.maven.org/maven2/org/eclipse/jkube/kubernetes-maven-plugin/1.20.0/" rel="noopener" title="Link to https://repo1.maven.org/maven2/org/eclipse/jkube/kubernetes-maven-plugin/1.20.0/" aria-label="Maven Central" target="_blank">Maven Central</a> 🎉.</p>
<p>Thanks to all of you who have contributed with issue reports, pull requests, feedback, and spreading the word
with blogs, videos, comments, and so on.
We really appreciate your help, keep it up!</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-20#whats-new" aria-label="whats-new permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="whats-new"></span>What's new?</h2>
<p>Without further ado, let's have a look at the most significant updates:</p>
<ul>
<li><a class="post-link " title="Link to the Jetty 12 section" href="/eclipse-jkube-1-20#jetty12">Jetty 12 is now the default web application server</a></li>
<li><a class="post-link " title="Link to the JDK selection section" href="/eclipse-jkube-1-20#jdk-selection">Explicit JDK selection for Java generators</a></li>
<li><a class="post-link " title="Link to the image pull policy section" href="/eclipse-jkube-1-20#image-pull-policy">Fine-grained image pull policy control</a></li>
<li><a class="post-link " title="Link to the Maven compatibility section" href="/eclipse-jkube-1-20#maven-compatibility">Compatibility with Maven 3.9.13 and newer</a></li>
<li><a class="post-link " title="Link to the workload redeployment section" href="/eclipse-jkube-1-20#workload-redeploy">StatefulSet and DaemonSet redeployment fixes</a></li>
<li>Spring Boot DevTools now wires up in the Gradle <code>k8sWatch</code>/<code>ocWatch</code> tasks</li>
<li>🐛 Many other bug-fixes and minor improvements</li>
</ul>
<h3 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-20#jetty12" aria-label="jetty12 permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="jetty12"></span>Jetty 12 is now the default web application server</h3>
<p>Web application (WAR) projects now build on top of the <code>jkube-jetty12</code> base image by default, replacing the
older <code>jkube-jetty9</code> image. Jetty 12 brings support for the Jakarta EE namespaces (<code>jakarta.*</code>) and keeps your
containers running on an actively maintained server.</p>
<p>The legacy <code>jkube-jetty9</code> image is now deprecated. If your project is a legacy Java EE web application that still
relies on the <code>javax.*</code> namespaces, you can opt back in to Jetty 9 by setting the
<code>jkube.generator.webapp.server=jetty9</code> property. Bear in mind this switch is potentially breaking for those
legacy applications, so make sure to test your deployment after upgrading.</p>
<p>On top of the new default, hot deployment now works with Jetty 12 when using <code>k8s:watch</code>/<code>oc:watch</code> in copy mode,
so you can keep iterating on your web application without rebuilding the whole image.</p>
<h3 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-20#jdk-selection" aria-label="jdk-selection permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="jdk-selection"></span>Explicit JDK selection for Java generators</h3>
<p>You can now explicitly select the JDK version used by the base image for Java generators through the new
<code>jkube.java.version</code> property. Previously the JDK was inferred automatically, which sometimes led to a base image
that didn't match the version you were targeting. With this property you can pin the exact JDK your workload needs.</p>
<h3 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-20#image-pull-policy" aria-label="image-pull-policy permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="image-pull-policy"></span>Fine-grained image pull policy control</h3>
<p>Image pull policy handling is now more consistent and flexible:</p>
<ul>
<li>A per-image <code>imagePullPolicy</code> set in the build configuration now correctly overrides the global pull policy,
so you can fine-tune the behavior on an image-by-image basis.</li>
<li>The Buildpacks build service now honors the global <code>imagePullPolicy</code> when no per-image policy is set, aligning
its behavior with the other build strategies.</li>
</ul>
<h3 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-20#maven-compatibility" aria-label="maven-compatibility permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="maven-compatibility"></span>Compatibility with Maven 3.9.13 and newer</h3>
<p>The Kubernetes Maven Plugin is now compatible with the <code>SecDispatcher</code> changes introduced in Maven 3.9.13. Registry
and Helm passwords are decrypted using Maven's <code>SettingsDecrypter</code>, which is the standard, supported mechanism for
handling encrypted credentials.</p>
<p>As a result, the <code>&lt;helm&gt;&lt;security&gt;</code> configuration and the <code>jkube.helm.security</code> property are now deprecated in favor
of the standard <code>-Dsettings.security</code> setting.</p>
<h3 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-20#workload-redeploy" aria-label="workload-redeploy permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="workload-redeploy"></span>StatefulSet and DaemonSet redeployment fixes</h3>
<p>Generated <code>StatefulSet</code> and <code>DaemonSet</code> resources no longer include the version label in their
<code>selector.matchLabels</code>. Because the selector is an immutable field, keeping a version label there prevented these
workloads from being redeployed after a version bump. With this fix, you can bump your application version and
redeploy <code>StatefulSet</code> and <code>DaemonSet</code> workloads without running into update errors.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-20#using-this-release" aria-label="using-this-release permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="using-this-release"></span>Using this release</h2>
<p>If your project is based on Maven, you just need to add the Kubernetes Maven plugin or the OpenShift Maven
plugin to your plugin dependencies:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-xml" style="white-space:pre"><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">plugin</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>org.eclipse.jkube</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>kubernetes-maven-plugin</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">version</span><span style="color:#e8bf6a">&gt;</span><span>1.20.0</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">version</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span></span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">plugin</span><span style="color:#e8bf6a">&gt;</span></code></pre></div>
<p>If your project is based on Gradle, you just need to add the Kubernetes Gradle plugin or the OpenShift Gradle
plugin to your plugin dependencies:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-groovy" style="white-space:pre"><span>plugins {
</span><span>  id </span><span style="color:#a5c261">'org.eclipse.jkube.kubernetes'</span><span> version </span><span style="color:#a5c261">'1.20.0'</span><span>
</span>}</code></pre></div>
<h2 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-20#how-can-you-help" aria-label="how-can-you-help permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="how-can-you-help"></span>How can you help?</h2>
<p>If you're interested in helping out and are a first-time contributor, check out the
<a href="https://github.com/eclipse-jkube/jkube/labels/first-timers-only" rel="noopener" title="Link to https://github.com/eclipse-jkube/jkube/labels/first-timers-only" aria-label="&quot;first-timers-only&quot;" target="_blank">"first-timers-only"</a>
tag in the issue repository.
We've tagged extremely easy issues so that you can get started contributing to Open Source and the Eclipse organization.</p>
<p>If you are a more experienced developer or have already contributed to JKube, check the
<a href="https://github.com/eclipse-jkube/jkube/labels/help%20wanted" rel="noopener" title="Link to https://github.com/eclipse-jkube/jkube/labels/help%20wanted" aria-label="&quot;help wanted&quot;" target="_blank">"help wanted"</a> tag.</p>
<p>We're also excited to read articles and posts mentioning our project and sharing the user experience.
Feedback is the only way to improve.</p>
<p><a href="https://www.eclipse.org/jkube" rel="noopener" title="Link to https://www.eclipse.org/jkube" aria-label="Project Page" target="_blank">Project Page</a> |
<a href="https://github.com/eclipse-jkube/jkube" rel="noopener" title="Link to https://github.com/eclipse-jkube/jkube" aria-label="GitHub" target="_blank">GitHub</a> |
<a href="https://github.com/eclipse-jkube/jkube/issues" rel="noopener" title="Link to https://github.com/eclipse-jkube/jkube/issues" aria-label="Issues" target="_blank">Issues</a> |
<a href="https://gitter.im/eclipse/jkube" rel="noopener" title="Link to https://gitter.im/eclipse/jkube" aria-label="Gitter" target="_blank">Gitter</a> |
<a href="https://accounts.eclipse.org/mailing-list/jkube-dev" rel="noopener" title="Link to https://accounts.eclipse.org/mailing-list/jkube-dev" aria-label="Mailing list" target="_blank">Mailing list</a> |
<a href="https://stackoverflow.com/questions/tagged/jkube" rel="noopener" title="Link to https://stackoverflow.com/questions/tagged/jkube" aria-label="Stack Overflow" target="_blank">Stack Overflow</a></p>
<span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/af6673438e09cd98812665335c800cbf/e5189/JKube-blog-banner.jpg" class="post-image__link" title="The logo of Eclipse JKube"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/af6673438e09cd98812665335c800cbf/15be0/JKube-blog-banner.webp 200w,/static/af6673438e09cd98812665335c800cbf/f0f61/JKube-blog-banner.webp 400w,/static/af6673438e09cd98812665335c800cbf/eb6ca/JKube-blog-banner.webp 800w" sizes="(min-width: 800px) 800px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 800px) 800px, 100vw" decoding="async" loading="lazy" data-src="/static/af6673438e09cd98812665335c800cbf/e5189/JKube-blog-banner.jpg" data-srcset="/static/af6673438e09cd98812665335c800cbf/f8ab9/JKube-blog-banner.jpg 200w,/static/af6673438e09cd98812665335c800cbf/a4161/JKube-blog-banner.jpg 400w,/static/af6673438e09cd98812665335c800cbf/e5189/JKube-blog-banner.jpg 800w" alt="The logo of Eclipse JKube" src="https://blog.marcnuri.com/static/af6673438e09cd98812665335c800cbf/e5189/JKube-blog-banner.jpg" srcset="https://blog.marcnuri.com/static/af6673438e09cd98812665335c800cbf/f8ab9/JKube-blog-banner.jpg 200w,https://blog.marcnuri.com/static/af6673438e09cd98812665335c800cbf/a4161/JKube-blog-banner.jpg 400w,https://blog.marcnuri.com/static/af6673438e09cd98812665335c800cbf/e5189/JKube-blog-banner.jpg 800w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure>
  ]]></content:encoded>
            <category>Cloud Native</category>
            <enclosure url="https://blog.marcnuri.com/static/af6673438e09cd98812665335c800cbf/e5189/JKube-blog-banner.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Project, Agent, Person: The Missing Axis of AI Coding Tools]]></title>
            <link>https://blog.marcnuri.com/project-agent-person-centric-ai-coding-tools</link>
            <guid isPermaLink="false">https://blog.marcnuri.com/project-agent-person-centric-ai-coding-tools</guid>
            <pubDate>Mon, 06 Jul 2026 09:00:00 GMT</pubDate>
            <description><![CDATA[Everyone ranks AI coding tools by how autonomous they are. The axis that predicts the gaps is what they're built around: the project, the agent, or you.]]></description>
            <content:encoded><![CDATA[
    <div><a href="https://blog.marcnuri.com/project-agent-person-centric-ai-coding-tools">Original post</a></div>
    <h2 class="heading"><a href="https://blog.marcnuri.com/project-agent-person-centric-ai-coding-tools#introduction" aria-label="introduction permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="introduction"></span>Introduction</h2>
<p>A few months ago I wrote about <a class="post-link " title="The Missing Levels of AI-Assisted Development: From Agent Chaos to Orchestration" href="/missing-levels-ai-assisted-development">the missing levels between juggling coding agents and building your own orchestrator</a>.
That post ended on an uncomfortable note: at some point the tooling runs out, and you are told to build the orchestrator yourself.</p>
<p>This post is about why.
Why, with dozens of well-funded <a class="category-link " title="Artificial Intelligence: Everything related to artificial intelligence (AI) and machine learning (ML)" aria-label="AI coding tools" href="/category/ai">AI coding tools</a> shipping every month, the one that would coordinate your work across every project, machine, and inbox is the one you still assemble by hand.</p>
<p>The reason has little to do with the market lagging.
Almost every tool is built around something other than you: the project, or the session, and rarely the person.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/project-agent-person-centric-ai-coding-tools#the-autonomy-axis" aria-label="the-autonomy-axis permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="the-autonomy-axis"></span>Autonomous vs assistive AI coding agents: the axis everyone measures</h2>
<p>Almost every framework for AI coding tools measures the same thing: how autonomous is it?</p>
<p><a href="https://newsletter.pragmaticengineer.com/p/steve-yegge-on-ai-agents-and-the" rel="noopener" title="Link to https://newsletter.pragmaticengineer.com/p/steve-yegge-on-ai-agents-and-the" aria-label="Steve Yegge's eight levels" target="_blank">Steve Yegge's eight levels</a> run from no AI at all to a custom orchestrator.
Dan Shapiro's five go from <a href="https://www.danshapiro.com/blog/2026/01/the-five-levels-from-spicy-autocomplete-to-the-software-factory/" rel="noopener" title="Link to https://www.danshapiro.com/blog/2026/01/the-five-levels-from-spicy-autocomplete-to-the-software-factory/" aria-label="spicy autocomplete to the Dark Factory" target="_blank">spicy autocomplete to the Dark Factory</a>.
Sourcegraph and <a href="https://www.swarmia.com/blog/five-levels-ai-agent-autonomy/" rel="noopener" title="Link to https://www.swarmia.com/blog/five-levels-ai-agent-autonomy/" aria-label="Swarmia" target="_blank">Swarmia</a> each ship their own.
<a href="https://addyo.substack.com/p/agentic-autonomy-levels" rel="noopener" title="Link to https://addyo.substack.com/p/agentic-autonomy-levels" aria-label="Addy Osmani" target="_blank">Addy Osmani</a> recently split the ladder into two axes. But both, as I will come back to, are still about autonomy.</p>
<p>I am not going to re-teach the ladder here.
My <a class="post-link " title="The Missing Levels of AI-Assisted Development: From Agent Chaos to Orchestration" href="/missing-levels-ai-assisted-development">previous post</a> tried to fill in its missing rungs, and the ladder itself is real, useful, and thoroughly mapped.
It answers one question well: how much can I trust the tool to do on its own?</p>
<p>That is not the only question worth asking.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/project-agent-person-centric-ai-coding-tools#center-of-gravity" aria-label="center-of-gravity permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="center-of-gravity"></span>The axis nobody names: a tool's center of gravity</h2>
<p>Here is a second question the ladders never ask: what is the tool built around?</p>
<p>Not how autonomous it is, but what sits at the center: the thing every feature is quietly designed to serve.
I think of it as a tool's <strong>center of gravity</strong>, and three answers keep showing up.
Sorted that way, AI coding tools fall into three kinds: project-centric, agent-centric, and person-centric.</p>
<ul>
<li><strong>Project-centric</strong>: the unit is the repository and the issue. You hand over a task and an agent works autonomously toward a pull request.</li>
<li><strong>Agent-centric</strong>: the unit is the session. You live inside one tool's cockpit and run your agents from there.</li>
<li><strong>Person-centric</strong>: the unit is you. The tool coordinates your work across the repositories, trackers, and machines your name touches.</li>
</ul>
<p>Two clarifications first.</p>
<p>This is a different "agent-centric" than you may have met elsewhere.
It is not Sonar's Agent-Centric Development Cycle, a code-quality governance loop, and it is not UX's user-centered design.
Here it means one thing: the tool is built around the agent session as the place you live, and the cockpit is the product.</p>
<p>And these are centers of gravity, not boxes.
Real tools straddle.
GitHub's Copilot coding agent is a project-centric workhorse that also ships a personal dashboard; Cursor is an agent-centric cockpit that also runs background jobs.
I am naming what each tool leans toward, not sorting them into sealed compartments.
The mappings that follow are my own analytical lens, not how these vendors describe themselves.</p>
<p>One caveat on that word "missing": it is missing from how we usually talk about these tools, not from the market.
Autonomy gets all the frameworks; center of gravity gets almost none.
And the two are not the same measure in a different coat.
A dependency-bump bot that opens one small, pre-approved kind of pull request is deeply project-centric and barely autonomous.
Autonomy and center of gravity tend to travel together, but nothing forces them to.
Set autonomy aside and sort tools by their center of gravity, and the space takes on a shape of its own: three worlds side by side, with one corner still conspicuously empty.</p>
<span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/b4857747d9ead297525fdc7a7873cfbb/56785/three-centers-of-gravity.png" class="post-image__link" title="Isometric diagram of three platforms side by side: a project-centric factory floor of worker-drones turning issues into pull requests, an agent-centric cockpit pod with a single robot at a laptop, and an empty person-centric platform showing only the dashed blueprint of an unbuilt control tower"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/b4857747d9ead297525fdc7a7873cfbb/daa9b/three-centers-of-gravity.webp 418w,/static/b4857747d9ead297525fdc7a7873cfbb/80974/three-centers-of-gravity.webp 836w,/static/b4857747d9ead297525fdc7a7873cfbb/52a32/three-centers-of-gravity.webp 1672w" sizes="(min-width: 1672px) 1672px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 1672px) 1672px, 100vw" decoding="async" loading="lazy" data-src="/static/b4857747d9ead297525fdc7a7873cfbb/56785/three-centers-of-gravity.png" data-srcset="/static/b4857747d9ead297525fdc7a7873cfbb/d5310/three-centers-of-gravity.png 418w,/static/b4857747d9ead297525fdc7a7873cfbb/20be4/three-centers-of-gravity.png 836w,/static/b4857747d9ead297525fdc7a7873cfbb/56785/three-centers-of-gravity.png 1672w" alt="Isometric diagram of three platforms side by side: a project-centric factory floor of worker-drones turning issues into pull requests, an agent-centric cockpit pod with a single robot at a laptop, and an empty person-centric platform showing only the dashed blueprint of an unbuilt control tower" src="https://blog.marcnuri.com/static/b4857747d9ead297525fdc7a7873cfbb/56785/three-centers-of-gravity.png" srcset="https://blog.marcnuri.com/static/b4857747d9ead297525fdc7a7873cfbb/d5310/three-centers-of-gravity.png 418w,https://blog.marcnuri.com/static/b4857747d9ead297525fdc7a7873cfbb/20be4/three-centers-of-gravity.png 836w,https://blog.marcnuri.com/static/b4857747d9ead297525fdc7a7873cfbb/56785/three-centers-of-gravity.png 1672w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure>
<h2 class="heading"><a href="https://blog.marcnuri.com/project-agent-person-centric-ai-coding-tools#project-centric" aria-label="project-centric permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="project-centric"></span>Project-centric: the repository is the unit</h2>
<p>The most crowded, best-funded corner is project-centric.</p>
<p>The pattern is familiar: you describe a change as an issue, an agent picks it up in the cloud, and a pull request appears for you to review.
<a href="https://devin.ai/" rel="noopener" title="Link to https://devin.ai/" aria-label="Devin" target="_blank">Devin</a> is the flagship. Cognition has marketed fleets of them migrating repositories in parallel, and in 2025 it bought the Windsurf IDE, one project-centric vendor absorbing an agent-centric one.
<a href="https://jules.google/" rel="noopener" title="Link to https://jules.google/" aria-label="Google's Jules" target="_blank">Google's Jules</a>, <a href="https://openai.com/index/introducing-codex/" rel="noopener" title="Link to https://openai.com/index/introducing-codex/" aria-label="OpenAI's Codex cloud agent" target="_blank">OpenAI's Codex cloud agent</a>, and <a href="https://github.blog/news-insights/company-news/welcome-home-agents/" rel="noopener" title="Link to https://github.blog/news-insights/company-news/welcome-home-agents/" aria-label="GitHub's Copilot coding agent" target="_blank">GitHub's Copilot coding agent</a> all run the same play, with Factory's <a href="https://github.com/Factory-AI-Droids" rel="noopener" title="Link to https://github.com/Factory-AI-Droids" aria-label="Droids" target="_blank">Droids</a> and the open-source <a href="https://github.com/OpenHands/openhands" rel="noopener" title="Link to https://github.com/OpenHands/openhands" aria-label="OpenHands" target="_blank">OpenHands</a> rounding it out.</p>
<p>This is the vendor-built road to the top of the autonomy ladder, and it is genuinely good.
For bounded, well-specified, high-volume, team-owned work, made of dependency bumps, CVE patches, and mechanical migrations, it is the right answer; a human in the loop is friction, not safety.</p>
<p>The momentum is real enough that I see it inside <a class="tag-link " title="Red Hat" aria-label="my own company" href="/tag/red-hat">my own company</a>.
<a href="https://github.com/fullsend-ai/fullsend" rel="noopener" title="Link to https://github.com/fullsend-ai/fullsend" aria-label="fullsend" target="_blank">fullsend</a> is a public design-doc exploration of fully autonomous engineering: triage, implement, review, and merge, with a human pulled in only for the risky changes.
It is an effort from engineers I know at Red Hat, where I also work, an independent open-source exploration, not a Red Hat product or roadmap.
I mention it because this direction is not a fringe bet; it is where serious people are pointing.</p>
<p>Some argue this is the only corner that will matter.
OpenAI's Thibault Sottiaux calls scaffolding "coping, not scaling," and a lot of the money is behind it.
It is a bet, though, not a settled result: across teams, the bottleneck has moved to verification, and context, coordination, and oversight still gate what raw autonomy delivers.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/project-agent-person-centric-ai-coding-tools#agent-centric" aria-label="agent-centric permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="agent-centric"></span>Agent-centric: the session is the unit</h2>
<p>Start with what this corner gets right.</p>
<p>A single, opinionated cockpit is the best environment we have for focused work.
Claude Code in a terminal, Cursor in an editor, Sourcegraph's Amp: deep in one hard change, living inside a single well-designed tool beats hopping between five.
The local orchestrators built on top of this, like <a href="https://www.conductor.build/" rel="noopener" title="Link to https://www.conductor.build/" aria-label="Conductor" target="_blank">Conductor</a>, <a href="https://github.com/smtg-ai/claude-squad" rel="noopener" title="Link to https://github.com/smtg-ai/claude-squad" aria-label="Claude Squad" target="_blank">Claude Squad</a>, and Vibe Kanban, inherit the same virtue.
Each agent gets its own git worktree, and you conduct them all from one place: the one machine they run on.
The tell of the category is that coordination happens around the agent.</p>
<p>Cursor is multi-model (Claude, GPT, Gemini), so the lock-in was never really about the model.
It was about the cockpit.
The same gravity that makes a cockpit great also draws it toward a closed edge: the more a tool optimizes for the session you live in, the more it wants to be the only session you live in.
Read one way, you can watch that edge harden.
When the third-party client opencode tried to use Claude subscription credentials, it got back <code>This credential is only authorized for use with Claude Code and cannot be used for other API requests.</code> Anthropic had restricted those credentials to its own tool, pushing other clients toward a metered API key.
Cognition buying Windsurf is the same physics at a larger scale.</p>
<p>None of this is villainy.
It is what happens when the session is the center of gravity: the tool is the product, and the product wants you inside it.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/project-agent-person-centric-ai-coding-tools#person-centric" aria-label="person-centric permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="person-centric"></span>Person-centric: you are the unit</h2>
<p>This brings us to the empty corner.
I should be upfront here: I build a tool that lives in this corner, so if the case only works because it flatters my own project, it does not work.
I have tried to write the version that survives deleting it.</p>
<p>A person-centric AI coding tool takes you as the unit of coordination.
Not this repository, not that session, but you, and the whole spread of work that carries your name: repositories across several orgs, issues in two trackers, a review waiting in Slack, agents on a laptop and a workstation.
The center is the person, not the project or the platform.
Acting on your behalf is a trait that follows from that, not the axis itself.</p>
<p>Two properties make it categorically different.
It is <strong>cross-surface</strong>: it reaches past any single repo, IDE, or platform into your actual working day.
And it is <strong>rooted in your environment</strong>: it coordinates whatever agents you run, Claude Code or anything else, wherever you run them, not only the ones a vendor hosts inside its walls.
It is the corner the human-as-operator camp keeps pointing at, from Willison's "agentic engineering" to Swarmia's caution that higher autonomy is not always better, usually with no developer-grade tool to name.</p>
<p>So, to put the claim in a way you can push back on: person-centric coding tools are not unserved, but they are badly underserved.
Consumer assistants already act on a person's behalf across their messaging apps, and "AI chief of staff" tools do it for knowledge work, but none of them touches code, repositories, or CI.
The closest thing in our world is GitHub's Agent HQ, whose mission-control view already gathers agents from several vendors into one pane.
But it orchestrates only the agents GitHub hosts, on GitHub's own surfaces.
It does not reach the sessions you run on your own hardware, or the work that spills into your inbox and your other trackers.
Others feel the same pull.
Cursor gathers its own agents into one window, and a few small cross-brand tools route agent approvals to your phone; the first is a single vendor's garden, and the second is a thin slice of the job.
As of July 2026 I still cannot find a professional-grade tool that is vendor-neutral, spans your projects and surfaces, keeps you as the operator, and coordinates the actual work rather than one seam of it.
A few of the tools inching toward that corner are not mine, and I would genuinely like to be wrong here.
If you know one that already qualifies, point me to it and I will add it.</p>
<p>That empty corner is the shape I am building toward with <a href="https://github.com/manusa/ai-beacon" rel="noopener" title="Link to https://github.com/manusa/ai-beacon" aria-label="ai-beacon" target="_blank">ai-beacon</a>: the agents on all your machines in one web view you can reach from anywhere, Claude Code and opencode alike.
It grew out of the <a class="post-link " title="AI Coding Agent Dashboard: Orchestrating Claude Code Across Devices" href="/ai-coding-agent-dashboard">coding-agent dashboard I wrote about earlier this year</a>, and it is early and experimental.
The category matters more than my attempt at it.</p>
<p>The plumbing for this corner is half-built.
There is a standard for each seam: <a class="post-link " title="Introduction to the Model Context Protocol (MCP): The Future of AI Integration" href="/model-context-protocol-mcp-introduction">Model Context Protocol (MCP)</a> between agents and tools, <a class="post-link " title="Agent Client Protocol (ACP): The LSP for AI Coding Agents Explained" href="/agent-client-protocol-acp-introduction">Agent Client Protocol (ACP)</a> between editors and agents, A2A between agents, AG-UI between agents and interfaces.
What none of them provides alone is the cross-context layer that would tie your work together above all of them.
That layer is missing precisely because no vendor is incentivized to center it on you.</p>
<p>The person-centric approach has a real weakness of its own.
It lives at the mercy of the very boundaries it crosses.
A control layer that rides on top of every vendor inherits none of their guardrails: no CI, no review gates.
And any vendor can starve it with a credential restriction, the closed edge from before.
That is not a reason the category should not exist.
It is the reason it is hard.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/project-agent-person-centric-ai-coding-tools#prior-art" aria-label="prior-art permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="prior-art"></span>What is new here, and what isn't</h2>
<p>Let me be careful about what I am and am not claiming, because the prior art deserves credit.
The project-versus-agent distinction is not new; it relabels things people already say.
<a href="https://openhands.dev/blog/20251202-agents-in-the-outer-loop" rel="noopener" title="Link to https://openhands.dev/blog/20251202-agents-in-the-outer-loop" aria-label="OpenHands has written about" target="_blank">OpenHands has written about</a> the inner loop versus the outer loop, accelerating the individual versus handling whole tasks over Slack and Jira, which maps almost exactly onto agent-centric versus project-centric, though OpenHands straddles both.
Osmani's two-axis model splits autonomy into agency and orchestration, but his second axis is how many agents you run, which is still a question about autonomy.</p>
<p>What is actually new is small but load-bearing.
It is choosing center of gravity, what the tool is built around, as the axis, instead of the degree of autonomy, and then naming the person-centric category, at professional developer grade, as the one that is missing.
Neither Osmani's orchestration axis nor OpenHands' outer loop leaves a seat for the person.
That seat is the whole point.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/project-agent-person-centric-ai-coding-tools#composing" aria-label="composing permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="composing"></span>How to choose between project-, agent-, and person-centric tools</h2>
<p>None of this is a fight to the death.
The future I expect is a composition, with a clear boundary between the parts: a control tower over a factory floor.
The factory floor is project-centric: many autonomous agents turning well-specified issues into pull requests while you sleep.
The control tower is person-centric: one person's single pane of glass over all of it, across projects, surfaces, and machines.
They are not rivals; they are different altitudes of the same operation.</p>
<p>That leaves a decision rule I can use day to day.</p>
<ul>
<li>When the work is depth on a single change, reach for the cockpit (agent-centric).</li>
<li>When it is a specced unit you can hand off and walk away from, reach for the factory (project-centric).</li>
<li>When it spans projects, surfaces, or machines and needs your judgment routed across all of them, reach for the control tower (person-centric).</li>
</ul>
<p>The uncomfortable part is that the third tool is the one the market is least likely to hand you.
My previous post said you would probably have to build your own orchestrator.
I now think the deeper reason is structural.
Every vendor's center of gravity is the project or the platform, because that is where the business is.
Almost none of it is you.
AI coding tools differ less by how autonomous they are than by what they are built around, and the human is the one center nobody has quite built for yet.</p>
<p>If you are somewhere on this map, I would like to know which corner.
I am collecting notes in the <a href="https://github.com/manusa/ai-beacon/discussions" rel="noopener" title="Link to https://github.com/manusa/ai-beacon/discussions" aria-label="ai-beacon discussions" target="_blank">ai-beacon discussions</a>, the same place as last time.
Thanks, too, to my colleague Václav Vančura, whose UX thinking helped sharpen how I see all of this.
The more corners we compare, the sharper the map gets.</p>
  ]]></content:encoded>
            <category>Artificial Intelligence</category>
            <enclosure url="https://blog.marcnuri.com/static/060b7ade44a19e1b4045f5290e6815e9/818f3/project-agent-person-centric-ai-coding-tools.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Fabric8 Kubernetes Client 7.8 is now available!]]></title>
            <link>https://blog.marcnuri.com/fabric8-kubernetes-client-7-8</link>
            <guid isPermaLink="false">https://blog.marcnuri.com/fabric8-kubernetes-client-7-8</guid>
            <pubDate>Mon, 29 Jun 2026 18:00:00 GMT</pubDate>
            <description><![CDATA[Fabric8 Kubernetes Client 7.8 is available! Check out the major changes and learn how you can contribute.]]></description>
            <content:encoded><![CDATA[
    <div><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-8">Original post</a></div>
    <p>On behalf of the <a class="post-link " title="Kubernetes Client for Java: Fabric8 introduction" href="/kubernetes-client-java-fabric8-introduction">Fabric8</a>
team and everyone who has contributed, I'm happy to announce that the Fabric8 Kubernetes Client <code>7.8.0</code> has been
<a href="https://github.com/fabric8io/kubernetes-client/releases/tag/v7.8.0" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/releases/tag/v7.8.0" aria-label="released" target="_blank">released</a> and is now available from
<a href="https://repo1.maven.org/maven2/io/fabric8/kubernetes-client/7.8.0/" rel="noopener" title="Link to https://repo1.maven.org/maven2/io/fabric8/kubernetes-client/7.8.0/" aria-label="Maven Central" target="_blank">Maven Central</a> 🎉.</p>
<p>This marks the eighth minor release of the Fabric8 Kubernetes Client 7, bringing new features, bug fixes, and improvements while keeping the breaking changes minimal.</p>
<p>Thanks to all of you who have contributed with issue reports, pull requests, feedback, and spreading the word with blogs, videos, comments, and so on.
We really appreciate your help, keep it up!</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-8#whats-new" aria-label="whats-new permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="whats-new"></span>What's new?</h2>
<p>Without further ado, let's have a look at the most significant updates:</p>
<ul>
<li><a class="post-link " title="Link to the shard selectors section" href="/fabric8-kubernetes-client-7-8#shard-selectors">Shard selectors for list, watch and informers</a></li>
<li><a class="post-link " title="Link to the TLS warm-up section" href="/fabric8-kubernetes-client-7-8#tls-warmup">Opt-in TLS warm-up for the Vert.x HTTP client</a></li>
<li><a class="post-link " title="Link to the CRD improvements section" href="/fabric8-kubernetes-client-7-8#crd-improvements">CRD generator: class-level descriptions via @JsonClassDescription</a></li>
<li>🐛 Many other bug fixes and minor improvements</li>
</ul>
<p>You can find the full changelog for this version in our GitHub <a href="https://github.com/fabric8io/kubernetes-client/releases/tag/v7.8.0" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/releases/tag/v7.8.0" aria-label="release page" target="_blank">release page</a>.</p>
<h3 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-8#shard-selectors" aria-label="shard-selectors permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="shard-selectors"></span>Shard selectors for list, watch and informers</h3>
<p>List and watch operations, including informers, now support shard selectors through the new <code>withShardSelector</code> method.
This lets you partition a large set of resources across multiple consumers, so each instance only lists and watches the slice it is responsible for.
It's a handy building block when you need to scale controllers or operators horizontally and distribute the watch load, instead of having every instance receive every event.</p>
<p>A shard selector is a CEL expression, typically a <code>shardRange(...)</code> over the resource UID, that you attach to any list, watch, inform or delete operation:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-java" style="white-space:pre"><span style="color:#c26230">try</span><span> (KubernetesClient client = </span><span style="color:#c26230">new</span><span> KubernetesClientBuilder().build()) {
</span><span>  </span><span style="color:#bc9458;font-style:italic">// This instance only sees the first half of the UID space</span><span>
</span><span>  String shard = </span><span style="color:#a5c261">"shardRange(object.metadata.uid, '0x0000000000000000', '0x8000000000000000')"</span><span>;
</span>  client.configMaps()
<!-- -->    .withShardSelector(shard)
<!-- -->    .inform(handler);
<!-- -->}</code></pre></div>
<p>Note that this relies on the server-side sharded list and watch support, an alpha Kubernetes feature, so the corresponding feature gate must be enabled on your cluster.
The initial support was rounded out with a few follow-ups to smooth out the rough edges.</p>
<h3 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-8#tls-warmup" aria-label="tls-warmup permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="tls-warmup"></span>Opt-in TLS warm-up for the Vert.x HTTP client</h3>
<p>The Vert.x HTTP client factories (<code>Vertx5HttpClientFactory</code> and <code>VertxHttpClientFactory</code>) now expose a <code>setTlsWarmup(TlsWarmup)</code> method with three modes: <code>OFF</code>, <code>CONTEXT</code> (the default, with unchanged behaviour) and <code>FULL</code>.</p>
<p>When set to <code>FULL</code>, the client performs a synchronous, once-per-JVM, throwaway loopback TLS handshake off the event loop when it's built.
This pays the one-time cost of JDK/Netty TLS class loading up front, so the first real connection no longer blocks the event loop on it.
It's specifically aimed at users running on cold or hard CPU-throttled JVMs who were hitting first-connection stalls or timeouts.</p>
<p>You opt in by configuring the warm-up mode on the HTTP client factory and passing it to the builder:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-java" style="white-space:pre"><span>Vertx5HttpClientFactory factory = </span><span style="color:#c26230">new</span><span> Vertx5HttpClientFactory();
</span>factory.setTlsWarmup(TlsWarmup.FULL);
<span></span><span style="color:#c26230">try</span><span> (KubernetesClient client = </span><span style="color:#c26230">new</span><span> KubernetesClientBuilder()
</span>    .withHttpClientFactory(factory)
<!-- -->    .build()) {
<span>  </span><span style="color:#bc9458;font-style:italic">// The first TLS connection no longer pays the class-loading cost on the event loop</span><span>
</span>}</code></pre></div>
<p>The default behaviour (<code>CONTEXT</code>) is unchanged, so you only opt in if you need it.</p>
<h3 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-8#crd-improvements" aria-label="crd-improvements permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="crd-improvements"></span>CRD generator: class-level descriptions via @JsonClassDescription</h3>
<p>The CRD generator now honors Jackson's <code>@JsonClassDescription</code> annotation, allowing you to add descriptions to whole classes in the generated CRD schema.
This complements the existing field-level description support, so the documentation embedded in your CRDs can be more complete and closer to your source model.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-8#using-this-release" aria-label="using-this-release permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="using-this-release"></span>Using this release</h2>
<p>If your project is based on Maven, you just need to add the Fabric8 Kubernetes Client to your Maven dependencies:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-xml" style="white-space:pre"><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">dependency</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>io.fabric8</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>kubernetes-client</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">version</span><span style="color:#e8bf6a">&gt;</span><span>7.8.0</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">version</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span></span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">dependency</span><span style="color:#e8bf6a">&gt;</span></code></pre></div>
<p>If your project is based on Gradle, you just need to add the Fabric8 Kubernetes Client to your Gradle dependencies:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-groovy" style="white-space:pre"><span>dependencies {
</span><span>  api </span><span style="color:#a5c261">"io.fabric8:kubernetes-client:7.8.0"</span><span>
</span>}</code></pre></div>
<p>Once your project is ready, you can create a new instance of the client to perform operations.
In the following code snippet, I show you how to instantiate the client and retrieve a list of Pods:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-java" style="white-space:pre"><span style="color:#c26230">try</span><span> (KubernetesClient client = </span><span style="color:#c26230">new</span><span> KubernetesClientBuilder().build()) {
</span>  client.pods().list().getItems().forEach(p -&gt; System.out.println(p.getMetadata().getName()));
<!-- -->}</code></pre></div>
<h2 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-8#how-can-you-help" aria-label="how-can-you-help permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="how-can-you-help"></span>How can you help?</h2>
<p>If you're interested in helping out and are a first-time contributor, check out
the <a href="https://github.com/fabric8io/kubernetes-client/labels/good%20first%20issue" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/labels/good%20first%20issue" aria-label="&quot;good first issue&quot;" target="_blank">"good first issue"</a> tag in the issue repository.
We've tagged extremely easy issues so that you can get started contributing to Open Source.</p>
<p>We're also excited to read articles and posts mentioning our project and sharing the user experience.
Giving a star to the project, and spreading the word in general, helps us reach more users and broaden the
feedback. Feedback is the only way to improve.</p>
<p><a href="https://github.com/fabric8io/kubernetes-client" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client" aria-label="Project Page" target="_blank">Project Page</a> |
<a href="https://github.com/fabric8io/kubernetes-client/issues" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/issues" aria-label="Issues" target="_blank">Issues</a> |
<a href="https://github.com/fabric8io/kubernetes-client/discussions" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/discussions" aria-label="Discussions" target="_blank">Discussions</a> |
<a href="https://gitter.im/fabric8io/kubernetes-client" rel="noopener" title="Link to https://gitter.im/fabric8io/kubernetes-client" aria-label="Gitter" target="_blank">Gitter</a> |
<a href="https://stackoverflow.com/questions/tagged/fabric8" rel="noopener" title="Link to https://stackoverflow.com/questions/tagged/fabric8" aria-label="Stack Overflow" target="_blank">Stack Overflow</a></p>
<span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png" class="post-image__link" title="The logo of Fabric8 Kubernetes Client"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/eb11f2059915fc28b71c76eff27dc487/a66c2/fabric8-logo.webp 269w,/static/eb11f2059915fc28b71c76eff27dc487/3860d/fabric8-logo.webp 539w,/static/eb11f2059915fc28b71c76eff27dc487/418ed/fabric8-logo.webp 1077w" sizes="(min-width: 1077px) 1077px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 1077px) 1077px, 100vw" decoding="async" loading="lazy" data-src="/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png" data-srcset="/static/eb11f2059915fc28b71c76eff27dc487/53e69/fabric8-logo.png 269w,/static/eb11f2059915fc28b71c76eff27dc487/f0c2e/fabric8-logo.png 539w,/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png 1077w" alt="The logo of Fabric8 Kubernetes Client" src="https://blog.marcnuri.com/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png" srcset="https://blog.marcnuri.com/static/eb11f2059915fc28b71c76eff27dc487/53e69/fabric8-logo.png 269w,https://blog.marcnuri.com/static/eb11f2059915fc28b71c76eff27dc487/f0c2e/fabric8-logo.png 539w,https://blog.marcnuri.com/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png 1077w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure>
  ]]></content:encoded>
            <category>Cloud Native</category>
            <enclosure url="https://blog.marcnuri.com/static/b9dbe50d753954e05cf28f5e3aa1c88a/baaed/fabric8.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Fabric8 Kubernetes Client 7.7 is now available!]]></title>
            <link>https://blog.marcnuri.com/fabric8-kubernetes-client-7-7</link>
            <guid isPermaLink="false">https://blog.marcnuri.com/fabric8-kubernetes-client-7-7</guid>
            <pubDate>Tue, 12 May 2026 08:00:00 GMT</pubDate>
            <description><![CDATA[Fabric8 Kubernetes Client 7.7 is available! Check out the major changes and learn how you can contribute.]]></description>
            <content:encoded><![CDATA[
    <div><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-7">Original post</a></div>
    <div class="admonition admonition__note"><p class="admonition-title"><i class="admonition-title-icon fa-solid fa-circle-info"></i>Note</p><div class="admonition-content">A newer version of Fabric8 Kubernetes Client is available, jump to <a class="post-link " title="Fabric8 Kubernetes Client 7.8 is now available!" href="/fabric8-kubernetes-client-7-8">Fabric8 Kubernetes Client 7.8 announcement</a>.</div></div>
<p>On behalf of the <a class="post-link " title="Kubernetes Client for Java: Fabric8 introduction" href="/kubernetes-client-java-fabric8-introduction">Fabric8</a>
team and everyone who has contributed, I'm happy to announce that the Fabric8 Kubernetes Client <code>7.7.0</code> has been
<a href="https://github.com/fabric8io/kubernetes-client/releases/tag/v7.7.0" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/releases/tag/v7.7.0" aria-label="released" target="_blank">released</a> and is now available from
<a href="https://repo1.maven.org/maven2/io/fabric8/kubernetes-client/7.7.0/" rel="noopener" title="Link to https://repo1.maven.org/maven2/io/fabric8/kubernetes-client/7.7.0/" aria-label="Maven Central" target="_blank">Maven Central</a> 🎉.</p>
<p>This marks the seventh minor release of the Fabric8 Kubernetes Client 7, bringing new features, bug fixes, and improvements while keeping the breaking changes minimal.</p>
<p>Thanks to all of you who have contributed with issue reports, pull requests, feedback, and spreading the word with blogs, videos, comments, and so on.
We really appreciate your help, keep it up!</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-7#whats-new" aria-label="whats-new permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="whats-new"></span>What's new?</h2>
<p>Without further ado, let's have a look at the most significant updates:</p>
<ul>
<li><a class="post-link " title="Link to the Kubernetes 1.36 section" href="/fabric8-kubernetes-client-7-7#kubernetes-136">Kubernetes 1.36 support (Haru)</a></li>
<li><a class="post-link " title="Link to the content negotiation section" href="/fabric8-kubernetes-client-7-7#content-negotiation">Server-side content negotiation (Table and PartialObjectMetadata)</a></li>
<li><a class="post-link " title="Link to the API ergonomics section" href="/fabric8-kubernetes-client-7-7#api-ergonomics">API ergonomics: isSameResource, subresource() and addOwnerReference</a></li>
<li><a class="post-link " title="Link to the stability fixes section" href="/fabric8-kubernetes-client-7-7#stability-fixes">Reliability fixes in leader election, informers and exec WebSockets</a></li>
<li>🐛 Many other bug fixes and minor improvements</li>
</ul>
<p>You can find the full changelog for this version in our GitHub <a href="https://github.com/fabric8io/kubernetes-client/releases/tag/v7.7.0" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/releases/tag/v7.7.0" aria-label="release page" target="_blank">release page</a>.</p>
<h3 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-7#kubernetes-136" aria-label="kubernetes-136 permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="kubernetes-136"></span>Kubernetes 1.36 support (Haru)</h3>
<p>This release adds support for Kubernetes v1.36 (ハル / Haru), ensuring you have access to the latest API resources and CRDs.
Several upstream dependencies have also been bumped, including <code>k8s.io/apimachinery</code> to 0.36.0, Istio client-go to 1.29.1, Knative to 0.49, Gateway API to 1.5.0 and the Prometheus Operator model to 0.91.0.</p>
<p>Note that the <code>scheduling.k8s.io/v1alpha1</code> workload-scheduling model classes (<code>Workload</code>, <code>WorkloadList</code>, <code>PodGroup</code>, etc.) have been removed following the upstream KEP-5832 rearchitecture.</p>
<div class="admonition admonition__note"><p class="admonition-title"><i class="admonition-title-icon fa-solid fa-circle-info"></i>Note</p><div class="admonition-content">
<p>Please note that you can still access newer Kubernetes clusters with <strong>older</strong> versions of the Fabric8 client.</p>
<p>The client provides a GenericKubernetesResources class to interact with resources that are not yet supported by the client.
We do recommend to always use the <strong>latest</strong> version of the client to benefit from the latest features and bug fixes, but it's not mandatory.</p>
</div></div>
<h3 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-7#content-negotiation" aria-label="content-negotiation permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="content-negotiation"></span>Server-side content negotiation (Table and PartialObjectMetadata)</h3>
<p>The client now supports server-side content negotiation, allowing you to request <code>Table</code> and <code>PartialObjectMetadata</code> responses from the API server.
This is the same mechanism <code>kubectl get</code> uses to render its tabular output, and <code>PartialObjectMetadata</code> is particularly useful when you need to list large numbers of resources but only care about their metadata, drastically reducing the payload size and parsing cost.</p>
<h3 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-7#api-ergonomics" aria-label="api-ergonomics permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="api-ergonomics"></span>API ergonomics: isSameResource, subresource() and addOwnerReference</h3>
<p>A few targeted API additions make day-to-day code a bit nicer to write:</p>
<ul>
<li>A new <code>HasMetadata#isSameResource</code> helper lets you test whether two <code>HasMetadata</code> instances point to the same logical cluster resource, with an optional strict mode that also requires matching <code>kind</code> and <code>resourceVersion</code>.</li>
<li>The generic <code>subresource()</code> method has been expanded with broader support and enhanced documentation and examples, making it easier to interact with custom or less common subresources.</li>
<li><code>addOwnerReference</code> now has a variant that lets you set the <code>controller</code> and <code>blockOwnerDeletion</code> fields in a single call, which is handy when writing operators.</li>
<li>A new <code>ResourceEventHandler#onList</code> method has been added (deprecating <code>onNothing</code>), giving informer consumers a more meaningful hook to react to the initial list event.</li>
</ul>
<h3 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-7#stability-fixes" aria-label="stability-fixes permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="stability-fixes"></span>Reliability fixes in leader election, informers and exec WebSockets</h3>
<p>This release includes a notable batch of stability fixes that are worth calling out:</p>
<ul>
<li><strong>Leader election</strong>: fixed an improper callback timing that could lead to a dual-leader situation.</li>
<li><strong>Informers</strong>: <code>SerialExecutor.execute()</code> now returns immediately after shutdown, fixing a post-stop NPE race in <code>SharedProcessor.distribute(...)</code>, and ephemeral index entries are now properly removed from informer caches.</li>
<li><strong>Exec WebSockets</strong>: handshake failures with a non-null upgrade response no longer throw <code>IllegalStateException</code> synchronously, exit codes are no longer overwritten by peer-close exceptions, and pending stdout/stderr writes are flushed before the exit signal is delivered.</li>
<li><strong>MockWebServer</strong>: <code>shutdown()</code> is now idempotent and avoids the <code>RejectedExecutionException</code> that could appear in JUnit <code>@Nested</code> <code>afterAll</code> cascades, and chunked responses no longer send <code>Content-Length</code> together with <code>Transfer-Encoding</code>.</li>
</ul>
<h2 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-7#using-this-release" aria-label="using-this-release permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="using-this-release"></span>Using this release</h2>
<p>If your project is based on Maven, you just need to add the Fabric8 Kubernetes Client to your Maven dependencies:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-xml" style="white-space:pre"><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">dependency</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>io.fabric8</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>kubernetes-client</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">version</span><span style="color:#e8bf6a">&gt;</span><span>7.7.0</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">version</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span></span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">dependency</span><span style="color:#e8bf6a">&gt;</span></code></pre></div>
<p>If your project is based on Gradle, you just need to add the Fabric8 Kubernetes Client to your Gradle dependencies:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-groovy" style="white-space:pre"><span>dependencies {
</span><span>  api </span><span style="color:#a5c261">"io.fabric8:kubernetes-client:7.7.0"</span><span>
</span>}</code></pre></div>
<p>Once your project is ready, you can create a new instance of the client to perform operations.
In the following code snippet, I show you how to instantiate the client and retrieve a list of Pods:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-java" style="white-space:pre"><span style="color:#c26230">try</span><span> (KubernetesClient client = </span><span style="color:#c26230">new</span><span> KubernetesClientBuilder().build()) {
</span>  client.pods().list().getItems().forEach(p -&gt; System.out.println(p.getMetadata().getName()));
<!-- -->}</code></pre></div>
<h2 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-7#how-can-you-help" aria-label="how-can-you-help permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="how-can-you-help"></span>How can you help?</h2>
<p>If you're interested in helping out and are a first-time contributor, check out
the <a href="https://github.com/fabric8io/kubernetes-client/labels/good%20first%20issue" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/labels/good%20first%20issue" aria-label="&quot;good first issue&quot;" target="_blank">"good first issue"</a> tag in the issue repository.
We've tagged extremely easy issues so that you can get started contributing to Open Source.</p>
<p>We're also excited to read articles and posts mentioning our project and sharing the user experience.
Giving a star to the project, and spreading the word in general, helps us reach more users and broaden the
feedback. Feedback is the only way to improve.</p>
<p><a href="https://github.com/fabric8io/kubernetes-client" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client" aria-label="Project Page" target="_blank">Project Page</a> |
<a href="https://github.com/fabric8io/kubernetes-client/issues" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/issues" aria-label="Issues" target="_blank">Issues</a> |
<a href="https://github.com/fabric8io/kubernetes-client/discussions" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/discussions" aria-label="Discussions" target="_blank">Discussions</a> |
<a href="https://gitter.im/fabric8io/kubernetes-client" rel="noopener" title="Link to https://gitter.im/fabric8io/kubernetes-client" aria-label="Gitter" target="_blank">Gitter</a> |
<a href="https://stackoverflow.com/questions/tagged/fabric8" rel="noopener" title="Link to https://stackoverflow.com/questions/tagged/fabric8" aria-label="Stack Overflow" target="_blank">Stack Overflow</a></p>
<span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png" class="post-image__link" title="The logo of Fabric8 Kubernetes Client"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/eb11f2059915fc28b71c76eff27dc487/a66c2/fabric8-logo.webp 269w,/static/eb11f2059915fc28b71c76eff27dc487/3860d/fabric8-logo.webp 539w,/static/eb11f2059915fc28b71c76eff27dc487/418ed/fabric8-logo.webp 1077w" sizes="(min-width: 1077px) 1077px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 1077px) 1077px, 100vw" decoding="async" loading="lazy" data-src="/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png" data-srcset="/static/eb11f2059915fc28b71c76eff27dc487/53e69/fabric8-logo.png 269w,/static/eb11f2059915fc28b71c76eff27dc487/f0c2e/fabric8-logo.png 539w,/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png 1077w" alt="The logo of Fabric8 Kubernetes Client" src="https://blog.marcnuri.com/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png" srcset="https://blog.marcnuri.com/static/eb11f2059915fc28b71c76eff27dc487/53e69/fabric8-logo.png 269w,https://blog.marcnuri.com/static/eb11f2059915fc28b71c76eff27dc487/f0c2e/fabric8-logo.png 539w,https://blog.marcnuri.com/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png 1077w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure>
  ]]></content:encoded>
            <category>Cloud Native</category>
            <enclosure url="https://blog.marcnuri.com/static/b9dbe50d753954e05cf28f5e3aa1c88a/baaed/fabric8.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Superpowers: The Claude Code Skills Framework Shipped as Markdown]]></title>
            <link>https://blog.marcnuri.com/superpowers-claude-code-skills-framework</link>
            <guid isPermaLink="false">https://blog.marcnuri.com/superpowers-claude-code-skills-framework</guid>
            <pubDate>Fri, 01 May 2026 18:00:00 GMT</pubDate>
            <description><![CDATA[Superpowers is the Claude Code skills framework that ships TDD, planning, debugging, and verification as markdown across six AI coding agents.]]></description>
            <content:encoded><![CDATA[
    <div><a href="https://blog.marcnuri.com/superpowers-claude-code-skills-framework">Original post</a></div>
    <h2 class="heading"><a href="https://blog.marcnuri.com/superpowers-claude-code-skills-framework#introduction" aria-label="introduction permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="introduction"></span>Superpowers in One Paragraph</h2>
<p><a href="https://github.com/obra/superpowers" rel="noopener" title="Link to https://github.com/obra/superpowers" aria-label="Superpowers" target="_blank">Superpowers</a> (from <code>obra/superpowers</code>) is the Claude Code plugin, and multi-host agentic skills framework, that ships an opinionated engineering culture as a folder of markdown files.
The same folder works across <a href="https://www.anthropic.com/claude-code" rel="noopener" title="Link to https://www.anthropic.com/claude-code" aria-label="Claude Code" target="_blank">Claude Code</a>, <a href="https://cursor.com" rel="noopener" title="Link to https://cursor.com" aria-label="Cursor" target="_blank">Cursor</a>, <a href="https://openai.com/codex/" rel="noopener" title="Link to https://openai.com/codex/" aria-label="OpenAI Codex" target="_blank">OpenAI Codex</a>, <a href="https://docs.github.com/copilot/github-copilot-cli" rel="noopener" title="Link to https://docs.github.com/copilot/github-copilot-cli" aria-label="GitHub Copilot CLI" target="_blank">GitHub Copilot CLI</a>, <a href="https://github.com/google-gemini/gemini-cli" rel="noopener" title="Link to https://github.com/google-gemini/gemini-cli" aria-label="Gemini CLI" target="_blank">Gemini CLI</a>, and <a href="https://opencode.ai" rel="noopener" title="Link to https://opencode.ai" aria-label="OpenCode" target="_blank">OpenCode</a>.
No fine-tuned model, no proprietary SDK, no agent platform.
Just a <code>skills/</code>, fourteen <code>SKILL.md</code> files, and a session hook that tells the <a class="tag-link " title="AI Agent" aria-label="agent" href="/tag/ai-agent">agent</a> to read them before doing anything else.</p>
<p>Created by <a href="https://blog.fsck.com" rel="noopener" title="Link to https://blog.fsck.com" aria-label="Jesse Vincent" target="_blank">Jesse Vincent</a> in <a href="https://blog.fsck.com/2025/10/09/superpowers/" rel="noopener" title="Link to https://blog.fsck.com/2025/10/09/superpowers/" aria-label="October 2025" target="_blank">October 2025</a>, the project has accumulated more than 174,000 GitHub stars in seven months and shipped as an <a href="https://claude.com/plugins/superpowers" rel="noopener" title="Link to https://claude.com/plugins/superpowers" aria-label="Anthropic marketplace plugin" target="_blank">Anthropic marketplace plugin</a> in early 2026.
The popularity is not what makes it interesting.
The real bet is that what AI coding agents are missing is not capability but <em>discipline</em>, and that discipline can be distributed as plain text.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/superpowers-claude-code-skills-framework#why-an-agent-needs-this" aria-label="why-an-agent-needs-this permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="why-an-agent-needs-this"></span>Why an Agent Needs This</h2>
<p>A modern coding agent is impressively capable.
It also fails in remarkably consistent ways:</p>
<ul>
<li>It writes the code first, then the test, then forgets to run it.</li>
<li>It declares the bug fixed because the failing example <em>now seems to work</em>.</li>
<li>It patches the symptom and never investigates the root cause.</li>
<li>It claims the build passes without ever running the build.</li>
<li>And when called out, it knows perfectly well it should have done otherwise.</li>
</ul>
<p>These are not knowledge gaps. The agent already has the right concepts in its training. Ask it to lecture you on <a class="tag-link " title="Test-Driven Development (TDD)" aria-label="TDD" href="/tag/tdd">TDD</a> and it will.
What it lacks is a mechanism that holds it to those concepts when shortcuts are easier.
That is the hole Superpowers tries to fill.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/superpowers-claude-code-skills-framework#what-superpowers-is" aria-label="what-superpowers-is permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="what-superpowers-is"></span>What the Superpowers Skills Framework Is</h2>
<p>Superpowers is unusually small.
It consists of three pieces:</p>
<ol>
<li>A directory of fourteen <em>skills</em>, each a single <code>SKILL.md</code> file with YAML frontmatter and a few hundred words of instructions for one engineering practice: TDD, debugging, brainstorming, writing plans, code review, verification before completion.</li>
<li>A session-start hook that injects a short bootstrap document, reportedly under two thousand tokens, telling the agent to invoke a relevant skill before it does anything else.</li>
<li>Per-host plugin manifests that let each agent host discover the same skills in its own way.</li>
</ol>
<p>That is the entire payload.
Engineering culture, distributed as a <a class="tag-link " title="Git" aria-label="git" href="/tag/git">git</a> repository you can skim in an hour.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/superpowers-claude-code-skills-framework#methodology-cycle" aria-label="methodology-cycle permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="methodology-cycle"></span>The Methodology in One Cycle</h2>
<p>A typical task moves through brainstorming, a <a href="https://git-scm.com/docs/git-worktree" rel="noopener" title="Link to https://git-scm.com/docs/git-worktree" aria-label="git worktree" target="_blank">git worktree</a>, a written plan, subagent-driven implementation with TDD inside, a code-review pass from a fresh agent, and a finishing step that closes out the branch.
Each phase has its own skill, and the next phase will not begin until the previous one is done.
The <a class="tag-link " title="Workflow" aria-label="workflow" href="/tag/workflow">workflow</a> is the skills, in order.</p>
<p>This is the discipline layer for <em>one task at a time</em>.
What gets layered on top once you are running many such cycles in parallel is a separate climb, which I outlined in <a class="post-link " title="The Missing Levels of AI-Assisted Development: From Agent Chaos to Orchestration" href="/missing-levels-ai-assisted-development">the missing levels of AI-assisted development</a>.</p>
<span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/8808e706bb58e3fd12e0d38ac1022e9c/ae105/methodology-cycle.png" class="post-image__link" title="Superpowers Claude Code skills framework lifecycle diagram: brainstorm, worktree, plan, subagent development, TDD, review, finish, arranged as a clockwise loop of glowing 3D nodes on a dark background"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/8808e706bb58e3fd12e0d38ac1022e9c/ba1b2/methodology-cycle.webp 314w,/static/8808e706bb58e3fd12e0d38ac1022e9c/b62e7/methodology-cycle.webp 627w,/static/8808e706bb58e3fd12e0d38ac1022e9c/29f96/methodology-cycle.webp 1254w" sizes="(min-width: 1254px) 1254px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 1254px) 1254px, 100vw" decoding="async" loading="lazy" data-src="/static/8808e706bb58e3fd12e0d38ac1022e9c/ae105/methodology-cycle.png" data-srcset="/static/8808e706bb58e3fd12e0d38ac1022e9c/548ac/methodology-cycle.png 314w,/static/8808e706bb58e3fd12e0d38ac1022e9c/b076a/methodology-cycle.png 627w,/static/8808e706bb58e3fd12e0d38ac1022e9c/ae105/methodology-cycle.png 1254w" alt="Superpowers Claude Code skills framework lifecycle diagram: brainstorm, worktree, plan, subagent development, TDD, review, finish, arranged as a clockwise loop of glowing 3D nodes on a dark background" src="https://blog.marcnuri.com/static/8808e706bb58e3fd12e0d38ac1022e9c/ae105/methodology-cycle.png" srcset="https://blog.marcnuri.com/static/8808e706bb58e3fd12e0d38ac1022e9c/548ac/methodology-cycle.png 314w,https://blog.marcnuri.com/static/8808e706bb58e3fd12e0d38ac1022e9c/b076a/methodology-cycle.png 627w,https://blog.marcnuri.com/static/8808e706bb58e3fd12e0d38ac1022e9c/ae105/methodology-cycle.png 1254w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure>
<h2 class="heading"><a href="https://blog.marcnuri.com/superpowers-claude-code-skills-framework#representative-skills" aria-label="representative-skills permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="representative-skills"></span>A Tour of Representative Skills</h2>
<p>The repository ships fourteen skills.
Six of them carry most of the weight:</p>
<ul>
<li><strong><code>brainstorming</code></strong>: refuses to write code until the agent has asked clarifying questions and proposed a design you can accept.</li>
<li><strong><code>writing-plans</code></strong>: breaks a feature into two-to-five-minute tasks, with file paths and tests written down before any code is touched.</li>
<li><strong><code>test-driven-development</code></strong>: enforces classic red/green/refactor and treats <em>I'll write the test after</em> as grounds to delete the implementation and start over.</li>
<li><strong><code>systematic-debugging</code></strong>: a four-phase process that explicitly forbids fixing what you have not understood.</li>
<li><strong><code>subagent-driven-development</code></strong>: dispatches the actual implementation to a fresh subagent that has only the plan and the tests, then sends a second agent to review the result.</li>
<li><strong><code>verification-before-completion</code></strong>: requires the agent to run the verification command itself and read the output before claiming anything is done.</li>
</ul>
<p>The remaining eight cover git worktrees, executing plans, requesting and receiving code review, parallel agent dispatch, finishing branches, the bootstrap <code>using-superpowers</code> skill itself, and the meta-skill of authoring new skills.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/superpowers-claude-code-skills-framework#multi-host" aria-label="multi-host permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="multi-host"></span>Multi-Host Skills: Not Just Claude Code</h2>
<p>Although Anthropic's plugin marketplace is the most visible distribution channel, Superpowers is not a Claude-only project.
The same <code>skills/</code> directory powers Cursor, OpenAI Codex (CLI and app), GitHub Copilot CLI, Gemini CLI (via extension), and the open-source <a href="https://opencode.ai" rel="noopener" title="Link to https://opencode.ai" aria-label="OpenCode" target="_blank">OpenCode</a> harness.
Each host gets a thin manifest such as <code>.claude-plugin/</code>, <code>.cursor-plugin/</code>, <code>.codex-plugin/</code>, <code>.opencode/</code>, or <code>gemini-extension.json</code> that points its own discovery mechanism at the same markdown.
The skills themselves are host-agnostic. Superpowers is portable engineering <a class="category-link " title="Artificial Intelligence: Everything related to artificial intelligence (AI) and machine learning (ML)" aria-label="culture" href="/category/ai">culture</a>, not a feature of any single agent.</p>
<span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/f48f0fee9c861d051c94d3a293206e1e/ae105/multi-host.png" class="post-image__link" title="Superpowers multi-host AI agent skills illustration: one glowing markdown file at the center radiates six luminous streams to six terminal windows labeled Claude Code, Cursor, Codex, Copilot CLI, Gemini CLI, and OpenCode"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/f48f0fee9c861d051c94d3a293206e1e/ba1b2/multi-host.webp 314w,/static/f48f0fee9c861d051c94d3a293206e1e/b62e7/multi-host.webp 627w,/static/f48f0fee9c861d051c94d3a293206e1e/29f96/multi-host.webp 1254w" sizes="(min-width: 1254px) 1254px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 1254px) 1254px, 100vw" decoding="async" loading="lazy" data-src="/static/f48f0fee9c861d051c94d3a293206e1e/ae105/multi-host.png" data-srcset="/static/f48f0fee9c861d051c94d3a293206e1e/548ac/multi-host.png 314w,/static/f48f0fee9c861d051c94d3a293206e1e/b076a/multi-host.png 627w,/static/f48f0fee9c861d051c94d3a293206e1e/ae105/multi-host.png 1254w" alt="Superpowers multi-host AI agent skills illustration: one glowing markdown file at the center radiates six luminous streams to six terminal windows labeled Claude Code, Cursor, Codex, Copilot CLI, Gemini CLI, and OpenCode" src="https://blog.marcnuri.com/static/f48f0fee9c861d051c94d3a293206e1e/ae105/multi-host.png" srcset="https://blog.marcnuri.com/static/f48f0fee9c861d051c94d3a293206e1e/548ac/multi-host.png 314w,https://blog.marcnuri.com/static/f48f0fee9c861d051c94d3a293206e1e/b076a/multi-host.png 627w,https://blog.marcnuri.com/static/f48f0fee9c861d051c94d3a293206e1e/ae105/multi-host.png 1254w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure>
<h2 class="heading"><a href="https://blog.marcnuri.com/superpowers-claude-code-skills-framework#iron-laws" aria-label="iron-laws permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="iron-laws"></span>The Iron Laws: Discipline as Anti-Rationalization</h2>
<p>Read enough of the skill files and a pattern emerges.
Each rigid skill opens with a capitalized, non-negotiable rule, an <em>Iron Law</em>, followed by a table of <em>red flags</em>: the rationalizations the agent is most likely to use to skip the rule.</p>
<p>The TDD skill opens with:</p>
<blockquote>
<p><strong>NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST</strong></p>
</blockquote>
<p>Verification's Iron Law is:</p>
<blockquote>
<p><strong>NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE</strong></p>
</blockquote>
<p>Debugging's is:</p>
<blockquote>
<p><strong>NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST</strong></p>
</blockquote>
<p>The accompanying red flags are unsparing.
TDD flags <em>tests passing on first run</em>, <em>keeping deleted code as reference</em>, and the eternal <em>just this once</em>.
Verification flags the words <em>should</em>, <em>probably</em>, and <em>seems to</em>.</p>
<p>These are not motivational posters.
They read like a senior engineer's code-review feedback codified into instructions.
The target is not <em>teaching</em> the agent, because it already knows the rules.
The target is preventing the agent from talking itself out of following them.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/superpowers-claude-code-skills-framework#whats-next" aria-label="whats-next permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="whats-next"></span>What to Watch Next</h2>
<p>Superpowers ships fast.
Vincent has published several follow-ups on his blog since the <a href="https://blog.fsck.com/2025/10/09/superpowers/" rel="noopener" title="Link to https://blog.fsck.com/2025/10/09/superpowers/" aria-label="origin post" target="_blank">origin post</a>, including a <a href="https://blog.fsck.com/2026/03/09/superpowers-5/" rel="noopener" title="Link to https://blog.fsck.com/2026/03/09/superpowers-5/" aria-label="fifth major release" target="_blank">fifth major release</a> in March 2026 and a candid post on the <a href="https://blog.fsck.com/2026/03/31/slop-prs/" rel="noopener" title="Link to https://blog.fsck.com/2026/03/31/slop-prs/" aria-label="Link to https://blog.fsck.com/2026/03/31/slop-prs/" target="_blank"><em>agentic slop PR</em></a> problem the project has exposed as it has scaled.</p>
<p>The interesting question is not whether the discipline approach can work.
The skill files strongly suggest that it can.
The more interesting question is which other parts of an engineering culture, such as code review style, on-call runbooks, security posture, or release management, turn out to be representable as markdown that a coding agent can be made to follow.
Once each agent runs with this kind of discipline, coordinating <em>many of them</em> becomes the next concern, which is exactly what the <a class="post-link " title="AI Coding Agent Dashboard: Orchestrating Claude Code Across Devices" href="/ai-coding-agent-dashboard">AI coding agent dashboard</a> was built for.</p>
  ]]></content:encoded>
            <category>Artificial Intelligence</category>
            <enclosure url="https://blog.marcnuri.com/static/342ca7f4a974f73af283d5a3dd2b13d5/818f3/superpowers-claude-code-skills-framework.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[The Missing Levels of AI-Assisted Development: From Agent Chaos to Orchestration]]></title>
            <link>https://blog.marcnuri.com/missing-levels-ai-assisted-development</link>
            <guid isPermaLink="false">https://blog.marcnuri.com/missing-levels-ai-assisted-development</guid>
            <pubDate>Tue, 21 Apr 2026 18:00:00 GMT</pubDate>
            <description><![CDATA[Steve Yegge's 8 levels of AI-assisted development skip the hardest climb: the five missing rungs between juggling coding agents and your own orchestrator.]]></description>
            <content:encoded><![CDATA[
    <div><a href="https://blog.marcnuri.com/missing-levels-ai-assisted-development">Original post</a></div>
    <h2 class="heading"><a href="https://blog.marcnuri.com/missing-levels-ai-assisted-development#introduction" aria-label="introduction permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="introduction"></span>Introduction</h2>
<p>A few months ago I wrote about <a class="post-link " title="Boosting My Developer Productivity with AI in 2025" href="/boosting-developer-productivity-ai-2025">shifting from AI implementer to AI orchestrator</a> and, shortly after, about <a class="post-link " title="AI Coding Agent Dashboard: Orchestrating Claude Code Across Devices" href="/ai-coding-agent-dashboard">the AI coding agent dashboard I built</a> to keep that orchestration from collapsing under its own weight.
The response surprised me.
The most common question was not <em>"how do I get started?"</em> but <em>"how do I get past this?"</em>.</p>
<p>Most developers I talk to can picture the first few steps up the <a class="category-link " title="Artificial Intelligence: Everything related to artificial intelligence (AI) and machine learning (ML)" aria-label="AI-assisted development" href="/category/ai">AI-assisted development</a> ladder.
Turn on autocomplete, let the agent edit files, trust it a little more every week.
What nobody seems to agree on is what happens once you have five, ten, or twenty agents running, and it has stopped feeling like productivity and started feeling like a second full-time job.</p>
<p>Steve Yegge has a useful framework for this.
His <a href="https://newsletter.pragmaticengineer.com/p/steve-yegge-on-ai-agents-and-the" rel="noopener" title="Link to https://newsletter.pragmaticengineer.com/p/steve-yegge-on-ai-agents-and-the" aria-label="8 levels of AI-assisted development, as described on The Pragmatic Engineer" target="_blank">8 levels of AI-assisted development, as described on The Pragmatic Engineer</a>, have become a common shorthand among people thinking seriously about <a class="tag-link " title="AI Agent" aria-label="coding agents" href="/tag/ai-agent">coding agents</a>.
The more I sit with it, the more I suspect Level 7 and Level 8 are not one step apart.
They are four or five.
This post is not a finished map.
It is my current sense of the rungs that seem to be there, based on my own climb so far.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/missing-levels-ai-assisted-development#yegges-ladder" aria-label="yegges-ladder permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="yegges-ladder"></span>Steve Yegge's 8 Levels of AI-Assisted Development</h2>
<p>Steve Yegge's 8 levels of AI-assisted development describe a developer's progression from no AI assistance to building a custom agent orchestrator.
As described in his <a href="https://newsletter.pragmaticengineer.com/p/steve-yegge-on-ai-agents-and-the" rel="noopener" title="Link to https://newsletter.pragmaticengineer.com/p/steve-yegge-on-ai-agents-and-the" aria-label="Pragmatic Engineer interview" target="_blank">Pragmatic Engineer interview</a>:</p>
<ul>
<li><strong>Level 1</strong>: No AI.</li>
<li><strong>Level 2</strong>: IDE coding agent with permissions on.</li>
<li><strong>Level 3</strong>: IDE coding agent in YOLO mode.</li>
<li><strong>Level 4</strong>: Conversation over diff review.</li>
<li><strong>Level 5</strong>: CLI-first workflow; the IDE is no longer home.</li>
<li><strong>Level 6</strong>: Several parallel agents.</li>
<li><strong>Level 7</strong>: Ten-plus agents, coordinated by hand.</li>
<li><strong>Level 8</strong>: A custom agent orchestrator.</li>
</ul>
<p>Levels 1 through 5 are mostly about <strong>trust in one agent</strong>.
Level 6 is where parallelism starts to pay off.
Level 7 is the moment you realize you are not ready for parallelism.
Level 8 is the moment you decide to build your way out of the mess.</p>
<p>That is a good ladder.
It also has a very large missing rung.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/missing-levels-ai-assisted-development#the-gap" aria-label="the-gap permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="the-gap"></span>The Gap Between Level 7 and Level 8</h2>
<p>Read Yegge's own description of the leap and something jumps out: there is no middle.
One paragraph you are texting the wrong agent by accident and misplacing work in tmux windows; the next paragraph you are writing an orchestrator in Go.
<a href="https://addyosmani.com/blog/code-agent-orchestra/" rel="noopener" title="Link to https://addyosmani.com/blog/code-agent-orchestra/" aria-label="Addy Osmani" target="_blank">Addy Osmani</a> captures the same transition in his "Code Agent Orchestra" piece with one of my favorite lines of the year:</p>
<blockquote>
<p>The bottleneck is no longer generation. It's verification.</p>
</blockquote>
<p>That is exactly what breaks at Level 7.
You can produce more code than you can realistically review.
Specs silently drift between agents because each one is working from the prompt <em>you gave it</em>, not a shared source of truth.
Two agents edit overlapping files and neither knows.
You rubber-stamp pull requests that you would have rejected at Level 5.
And the cognitive cost of twelve parallel threads starts to erase the productivity you went looking for.</p>
<p>Jumping from there to <em>"build your own orchestrator"</em> is, for almost everyone, unrealistic.
Writing a task scheduler is not the hard part.
The hard part is figuring out <em>what</em> to schedule, <em>when</em> a human should intervene, and <em>how</em> to retain enough understanding of the system to stay the person in charge of it.</p>
<p>Those are not engineering problems.
They are workflow problems.
And they have rungs of their own.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/missing-levels-ai-assisted-development#the-missing-rungs" aria-label="the-missing-rungs permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="the-missing-rungs"></span>The Missing Levels Between Agent Chaos and Orchestration</h2>
<p>What follows is my current sense of the sequence between Yegge's Level 7 and Level 8.
I have climbed some of these rungs.
Others I can only see from below.
Think of them as an agentic Software Development Lifecycle (SDLC) maturity model for the mid-game, where agent autonomy goes up but your visibility into the system has to go up with it.</p>
<span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/5506032c2c2ac76e1e4bb99423e3f8f5/6e90d/missing-levels-diagram.png" class="post-image__link" title="Isometric staircase diagram showing the climb from Level 7 (ten-plus agents by hand) up through five intermediate rungs — 7.1 See the swarm, 7.2 Decouple, 7.3 Delegate async, 7.4 Gate by blast radius, 7.5 Build primitives — to Level 8 (custom orchestrator)"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/5506032c2c2ac76e1e4bb99423e3f8f5/bf9f6/missing-levels-diagram.webp 384w,/static/5506032c2c2ac76e1e4bb99423e3f8f5/588ff/missing-levels-diagram.webp 768w,/static/5506032c2c2ac76e1e4bb99423e3f8f5/7db9c/missing-levels-diagram.webp 1536w" sizes="(min-width: 1536px) 1536px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 1536px) 1536px, 100vw" decoding="async" loading="lazy" data-src="/static/5506032c2c2ac76e1e4bb99423e3f8f5/6e90d/missing-levels-diagram.png" data-srcset="/static/5506032c2c2ac76e1e4bb99423e3f8f5/65afd/missing-levels-diagram.png 384w,/static/5506032c2c2ac76e1e4bb99423e3f8f5/97cdf/missing-levels-diagram.png 768w,/static/5506032c2c2ac76e1e4bb99423e3f8f5/6e90d/missing-levels-diagram.png 1536w" alt="Isometric staircase diagram showing the climb from Level 7 (ten-plus agents by hand) up through five intermediate rungs — 7.1 See the swarm, 7.2 Decouple, 7.3 Delegate async, 7.4 Gate by blast radius, 7.5 Build primitives — to Level 8 (custom orchestrator)" src="https://blog.marcnuri.com/static/5506032c2c2ac76e1e4bb99423e3f8f5/6e90d/missing-levels-diagram.png" srcset="https://blog.marcnuri.com/static/5506032c2c2ac76e1e4bb99423e3f8f5/65afd/missing-levels-diagram.png 384w,https://blog.marcnuri.com/static/5506032c2c2ac76e1e4bb99423e3f8f5/97cdf/missing-levels-diagram.png 768w,https://blog.marcnuri.com/static/5506032c2c2ac76e1e4bb99423e3f8f5/6e90d/missing-levels-diagram.png 1536w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure>
<h3 class="heading"><a href="https://blog.marcnuri.com/missing-levels-ai-assisted-development#see-the-swarm" aria-label="see-the-swarm permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="see-the-swarm"></span>7.1 — See the swarm</h3>
<p>The first thing that breaks at Level 7 is not the agents.
It is your memory of them.</p>
<p>You cannot coordinate what you cannot see, and at ten-plus concurrent sessions, tmux tabs and terminal windows stop being a mental model.
You start forgetting which coding agent is doing what, which branch it is on, which one is stalled waiting for permission, and which one finished an hour ago.
What fixed this for me was not a new agent or a better model.
It was externalizing the state of every session into one view I could glance at.</p>
<p>That is what the <a class="post-link " title="AI Coding Agent Dashboard: Orchestrating Claude Code Across Devices" href="/ai-coding-agent-dashboard">coding agent dashboard I built earlier this year</a> does.
It pulls the device, branch, model, context usage, status, and current task of every active session into one place.
You do not need my dashboard for this rung.
You need <em>a</em> dashboard.
A spreadsheet, a shell script, anything that externalizes what your working memory can no longer hold.</p>
<div style="max-width:480px;margin:0 auto"><span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/87e0f7f0176484cb4baeeb3e46587fff/6e90d/see-the-swarm.png" class="post-image__link" title="Chaotic stack of terminal windows on the left, a single unified dashboard panel on the right, with a calm human figure in front of it"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/87e0f7f0176484cb4baeeb3e46587fff/bf9f6/see-the-swarm.webp 384w,/static/87e0f7f0176484cb4baeeb3e46587fff/588ff/see-the-swarm.webp 768w,/static/87e0f7f0176484cb4baeeb3e46587fff/7db9c/see-the-swarm.webp 1536w" sizes="(min-width: 1536px) 1536px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 1536px) 1536px, 100vw" decoding="async" loading="lazy" data-src="/static/87e0f7f0176484cb4baeeb3e46587fff/6e90d/see-the-swarm.png" data-srcset="/static/87e0f7f0176484cb4baeeb3e46587fff/65afd/see-the-swarm.png 384w,/static/87e0f7f0176484cb4baeeb3e46587fff/97cdf/see-the-swarm.png 768w,/static/87e0f7f0176484cb4baeeb3e46587fff/6e90d/see-the-swarm.png 1536w" alt="Chaotic stack of terminal windows on the left, a single unified dashboard panel on the right, with a calm human figure in front of it" src="https://blog.marcnuri.com/static/87e0f7f0176484cb4baeeb3e46587fff/6e90d/see-the-swarm.png" srcset="https://blog.marcnuri.com/static/87e0f7f0176484cb4baeeb3e46587fff/65afd/see-the-swarm.png 384w,https://blog.marcnuri.com/static/87e0f7f0176484cb4baeeb3e46587fff/97cdf/see-the-swarm.png 768w,https://blog.marcnuri.com/static/87e0f7f0176484cb4baeeb3e46587fff/6e90d/see-the-swarm.png 1536w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure></div>
<h3 class="heading"><a href="https://blog.marcnuri.com/missing-levels-ai-assisted-development#decouple-from-the-machine" aria-label="decouple-from-the-machine permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="decouple-from-the-machine"></span>7.2 — Decouple from the machine</h3>
<p>Once you can see all your agents, the next thing you want is to stop being chained to the machine they run on.</p>
<p>My daily setup involves a laptop and a Linux workstation.
Before this rung, agents were pinned to whichever machine I happened to start them on.
After it, I could spawn a session on the workstation from my laptop, attach to a session on the workstation from my phone, and kick off work from a tablet on the couch.
I did not want to work more hours.
I wanted to stop paying the friction of <em>"is this the machine the agent is on?"</em> every time I switched contexts.</p>
<p>There is more than one way up this rung.
A shared tmux server reachable over SSH gets you part of the way.
Tools like <a href="https://tmate.io/" rel="noopener" title="Link to https://tmate.io/" aria-label="tmate" target="_blank">tmate</a>, Tailscale, or any browser-accessible terminal close the gap further.
What matters is that the agent stops being pinned to a physical device and starts being reachable from wherever you happen to be.</p>
<p>This is where parallelism stops being a local trick and starts becoming a property of your workflow.
Cross-device visibility and control turn a group of parallel agents into something closer to a multi-agent team that happens to share your laptop.</p>
<div style="max-width:480px;margin:0 auto"><span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/adf91d8ba6701cd6c40174b10d3934f1/ae105/decouple-from-the-machine.png" class="post-image__link" title="Agents floating above a laptop, a phone, and a tablet, connected by luminous data streams to all three devices at once"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/adf91d8ba6701cd6c40174b10d3934f1/ba1b2/decouple-from-the-machine.webp 314w,/static/adf91d8ba6701cd6c40174b10d3934f1/b62e7/decouple-from-the-machine.webp 627w,/static/adf91d8ba6701cd6c40174b10d3934f1/29f96/decouple-from-the-machine.webp 1254w" sizes="(min-width: 1254px) 1254px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 1254px) 1254px, 100vw" decoding="async" loading="lazy" data-src="/static/adf91d8ba6701cd6c40174b10d3934f1/ae105/decouple-from-the-machine.png" data-srcset="/static/adf91d8ba6701cd6c40174b10d3934f1/548ac/decouple-from-the-machine.png 314w,/static/adf91d8ba6701cd6c40174b10d3934f1/b076a/decouple-from-the-machine.png 627w,/static/adf91d8ba6701cd6c40174b10d3934f1/ae105/decouple-from-the-machine.png 1254w" alt="Agents floating above a laptop, a phone, and a tablet, connected by luminous data streams to all three devices at once" src="https://blog.marcnuri.com/static/adf91d8ba6701cd6c40174b10d3934f1/ae105/decouple-from-the-machine.png" srcset="https://blog.marcnuri.com/static/adf91d8ba6701cd6c40174b10d3934f1/548ac/decouple-from-the-machine.png 314w,https://blog.marcnuri.com/static/adf91d8ba6701cd6c40174b10d3934f1/b076a/decouple-from-the-machine.png 627w,https://blog.marcnuri.com/static/adf91d8ba6701cd6c40174b10d3934f1/ae105/decouple-from-the-machine.png 1254w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure></div>
<h3 class="heading"><a href="https://blog.marcnuri.com/missing-levels-ai-assisted-development#delegate-asynchronously" aria-label="delegate-asynchronously permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="delegate-asynchronously"></span>7.3 — Delegate asynchronously</h3>
<p>Up to here, every agent is still waiting for <em>you</em>.
You start it, you watch it, you answer its questions.</p>
<p>The next rung is letting go of that loop.
<a class="post-link " title="Boosting My Developer Productivity with AI in 2025" href="/boosting-developer-productivity-ai-2025">GitHub Copilot coding agent workflows</a>, and similar <a class="tag-link " title="GitHub" aria-label="issue-to-pull-request agent workflows" href="/tag/github">issue-to-pull-request agent workflows</a>, change the unit of work from <em>"a terminal session"</em> to <em>"a well-written issue and a pull request"</em>.
You describe the change in prose, assign it, walk away, and review the result whenever you get to it.
Long-running tasks progress while you are on a call, on a train, or asleep.</p>
<p>This is the rung where the <strong>quality of your project</strong> starts to dominate the quality of your output.
Async delegation only works if the agent has enough context to succeed without you in the room: clear specs, strong test coverage, and well-scoped interfaces.
A well-structured codebase amplifies the agent.
A chaotic one amplifies the chaos.</p>
<div style="max-width:480px;margin:0 auto"><span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/478ae1b0fa82a107e1ee08a820c5f3b5/ae105/delegate-asynchronously.png" class="post-image__link" title="A developer drops an issue into an input slot on the left while another retrieves a completed pull request from an output slot on the right, with a glowing factory of agents processing work in between"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/478ae1b0fa82a107e1ee08a820c5f3b5/ba1b2/delegate-asynchronously.webp 314w,/static/478ae1b0fa82a107e1ee08a820c5f3b5/b62e7/delegate-asynchronously.webp 627w,/static/478ae1b0fa82a107e1ee08a820c5f3b5/29f96/delegate-asynchronously.webp 1254w" sizes="(min-width: 1254px) 1254px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 1254px) 1254px, 100vw" decoding="async" loading="lazy" data-src="/static/478ae1b0fa82a107e1ee08a820c5f3b5/ae105/delegate-asynchronously.png" data-srcset="/static/478ae1b0fa82a107e1ee08a820c5f3b5/548ac/delegate-asynchronously.png 314w,/static/478ae1b0fa82a107e1ee08a820c5f3b5/b076a/delegate-asynchronously.png 627w,/static/478ae1b0fa82a107e1ee08a820c5f3b5/ae105/delegate-asynchronously.png 1254w" alt="A developer drops an issue into an input slot on the left while another retrieves a completed pull request from an output slot on the right, with a glowing factory of agents processing work in between" src="https://blog.marcnuri.com/static/478ae1b0fa82a107e1ee08a820c5f3b5/ae105/delegate-asynchronously.png" srcset="https://blog.marcnuri.com/static/478ae1b0fa82a107e1ee08a820c5f3b5/548ac/delegate-asynchronously.png 314w,https://blog.marcnuri.com/static/478ae1b0fa82a107e1ee08a820c5f3b5/b076a/delegate-asynchronously.png 627w,https://blog.marcnuri.com/static/478ae1b0fa82a107e1ee08a820c5f3b5/ae105/delegate-asynchronously.png 1254w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure></div>
<h3 class="heading"><a href="https://blog.marcnuri.com/missing-levels-ai-assisted-development#gate-by-blast-radius" aria-label="gate-by-blast-radius permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="gate-by-blast-radius"></span>7.4 — Gate by blast radius</h3>
<p>This is the rung I have not climbed yet.</p>
<p>At Level 7 I have more work flowing through more agents than I can possibly inspect line by line, and I still try to.
I review every pull request.
I open every diff.
It is exhausting, and I know it does not scale, but the alternative feels reckless.</p>
<p>I think the better move is to <strong>decide in advance</strong> which decisions deserve a human gate, instead of compensating for the volume with fear.
Not <em>"does this need approval?"</em>, because the honest answer is always <em>"probably"</em>.
The more useful question is <em>why</em> it would need approval:</p>
<ul>
<li>How big is the <strong>blast radius</strong>: one component, or a dozen services?</li>
<li>Is the change <strong>reversible</strong> in minutes, or would rolling it back take hours?</li>
<li>Does it <strong>commit</strong> the project to anything external, such as money, contracts, or third-party accounts?</li>
<li>Is it <strong>visible</strong> to users, customers, or stakeholders if it goes wrong?</li>
</ul>
<p>If all four are low, the agent could in theory proceed on its own and tell me after the fact.
If any one of them is high, the agent stops and waits for me.
This is not a new idea.
Incident response teams have lived on similar dimensions for years.
The hard part is encoding it into a workflow so it actually shapes what lands on my review queue, instead of just being a diagram on a slide.</p>
<p>A fifth, boring dimension deserves a mention here: <strong>cost</strong>.
Ten agents running continuously against a capable model is not free, and the bill grows quietly while you sleep.
Treating spend as just another gate, with per-session budgets and a hard ceiling, is the difference between an experiment and a surprise.</p>
<p>If I get this rung right, human time moves from <em>tasks</em> to <em>decisions</em>, which is where it actually belongs.
I am not there yet.
Most of the time I am still staring at diffs.</p>
<h3 class="heading"><a href="https://blog.marcnuri.com/missing-levels-ai-assisted-development#build-the-primitives" aria-label="build-the-primitives permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="build-the-primitives"></span>7.5 — Build the primitives</h3>
<p>The last rung before a real orchestrator is where things stop being policies and start being code.</p>
<p>At some point the dashboard needs to know more than where your sessions are.
It needs to know which tasks are queued and which are in flight, so two agents do not grab the same issue.
The smallest version I have been sketching in my head is a shared list of open issues, a lightweight <em>"claimed"</em> flag, and an agent-side hook that checks before picking one up.
Barely a hundred lines of glue, and yet it seems like it would be the single biggest reduction in duplicated work I can imagine.</p>
<p>From there the primitives accumulate quickly.
Checkpointing long-running sessions so a crash does not lose an afternoon of work.
Scoped, short-lived credentials per session so <em>"the agent has my credentials"</em> stops being a sentence that keeps you up at night.
A way to feed spec updates back to an agent that was started twenty minutes ago.</p>
<p>None of that is a custom orchestrator yet.
But each of those primitives is what a custom orchestrator would be <em>made of</em>.
If they accumulate one at a time, Level 8 stops being a weekend project and becomes the natural conclusion of a pile of small, pragmatic tools.
This is the direction my tooling keeps pulling me in, even when I am not actively building for it.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/missing-levels-ai-assisted-development#beyond-level-8" aria-label="beyond-level-8 permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="beyond-level-8"></span>Beyond Level 8: Fully Autonomous Software Development</h2>
<p>Yegge's Level 8 is <em>"build your own orchestrator"</em>, but it is not the end of the road.
<a href="https://www.danshapiro.com/blog/2026/01/the-five-levels-from-spicy-autocomplete-to-the-software-factory/" rel="noopener" title="Link to https://www.danshapiro.com/blog/2026/01/the-five-levels-from-spicy-autocomplete-to-the-software-factory/" aria-label="Dan Shapiro's five levels from spicy autocomplete to the Dark Factory" target="_blank">Dan Shapiro's five levels from spicy autocomplete to the Dark Factory</a> describe a very different endpoint: Level 5, the <strong>Dark Factory</strong>.
A software process that takes specifications in and ships software out, with no human writing or reviewing code.</p>
<p>It is no longer hypothetical.
<a href="https://simonwillison.net/2026/Feb/7/software-factory/" rel="noopener" title="Link to https://simonwillison.net/2026/Feb/7/software-factory/" aria-label="Simon Willison has documented" target="_blank">Simon Willison has documented</a> how StrongDM's three-person team has been operating this way since mid-2025, shipping a codebase of around 32,000 lines of Rust, Go, and TypeScript under a rule that no human writes or reviews the code.
The questions it raises, about specification quality, the information barrier between builder and evaluator, and who is accountable for a codebase nobody has read, deserve a post of their own.</p>
<p>For now I am comfortable saying I am not there, and I am not sure I want to be.
The climb I am describing stops well short of that.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/missing-levels-ai-assisted-development#conclusion" aria-label="conclusion permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="conclusion"></span>Conclusion</h2>
<p>Frameworks like Yegge's are useful because they let us talk about where we are and where we are trying to go.
But a ladder with a missing rung is not a ladder. It is a drop.
And the drop between <em>"ten agents managed by hand"</em> and <em>"build your own orchestrator"</em> has swallowed a lot of promising AI-assisted workflows.</p>
<p>The climb between them is not glamorous.
It is visibility, distribution, asynchrony, gating, and small primitives.
Five quiet rungs that together make Level 8 feasible and, much more importantly, make Level 7 survivable.</p>
<p>There is also a second question hiding beneath the whole climb: not how high you go, but <a class="post-link " title="Project, Agent, Person: The Missing Axis of AI Coding Tools" href="/project-agent-person-centric-ai-coding-tools">what the tools you are climbing with are built around</a> — the project, the agent, or you. It is a large part of why the orchestrator ends up being yours to build.</p>
<p>If you are somewhere on this climb, I would like to hear where.
I am tracking feedback and ideas on the <a href="https://github.com/manusa/ai-beacon/discussions" rel="noopener" title="Link to https://github.com/manusa/ai-beacon/discussions" aria-label="ai-beacon discussions" target="_blank">ai-beacon discussions</a> while I work on the tooling, so that is the best place to compare notes.
The more of them we share, the better the next version of the ladder will be.</p>
  ]]></content:encoded>
            <category>Artificial Intelligence</category>
            <enclosure url="https://blog.marcnuri.com/static/5bf89363d373a1c4eac1689d338e60c2/818f3/missing-levels-ai-assisted-development.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Promoted to Senior Principal Software Engineer at Red Hat]]></title>
            <link>https://blog.marcnuri.com/promoted-senior-principal-software-engineer-red-hat</link>
            <guid isPermaLink="false">https://blog.marcnuri.com/promoted-senior-principal-software-engineer-red-hat</guid>
            <pubDate>Mon, 13 Apr 2026 18:00:00 GMT</pubDate>
            <description><![CDATA[Promoted to Senior Principal Software Engineer at Red Hat. Reflections on the Kubernetes MCP Server, AI-assisted development, and the people behind it.]]></description>
            <content:encoded><![CDATA[
    <div><a href="https://blog.marcnuri.com/promoted-senior-principal-software-engineer-red-hat">Original post</a></div>
    <p>On April 1, 2026, I was promoted to Senior Principal Software Engineer at <a class="tag-link " title="Red Hat" aria-label="Red Hat" href="/tag/red-hat">Red Hat</a>.</p>
<p>I don't usually care much about job titles.
But given the uncertainty in the tech industry, the intensity of the past fourteen months, and everything that happened in between, this one hit differently.
It's a milestone I genuinely needed.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/promoted-senior-principal-software-engineer-red-hat#the-road-here" aria-label="the-road-here permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="the-road-here"></span>The road here</h2>
<p>If you've read my <a class="post-link " title="2025 Year in Review: The Year of AI" href="/2025-year-in-review-the-year-of-ai">2025 year in review</a>, you know it was a pivotal but difficult year.
The transfer of Red Hat's middleware teams to IBM shook me.
<a class="tag-link " title="RedHattiversary" aria-label="Being a Red Hatter" href="/tag/redhattiversary">Being a Red Hatter</a> has always been more than a job for me, it's an identity tied to the free software values I've admired since I first started using Linux as a teenager.</p>
<p>But difficult years have a way of producing unexpected opportunities.
In hindsight, I think the work that came out of 2025 is what ultimately led to this promotion.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/promoted-senior-principal-software-engineer-red-hat#the-kubernetes-mcp-server" aria-label="the-kubernetes-mcp-server permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="the-kubernetes-mcp-server"></span>The Kubernetes MCP Server</h2>
<p>In early 2025, I built a proof of concept for a <a class="post-link " title="Introduction to the Model Context Protocol (MCP): The Future of AI Integration" href="/model-context-protocol-mcp-introduction">Model Context Protocol (MCP)</a> server to manage <a class="tag-link " title="Kubernetes" aria-label="Kubernetes" href="/tag/kubernetes">Kubernetes</a> clusters, originally based on the <a class="tag-link " title="Fabric8" aria-label="Fabric8 Kubernetes Client" href="/tag/fabric8">Fabric8 Kubernetes Client</a>.
The first time I saw an AI agent autonomously deploy and manage applications on Kubernetes, I knew this was something special.</p>
<p>I demoed it internally at Red Hat, and the response made it clear this could grow into something much bigger.
Seeing the potential, I made a bet: port it to <a class="tag-link " title="Go" aria-label="Go" href="/tag/go">Go</a>.
Most of the Kubernetes ecosystem is built in Go, and Kubernetes developers are far more comfortable with it.
It turned out to be the right call.</p>
<p>The project grew fast.
People from across Red Hat reached out to collaborate.
The repository <a class="post-link " title="Kubernetes MCP Server Joins the Containers Organization!" href="/kubernetes-mcp-server-containers-organization">joined the Containers organization</a> on GitHub, alongside tools like Podman and Buildah.
Today, the <a href="https://github.com/containers/kubernetes-mcp-server" rel="noopener" title="Kubernetes MCP Server GitHub repository" aria-label="Kubernetes MCP Server" target="_blank">Kubernetes MCP Server</a> has a thriving upstream community with many active contributors and a healthy ecosystem of forks.</p>
<p>It's also being productized as the <a href="https://catalog.redhat.com/en/software/container-stacks/detail/69a94fac9956d5da803edd1a" rel="noopener" title="MCP server for Red Hat OpenShift on the Red Hat Ecosystem Catalog" aria-label="MCP server for Red Hat OpenShift" target="_blank">MCP server for Red Hat OpenShift</a>, with a dedicated team at Red Hat working on it.
I actively collaborate with this team, and without them the project wouldn't be where it is today.
I feel extremely lucky to be working with such talented people.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/promoted-senior-principal-software-engineer-red-hat#ai-assisted-development" aria-label="ai-assisted-development permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="ai-assisted-development"></span>AI-assisted development</h2>
<p>The MCP server wasn't the only way AI reshaped my work.
I also restructured my entire development workflow around <a class="tag-link " title="AI Agent" aria-label="AI coding agents" href="/tag/ai-agent">AI coding agents</a>.
I wrote about this in <a class="post-link " title="Boosting My Developer Productivity with AI in 2025" href="/boosting-developer-productivity-ai-2025">Boosting My Developer Productivity with AI in 2025</a>, and later built an <a class="post-link " title="AI Coding Agent Dashboard: Orchestrating Claude Code Across Devices" href="/ai-coding-agent-dashboard">AI Coding Agent Dashboard</a> to orchestrate multiple agent sessions across devices in real time.</p>
<p>This wasn't just a personal experiment.
I've given three internal presentations at Red Hat on how I leverage AI tooling, the workflows I've built, and the productivity gains I've measured.
The interest from colleagues across the company has been one of the most rewarding parts of this journey.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/promoted-senior-principal-software-engineer-red-hat#the-foundation" aria-label="the-foundation permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="the-foundation"></span>The foundation</h2>
<p>None of these new initiatives meant I could step away from my existing responsibilities.
Throughout all of this, I continued maintaining the <a class="tag-link " title="Fabric8" aria-label="Fabric8 Kubernetes Client" href="/tag/fabric8">Fabric8 Kubernetes Client</a> and <a class="tag-link " title="Eclipse JKube" aria-label="Eclipse JKube" href="/tag/jkube">Eclipse JKube</a>, two projects I've been responsible for since I <a class="tag-link " title="RedHattiversary" aria-label="joined Red Hat" href="/tag/redhattiversary">joined Red Hat</a> more than six years ago.</p>
<p>The Kubernetes Client is a foundational building block for projects like Quarkus, Spring Cloud, and Java Operator SDK.
Eclipse JKube remains a key tool for Java developers deploying to Kubernetes.
Keeping these projects healthy while taking on everything else was one of the hardest and most important parts of the past year.</p>
<p>I'm grateful that I now have help maintaining them, which has been essential as my scope expanded.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/promoted-senior-principal-software-engineer-red-hat#hard-work-and-good-luck" aria-label="hard-work-and-good-luck permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="hard-work-and-good-luck"></span>Hard work and good luck</h2>
<p>Looking back, I think this promotion was a mix of hard work and good timing.
I was fortunate that MCP was emerging as a standard and AI-assisted development was gaining traction right when I was in a position to act on both.
I worked hard, but I also got lucky.
Not everyone who works this hard gets the same recognition, and I'm very aware of that.</p>
<p>2025 and early 2026 have been the most intense period of my career.
On top of all the new initiatives, I still had to sustain the Fabric8 Kubernetes Client and Eclipse JKube, hire someone to help with maintenance, and deliver three internal presentations on AI-assisted development.
This promotion feels like the culmination of all of it.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/promoted-senior-principal-software-engineer-red-hat#gratitude" aria-label="gratitude permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="gratitude"></span>Gratitude</h2>
<p>This promotion wouldn't have happened without the people around me.</p>
<p>My managers gave me the autonomy and trust to pursue ambitious projects.
They believed in my work when the path forward wasn't always clear, and they created the environment that made all of this possible.</p>
<p>The peers who provided feedback during the promotion process were generous and deeply encouraging.
Knowing that the people I work with value my contributions means more to me than the title itself.</p>
<p>The tech industry is going through turbulent times.
Recent changes at Red Hat have affected people I deeply respect, people who directly contributed to my growth and success.
I carry their support with me, and this milestone is bittersweet because of it.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/promoted-senior-principal-software-engineer-red-hat#looking-forward" aria-label="looking-forward permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="looking-forward"></span>Looking forward</h2>
<p>I don't know what comes next.
What I do know is that the mission hasn't changed: building <a class="tag-link " title="Open Source" aria-label="free and open source" href="/tag/open-source">free and open source</a> tools that make developers' lives easier, whether those developers are humans, AI agents, or both.</p>
<p>I'm incredibly grateful for this recognition, and even more motivated by the work still ahead.</p>
  ]]></content:encoded>
            <category>Personal</category>
            <enclosure url="https://blog.marcnuri.com/static/0d609c62ab9c091eb68539fa933fb8e5/52ca6/promoted-senior-principal-software-engineer-red-hat.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Fabric8 Kubernetes Client 7.6 is now available!]]></title>
            <link>https://blog.marcnuri.com/fabric8-kubernetes-client-7-6</link>
            <guid isPermaLink="false">https://blog.marcnuri.com/fabric8-kubernetes-client-7-6</guid>
            <pubDate>Tue, 03 Mar 2026 14:00:00 GMT</pubDate>
            <description><![CDATA[Fabric8 Kubernetes Client 7.6 is available! Check out the major changes and learn how you can contribute.]]></description>
            <content:encoded><![CDATA[
    <div><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-6">Original post</a></div>
    <div class="admonition admonition__note"><p class="admonition-title"><i class="admonition-title-icon fa-solid fa-circle-info"></i>Note</p><div class="admonition-content">A newer version of Fabric8 Kubernetes Client is available, jump to <a class="post-link " title="Fabric8 Kubernetes Client 7.8 is now available!" href="/fabric8-kubernetes-client-7-8">Fabric8 Kubernetes Client 7.8 announcement</a>.</div></div>
<p>On behalf of the <a class="post-link " title="Kubernetes Client for Java: Fabric8 introduction" href="/kubernetes-client-java-fabric8-introduction">Fabric8</a>
team and everyone who has contributed, I'm happy to announce that the Fabric8 Kubernetes Client <code>7.6.1</code> has been
<a href="https://github.com/fabric8io/kubernetes-client/releases/tag/v7.6.1" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/releases/tag/v7.6.1" aria-label="released" target="_blank">released</a> and is now available from
<a href="https://repo1.maven.org/maven2/io/fabric8/kubernetes-client/7.6.1/" rel="noopener" title="Link to https://repo1.maven.org/maven2/io/fabric8/kubernetes-client/7.6.1/" aria-label="Maven Central" target="_blank">Maven Central</a> 🎉.</p>
<p>This marks the sixth minor release of the Fabric8 Kubernetes Client 7, bringing new features, bug fixes, and improvements while keeping the breaking changes minimal.</p>
<p>Thanks to all of you who have contributed with issue reports, pull requests, feedback, and spreading the word with blogs, videos, comments, and so on.
We really appreciate your help, keep it up!</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-6#whats-new" aria-label="whats-new permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="whats-new"></span>What's new?</h2>
<p>Without further ado, let's have a look at the most significant updates:</p>
<ul>
<li><a class="post-link " title="Link to the Kubernetes 1.35 section" href="/fabric8-kubernetes-client-7-6#kubernetes-135">Kubernetes 1.35 support (Timbernetes)</a></li>
<li><a class="post-link " title="Link to the Vert.x 5 section" href="/fabric8-kubernetes-client-7-6#vertx-5">New Vert.x 5 HTTP client implementation</a></li>
<li><a class="post-link " title="Link to the OkHttp 5 section" href="/fabric8-kubernetes-client-7-6#okhttp-5">OkHttp upgraded to version 5</a></li>
<li>🐛 Many other bug fixes and minor improvements</li>
</ul>
<p>You can find the full changelog for this version in our GitHub <a href="https://github.com/fabric8io/kubernetes-client/releases/tag/v7.6.1" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/releases/tag/v7.6.1" aria-label="release page" target="_blank">release page</a>.</p>
<h3 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-6#kubernetes-135" aria-label="kubernetes-135 permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="kubernetes-135"></span>Kubernetes 1.35 support (Timbernetes)</h3>
<p>This release adds support for Kubernetes v1.35 (Timbernetes), ensuring you have access to the latest API resources and CRDs.</p>
<div class="admonition admonition__note"><p class="admonition-title"><i class="admonition-title-icon fa-solid fa-circle-info"></i>Note</p><div class="admonition-content">
<p>Please note that you can still access newer Kubernetes clusters with <strong>older</strong> versions of the Fabric8 client.</p>
<p>The client provides a GenericKubernetesResources class to interact with resources that are not yet supported by the client.
We do recommend to always use the <strong>latest</strong> version of the client to benefit from the latest features and bug fixes, but it's not mandatory.</p>
</div></div>
<h3 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-6#vertx-5" aria-label="vertx-5 permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="vertx-5"></span>New Vert.x 5 HTTP client implementation</h3>
<p>A new Vert.x 5 HTTP client implementation has been added with improved async handling and WebSocket separation.
The <code>kubernetes-httpclient-vertx-5</code> module includes a runtime check for Vert.x 5 classes to provide a clear error message when a Vert.x 4/5 conflict occurs.</p>
<p>The HTTP client factory priority has also been fixed: <code>VertxHttpClientFactory</code> (the default) now has priority -1, while <code>OkHttpClientFactory</code> is restored to priority 0.</p>
<p>Note that the <code>kubernetes-httpclient-vertx</code> (Vert.x 4.x) and <code>kubernetes-httpclient-vertx-5</code> (Vert.x 5.x) modules are <strong>mutually exclusive</strong> and must not be included together in your project dependencies.
When using Vert.x 5, exclude the default Vert.x 4 client and add the Vert.x 5 module:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-xml" style="white-space:pre"><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">dependency</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>io.fabric8</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>kubernetes-client</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">exclusions</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>    </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">exclusion</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>      </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>io.fabric8</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>      </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>kubernetes-httpclient-vertx</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>    </span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">exclusion</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">exclusions</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span></span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">dependency</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span></span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">dependency</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>io.fabric8</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>kubernetes-httpclient-vertx-5</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span></span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">dependency</span><span style="color:#e8bf6a">&gt;</span></code></pre></div>
<h3 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-6#okhttp-5" aria-label="okhttp-5 permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="okhttp-5"></span>OkHttp upgraded to version 5</h3>
<p>OkHttp has been upgraded from version 4.12.0 to 5.3.2.
This update removes internal API usage and fixes deprecated OkHttp 5 calls.
While the versions are binary compatible, the major version upgrade might cause side effects in some projects.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-6#using-this-release" aria-label="using-this-release permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="using-this-release"></span>Using this release</h2>
<p>If your project is based on Maven, you just need to add the Fabric8 Kubernetes Client to your Maven dependencies:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-xml" style="white-space:pre"><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">dependency</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>io.fabric8</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>kubernetes-client</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">version</span><span style="color:#e8bf6a">&gt;</span><span>7.6.1</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">version</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span></span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">dependency</span><span style="color:#e8bf6a">&gt;</span></code></pre></div>
<p>If your project is based on Gradle, you just need to add the Fabric8 Kubernetes Client to your Gradle dependencies:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-groovy" style="white-space:pre"><span>dependencies {
</span><span>  api </span><span style="color:#a5c261">"io.fabric8:kubernetes-client:7.6.1"</span><span>
</span>}</code></pre></div>
<p>Once your project is ready, you can create a new instance of the client to perform operations.
In the following code snippet, I show you how to instantiate the client and retrieve a list of Pods:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-java" style="white-space:pre"><span style="color:#c26230">try</span><span> (KubernetesClient client = </span><span style="color:#c26230">new</span><span> KubernetesClientBuilder().build()) {
</span>  client.pods().list().getItems().forEach(p -&gt; System.out.println(p.getMetadata().getName()));
<!-- -->}</code></pre></div>
<h2 class="heading"><a href="https://blog.marcnuri.com/fabric8-kubernetes-client-7-6#how-can-you-help" aria-label="how-can-you-help permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="how-can-you-help"></span>How can you help?</h2>
<p>If you're interested in helping out and are a first-time contributor, check out
the <a href="https://github.com/fabric8io/kubernetes-client/labels/good%20first%20issue" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/labels/good%20first%20issue" aria-label="&quot;good first issue&quot;" target="_blank">"good first issue"</a> tag in the issue repository.
We've tagged extremely easy issues so that you can get started contributing to Open Source.</p>
<p>We're also excited to read articles and posts mentioning our project and sharing the user experience.
Giving a star to the project, and spreading the word in general, helps us reach more users and broaden the
feedback. Feedback is the only way to improve.</p>
<p><a href="https://github.com/fabric8io/kubernetes-client" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client" aria-label="Project Page" target="_blank">Project Page</a> |
<a href="https://github.com/fabric8io/kubernetes-client/issues" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/issues" aria-label="Issues" target="_blank">Issues</a> |
<a href="https://github.com/fabric8io/kubernetes-client/discussions" rel="noopener" title="Link to https://github.com/fabric8io/kubernetes-client/discussions" aria-label="Discussions" target="_blank">Discussions</a> |
<a href="https://gitter.im/fabric8io/kubernetes-client" rel="noopener" title="Link to https://gitter.im/fabric8io/kubernetes-client" aria-label="Gitter" target="_blank">Gitter</a> |
<a href="https://stackoverflow.com/questions/tagged/fabric8" rel="noopener" title="Link to https://stackoverflow.com/questions/tagged/fabric8" aria-label="Stack Overflow" target="_blank">Stack Overflow</a></p>
<span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png" class="post-image__link" title="The logo of Fabric8 Kubernetes Client"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/eb11f2059915fc28b71c76eff27dc487/a66c2/fabric8-logo.webp 269w,/static/eb11f2059915fc28b71c76eff27dc487/3860d/fabric8-logo.webp 539w,/static/eb11f2059915fc28b71c76eff27dc487/418ed/fabric8-logo.webp 1077w" sizes="(min-width: 1077px) 1077px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 1077px) 1077px, 100vw" decoding="async" loading="lazy" data-src="/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png" data-srcset="/static/eb11f2059915fc28b71c76eff27dc487/53e69/fabric8-logo.png 269w,/static/eb11f2059915fc28b71c76eff27dc487/f0c2e/fabric8-logo.png 539w,/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png 1077w" alt="The logo of Fabric8 Kubernetes Client" src="https://blog.marcnuri.com/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png" srcset="https://blog.marcnuri.com/static/eb11f2059915fc28b71c76eff27dc487/53e69/fabric8-logo.png 269w,https://blog.marcnuri.com/static/eb11f2059915fc28b71c76eff27dc487/f0c2e/fabric8-logo.png 539w,https://blog.marcnuri.com/static/eb11f2059915fc28b71c76eff27dc487/539a4/fabric8-logo.png 1077w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure>
  ]]></content:encoded>
            <category>Cloud Native</category>
            <enclosure url="https://blog.marcnuri.com/static/b9dbe50d753954e05cf28f5e3aa1c88a/baaed/fabric8.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[AI Coding Agent Dashboard: Orchestrating Claude Code Across Devices]]></title>
            <link>https://blog.marcnuri.com/ai-coding-agent-dashboard</link>
            <guid isPermaLink="false">https://blog.marcnuri.com/ai-coding-agent-dashboard</guid>
            <pubDate>Mon, 23 Feb 2026 18:00:00 GMT</pubDate>
            <description><![CDATA[Learn how I built a real-time dashboard to monitor and orchestrate multiple AI coding agents running in parallel across projects and devices.]]></description>
            <content:encoded><![CDATA[
    <div><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard">Original post</a></div>
    <h2 class="heading"><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard#introduction" aria-label="introduction permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="introduction"></span>Introduction</h2>
<p>In December 2025 I wrote about <a class="post-link " title="Boosting My Developer Productivity with AI in 2025" href="/boosting-developer-productivity-ai-2025">becoming a coding orchestrator</a>, shifting my role from implementer to someone who directs multiple <a class="tag-link " title="AI Agent" aria-label="AI coding agents" href="/tag/ai-agent">AI coding agents</a> working in parallel.
The productivity gains were real, but so was the chaos.</p>
<p>Running 5 to 10 Claude Code sessions across multiple machines, different projects, and various git branches quickly became unmanageable.
I'd forget about sessions, lose track of what was running where, and sometimes only rediscover abandoned work hours or days later.</p>
<p>Terminal tabs and tmux solve local organization.
They do not solve cross-device visibility.
I needed a single view of everything, so I <a href="https://github.com/manusa/ai-beacon" rel="noopener" title="Link to https://github.com/manusa/ai-beacon" aria-label="built a dashboard" target="_blank">built a dashboard</a>.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard#the-problem-orchestrating-without-visibility" aria-label="the-problem-orchestrating-without-visibility permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="the-problem-orchestrating-without-visibility"></span>The Problem: Orchestrating Without Visibility</h2>
<p>My daily setup involves a MacBook for on-the-go work and a Linux workstation for heavier tasks.
On any given day, I might have Claude Code sessions running across both machines, each tackling different tasks in different projects and branches.</p>
<p>The cognitive overhead of tracking which agent is doing what, on which machine, adds up fast.
You have to remember which terminal window maps to which project, which branch each agent is working on, whether a session is still active or has stalled, and whether the agent is waiting for permission or has already finished.</p>
<p>Without a centralized view, the orchestrator workflow I described in my <a class="post-link " title="Boosting My Developer Productivity with AI in 2025" href="/boosting-developer-productivity-ai-2025">productivity post</a> breaks down.
You lose the parallelism advantage that makes AI-assisted development so powerful.</p>
<p>Existing tools only solve parts of this problem.
Tmux and terminal multiplexers work great on a single machine, but they don't span devices.
IDE-based solutions are tied to a specific editor.
What I needed was a tool that could aggregate information from all my machines and present it in a single, real-time view.</p>
<p>As I discussed in <a class="post-link " title="The Future of Developer Tools: Adapting to Machine-Based Developers" href="/the-future-of-developer-tools-machine-based-developers">The Future of Developer Tools</a>, our tools need to adapt to AI-agent workflows.
This dashboard is one example of what that adaptation looks like in practice.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard#context-switching-and-cognitive-load" aria-label="context-switching-and-cognitive-load permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="context-switching-and-cognitive-load"></span>The Real Challenge: Context Switching and Cognitive Load</h2>
<p>One thing that doesn't get discussed enough is that the biggest bottleneck in AI-assisted parallel development isn't the AI itself.
<strong>It's the human</strong>.</p>
<p>When you're orchestrating multiple agents, the cognitive load of context switching between sessions becomes the primary constraint.
Which agent was working on what?
Did that session finish or stall?
Is the agent waiting for my input?
Every time you have to answer these questions by opening terminals and inspecting state manually, you pay a context-switching tax that eats into the productivity gains you're trying to achieve.</p>
<p>This is the core challenge of the orchestrator era: <strong>managing the mental overhead of parallel work</strong>.
Any tool that claims to support AI-assisted development needs to address this head-on.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard#the-dashboard-what-it-does" aria-label="the-dashboard-what-it-does permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="the-dashboard-what-it-does"></span>The Dashboard: What It Does</h2>
<span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/bc3b8c8feb9da0aacb9adc01963f55a9/7192e/dashboard-overview.png" class="post-image__link" title="AI Coding Agent Dashboard overview showing sessions organized by device"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/bc3b8c8feb9da0aacb9adc01963f55a9/4b64d/dashboard-overview.webp 458w,/static/bc3b8c8feb9da0aacb9adc01963f55a9/3ca0d/dashboard-overview.webp 916w,/static/bc3b8c8feb9da0aacb9adc01963f55a9/01aaf/dashboard-overview.webp 1832w" sizes="(min-width: 1832px) 1832px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 1832px) 1832px, 100vw" decoding="async" loading="lazy" data-src="/static/bc3b8c8feb9da0aacb9adc01963f55a9/7192e/dashboard-overview.png" data-srcset="/static/bc3b8c8feb9da0aacb9adc01963f55a9/00c70/dashboard-overview.png 458w,/static/bc3b8c8feb9da0aacb9adc01963f55a9/9bf80/dashboard-overview.png 916w,/static/bc3b8c8feb9da0aacb9adc01963f55a9/7192e/dashboard-overview.png 1832w" alt="AI Coding Agent Dashboard overview showing sessions organized by device" src="https://blog.marcnuri.com/static/bc3b8c8feb9da0aacb9adc01963f55a9/7192e/dashboard-overview.png" srcset="https://blog.marcnuri.com/static/bc3b8c8feb9da0aacb9adc01963f55a9/00c70/dashboard-overview.png 458w,https://blog.marcnuri.com/static/bc3b8c8feb9da0aacb9adc01963f55a9/9bf80/dashboard-overview.png 916w,https://blog.marcnuri.com/static/bc3b8c8feb9da0aacb9adc01963f55a9/7192e/dashboard-overview.png 1832w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure>
<p>The dashboard provides a real-time overview of all my active coding agent sessions across every device and project.</p>
<p>Sessions are organized by device, so I can immediately see what's running on my MacBook versus my workstation.
Each session is represented by a card that displays key information at a glance:</p>
<ul>
<li><strong>Project and git branch</strong>: Which repository and branch the agent is working on.</li>
<li><strong>Pull Request link</strong>: If the agent has created or is working on a PR, a direct link is shown.</li>
<li><strong>Model and context usage</strong>: Which <a class="tag-link " title="LLM" aria-label="LLM" href="/tag/llm">LLM</a> model the session is using and how much of the context window has been consumed.</li>
<li><strong>Status</strong>: Whether the agent is actively working, idle, or waiting for user permission.</li>
<li><strong>Task description</strong>: A summary of what the agent is currently doing.</li>
<li><strong>MCP servers</strong>: Which <a class="post-link " title="Introduction to the Model Context Protocol (MCP): The Future of AI Integration" href="/model-context-protocol-mcp-introduction">Model Context Protocol</a> servers are connected to the session.</li>
</ul>
<div style="max-width:400px;margin:0 auto"><span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/2e517de3ea8ebf0070ac54343a6d75c7/eca67/session-card-detail.png" class="post-image__link" title="Close-up of a session card showing project, branch, model, context usage, and status"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/2e517de3ea8ebf0070ac54343a6d75c7/f2825/session-card-detail.webp 199w,/static/2e517de3ea8ebf0070ac54343a6d75c7/0c980/session-card-detail.webp 397w,/static/2e517de3ea8ebf0070ac54343a6d75c7/43896/session-card-detail.webp 794w" sizes="(min-width: 794px) 794px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 794px) 794px, 100vw" decoding="async" loading="lazy" data-src="/static/2e517de3ea8ebf0070ac54343a6d75c7/eca67/session-card-detail.png" data-srcset="/static/2e517de3ea8ebf0070ac54343a6d75c7/88b5f/session-card-detail.png 199w,/static/2e517de3ea8ebf0070ac54343a6d75c7/5d600/session-card-detail.png 397w,/static/2e517de3ea8ebf0070ac54343a6d75c7/eca67/session-card-detail.png 794w" alt="Close-up of a session card showing project, branch, model, context usage, and status" src="https://blog.marcnuri.com/static/2e517de3ea8ebf0070ac54343a6d75c7/eca67/session-card-detail.png" srcset="https://blog.marcnuri.com/static/2e517de3ea8ebf0070ac54343a6d75c7/88b5f/session-card-detail.png 199w,https://blog.marcnuri.com/static/2e517de3ea8ebf0070ac54343a6d75c7/5d600/session-card-detail.png 397w,https://blog.marcnuri.com/static/2e517de3ea8ebf0070ac54343a6d75c7/eca67/session-card-detail.png 794w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure></div>
<p>The dashboard updates in real time as agents report their state.
It uses this stream of information to detect stale or crashed sessions automatically.
If an agent stops reporting for too long, the session card reflects that something may have gone wrong.</p>
<p>By externalizing this state, the dashboard directly attacks the context-switching problem I described above.
Instead of cycling through terminal windows to reconstruct what's happening, I can glance at the dashboard and know the state of every agent instantly.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard#remote-terminal-attachment" aria-label="remote-terminal-attachment permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="remote-terminal-attachment"></span>Remote Terminal Attachment</h2>
<p>The feature that changed everything for me is the ability to connect to any agent session directly from the browser.</p>
<p>I've seen developers set up elaborate solutions to achieve cross-device terminal access: Tailscale VPNs, SSH tunneling from their phones, complex tmux configurations with nested sessions, and custom scripts to keep everything in sync.
These approaches work, but they add layers of friction and configuration that become their own maintenance burden.</p>
<span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/3d50657667ae2381bbb22da456755d6f/302b5/terminal-attachment.png" class="post-image__link" title="Browser-based terminal attached to a remote coding agent session"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/3d50657667ae2381bbb22da456755d6f/d052c/terminal-attachment.webp 414w,/static/3d50657667ae2381bbb22da456755d6f/28ff6/terminal-attachment.webp 828w,/static/3d50657667ae2381bbb22da456755d6f/9ed7e/terminal-attachment.webp 1656w" sizes="(min-width: 1656px) 1656px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 1656px) 1656px, 100vw" decoding="async" loading="lazy" data-src="/static/3d50657667ae2381bbb22da456755d6f/302b5/terminal-attachment.png" data-srcset="/static/3d50657667ae2381bbb22da456755d6f/9451b/terminal-attachment.png 414w,/static/3d50657667ae2381bbb22da456755d6f/22d71/terminal-attachment.png 828w,/static/3d50657667ae2381bbb22da456755d6f/302b5/terminal-attachment.png 1656w" alt="Browser-based terminal attached to a remote coding agent session" src="https://blog.marcnuri.com/static/3d50657667ae2381bbb22da456755d6f/302b5/terminal-attachment.png" srcset="https://blog.marcnuri.com/static/3d50657667ae2381bbb22da456755d6f/9451b/terminal-attachment.png 414w,https://blog.marcnuri.com/static/3d50657667ae2381bbb22da456755d6f/22d71/terminal-attachment.png 828w,https://blog.marcnuri.com/static/3d50657667ae2381bbb22da456755d6f/302b5/terminal-attachment.png 1656w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure>
<p>The dashboard takes a different approach.
When I click on a session, I get a live terminal embedded right in the browser, attached to that agent's session.
I can read the output, provide input, approve permissions, or intervene if the agent goes off track.
No SSH setup, no VPN, no terminal app on my phone.
Just a browser and the same interface everywhere.</p>
<p>This works from any device: my laptop, my phone, a tablet, or any machine with a web browser.
During lunch, I can check on a long-running session from my phone.
On the couch in the evening, I can review what an agent produced during the day from a tablet.
The UX is the same everywhere because the interface is always the same web application.</p>
<p>On touch devices, an extra-keys toolbar (Esc, Ctrl, Tab, arrows) and clipboard bridging make the embedded terminal genuinely usable from a phone or tablet.</p>
<p>Security was a primary design concern from the start.
This runs in my own environment with authenticated device registration and scoped access, so the goal is convenience without exposing raw terminal access publicly.</p>
<div><iframe src="https://www.youtube.com/embed/CY70iLBwbrg" class="aligncenter " width="560" height="315" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></div>
<h2 class="heading"><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard#spawning-sessions-remotely" aria-label="spawning-sessions-remotely permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="spawning-sessions-remotely"></span>Spawn, Prime, Notify: Productive From Anywhere</h2>
<p>If remote terminal attachment is the feature that keeps me connected, the next three are what keep me productive from anywhere.
Each one removes a different friction point that shows up the moment you try to run several agents in parallel without sitting in front of the machine.</p>
<div style="max-width:600px;margin:0 auto"><span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/b6327da9b4333177e0fa93e1c8524df5/178c1/spawn-session.png" class="post-image__link" title="New session dialog for spawning a coding agent on a remote device"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/b6327da9b4333177e0fa93e1c8524df5/86c34/spawn-session.webp 300w,/static/b6327da9b4333177e0fa93e1c8524df5/e82c4/spawn-session.webp 600w,/static/b6327da9b4333177e0fa93e1c8524df5/a842b/spawn-session.webp 1200w" sizes="(min-width: 1200px) 1200px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 1200px) 1200px, 100vw" decoding="async" loading="lazy" data-src="/static/b6327da9b4333177e0fa93e1c8524df5/178c1/spawn-session.png" data-srcset="/static/b6327da9b4333177e0fa93e1c8524df5/8fe8f/spawn-session.png 300w,/static/b6327da9b4333177e0fa93e1c8524df5/59f64/spawn-session.png 600w,/static/b6327da9b4333177e0fa93e1c8524df5/178c1/spawn-session.png 1200w" alt="New session dialog for spawning a coding agent on a remote device" src="https://blog.marcnuri.com/static/b6327da9b4333177e0fa93e1c8524df5/178c1/spawn-session.png" srcset="https://blog.marcnuri.com/static/b6327da9b4333177e0fa93e1c8524df5/8fe8f/spawn-session.png 300w,https://blog.marcnuri.com/static/b6327da9b4333177e0fa93e1c8524df5/59f64/spawn-session.png 600w,https://blog.marcnuri.com/static/b6327da9b4333177e0fa93e1c8524df5/178c1/spawn-session.png 1200w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure></div>
<h3 class="heading"><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard#spawning-sessions-and-worktrees" aria-label="spawning-sessions-and-worktrees permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="spawning-sessions-and-worktrees"></span>Spawning Sessions and Worktrees: Parallel Without Stepping on Each Other</h3>
<p>From the dashboard, I can start a new Claude Code session on any of my registered devices.
I pick a device, select a project from its available repositories, and a fresh agent session spins up in a new tmux window on that machine.
The dashboard picks it up immediately and starts tracking it.
This means I can kick off agent sessions while commuting, then sit down later to review the results, and distribute work across machines based on what each one is best suited for.</p>
<p>The piece that turns this from a convenience into something I rely on is the <strong>worktree picker</strong>.
For the chosen project, the spawn dialog lists the existing git worktrees on that device and lets me either attach a new agent to one of them, or type a fresh branch name to spin up a brand-new worktree on the fly.
Branch names are validated, so a typo never wedges the working tree.
This matters because at five or ten parallel agents on the same repository, the bottleneck is no longer compute. The real cost is <em>agents stepping on each other</em>.
Worktrees give every agent its own isolated filesystem, and the picker turns that into a one-click decision instead of a context switch into the terminal.</p>
<div><iframe src="https://www.youtube.com/embed/CzenyaZ4k3o" class="aligncenter " width="560" height="315" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></div>
<h3 class="heading"><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard#workflow-templates" aria-label="workflow-templates permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="workflow-templates"></span>Workflow Templates: Priming, Not Just Spawning</h3>
<p>Spawning a fresh session in the right place is half the problem.
The other half is <em>what the agent does once it is there</em>.</p>
<p>Workflow templates are opinionated, built-in prompts that prime a new session for a specific task instead of dropping the agent at a blank prompt.
Two ship today: <strong>Implement Issue</strong> and <strong>Review PR</strong>.
For <em>Implement Issue</em>, I paste a GitHub issue URL; the dialog parses repo and issue number, spawns a session in an <code>issue-&lt;number&gt;</code> worktree, and labels it <code>#&lt;number&gt; Implement</code>.
For <em>Review PR</em>, the same thing happens with a PR URL, optionally in a <code>review-&lt;number&gt;</code> worktree, labeled <code>#&lt;number&gt; Review</code>.</p>
<p>Each template embeds engineering-discipline guardrails I would otherwise paste from a snippets file every time: interview before you assume, write a failing test first, verify before claiming success, hand the diff to a peer-review pass.
The agent starts from a strong baseline rather than from whatever I happened to type into the spawn box during a coffee break.
Templates are configurable per-deployment via <code>config.toml</code>, and embedders can replace them entirely with their own prompts.</p>
<h3 class="heading"><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard#push-notifications" aria-label="push-notifications permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="push-notifications"></span>Push Notifications: The Loop Closes Back to You</h3>
<p>Async delegation only works if the agent can find you when it needs to.
Without that, you are back to polling the dashboard every few minutes, which defeats the point.</p>
<p>The dashboard registers a service worker that fires browser notifications on three transitions: <strong>Needs you</strong> when an agent is blocked waiting for human input, <strong>PR opened</strong> when a session has just produced a pull request, and <strong>Done</strong> when a session moves from working to idle.
Clicking a notification deep-links straight to the relevant session card, so I am already where I need to act.
Because they go through a service worker, the notifications survive the dashboard tab being closed, so I do not have to leave a browser window open all day to be paged.
Awaiting-permission alerts beep because they actually need a human; the rest are silent so they pile up without nagging.
The agents pull me back in only when there is something to do.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard#architecture-overview" aria-label="architecture-overview permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="architecture-overview"></span>How It Works: Architecture Overview</h2>
<p>Rather than walking through every implementation detail, I want to highlight the high-level architecture and the design decisions that make this system flexible.</p>
<p><span><svg aria-roledescription="flowchart-v2" role="graphics-document document" viewBox="0 0 819.3125 242" style="max-width: 819.312px; background-color: transparent;" class="flowchart" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="100%" id="mermaid-48"><style>#mermaid-48{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#ccc;}#mermaid-48 .error-icon{fill:#a44141;}#mermaid-48 .error-text{fill:#ddd;stroke:#ddd;}#mermaid-48 .edge-thickness-normal{stroke-width:1px;}#mermaid-48 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-48 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-48 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-48 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-48 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-48 .marker{fill:lightgrey;stroke:lightgrey;}#mermaid-48 .marker.cross{stroke:lightgrey;}#mermaid-48 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-48 p{margin:0;}#mermaid-48 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#ccc;}#mermaid-48 .cluster-label text{fill:#F9FFFE;}#mermaid-48 .cluster-label span{color:#F9FFFE;}#mermaid-48 .cluster-label span p{background-color:transparent;}#mermaid-48 .label text,#mermaid-48 span{fill:#ccc;color:#ccc;}#mermaid-48 .node rect,#mermaid-48 .node circle,#mermaid-48 .node ellipse,#mermaid-48 .node polygon,#mermaid-48 .node path{fill:#1f2020;stroke:#ccc;stroke-width:1px;}#mermaid-48 .rough-node .label text,#mermaid-48 .node .label text,#mermaid-48 .image-shape .label,#mermaid-48 .icon-shape .label{text-anchor:middle;}#mermaid-48 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-48 .rough-node .label,#mermaid-48 .node .label,#mermaid-48 .image-shape .label,#mermaid-48 .icon-shape .label{text-align:center;}#mermaid-48 .node.clickable{cursor:pointer;}#mermaid-48 .root .anchor path{fill:lightgrey!important;stroke-width:0;stroke:lightgrey;}#mermaid-48 .arrowheadPath{fill:lightgrey;}#mermaid-48 .edgePath .path{stroke:lightgrey;stroke-width:2.0px;}#mermaid-48 .flowchart-link{stroke:lightgrey;fill:none;}#mermaid-48 .edgeLabel{background-color:hsl(0, 0%, 34.4117647059%);text-align:center;}#mermaid-48 .edgeLabel p{background-color:hsl(0, 0%, 34.4117647059%);}#mermaid-48 .edgeLabel rect{opacity:0.5;background-color:hsl(0, 0%, 34.4117647059%);fill:hsl(0, 0%, 34.4117647059%);}#mermaid-48 .labelBkg{background-color:rgba(87.75, 87.75, 87.75, 0.5);}#mermaid-48 .cluster rect{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:rgba(255, 255, 255, 0.25);stroke-width:1px;}#mermaid-48 .cluster text{fill:#F9FFFE;}#mermaid-48 .cluster span{color:#F9FFFE;}#mermaid-48 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(20, 1.5873015873%, 12.3529411765%);border:1px solid rgba(255, 255, 255, 0.25);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-48 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#ccc;}#mermaid-48 rect.text{fill:none;stroke-width:0;}#mermaid-48 .icon-shape,#mermaid-48 .image-shape{background-color:hsl(0, 0%, 34.4117647059%);text-align:center;}#mermaid-48 .icon-shape p,#mermaid-48 .image-shape p{background-color:hsl(0, 0%, 34.4117647059%);padding:2px;}#mermaid-48 .icon-shape rect,#mermaid-48 .image-shape rect{opacity:0.5;background-color:hsl(0, 0%, 34.4117647059%);fill:hsl(0, 0%, 34.4117647059%);}#mermaid-48 .edgeLabel p{padding:3px 6px;}#mermaid-48 .flowchartTitleText{color:#333;fill:#333;font-size:1.5rem;}#mermaid-48 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
  /* blog.marcnuri.com - Custom mermaid overrides */
  /* Make arrows darker for better visibility on light backgrounds */
  .edgePath path, .flowchart-link { stroke: #666 !important; stroke-width: 1.5px !important; }
  .marker path { fill: #666 !important; stroke: #666 !important; }
  .arrowMarkerPath { fill: #666 !important; }
</style><g><marker orient="auto" markerHeight="8" markerWidth="8" markerUnits="userSpaceOnUse" refY="5" refX="5" viewBox="0 0 10 10" class="marker flowchart-v2" id="mermaid-48_flowchart-v2-pointEnd"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 0 L 10 5 L 0 10 z"></path></marker><marker orient="auto" markerHeight="8" markerWidth="8" markerUnits="userSpaceOnUse" refY="5" refX="4.5" viewBox="0 0 10 10" class="marker flowchart-v2" id="mermaid-48_flowchart-v2-pointStart"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 5 L 10 10 L 10 0 z"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="11" viewBox="0 0 10 10" class="marker flowchart-v2" id="mermaid-48_flowchart-v2-circleEnd"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="-1" viewBox="0 0 10 10" class="marker flowchart-v2" id="mermaid-48_flowchart-v2-circleStart"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="12" viewBox="0 0 11 11" class="marker cross flowchart-v2" id="mermaid-48_flowchart-v2-crossEnd"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="-1" viewBox="0 0 11 11" class="marker cross flowchart-v2" id="mermaid-48_flowchart-v2-crossStart"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path marker-end="url(#mermaid-48_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Phone_Dashboard_0" d="M158.719,64L168.628,64C178.536,64,198.354,64,218.664,67.931C238.973,71.862,259.775,79.724,270.175,83.655L280.576,87.586"></path><path marker-end="url(#mermaid-48_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Tablet_Dashboard_1" d="M156.789,178L167.02,178C177.25,178,197.711,178,218.342,174.069C238.973,170.138,259.775,162.276,270.175,158.345L280.576,154.414"></path><path marker-end="url(#mermaid-48_flowchart-v2-pointEnd)" marker-start="url(#mermaid-48_flowchart-v2-pointStart)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Dashboard_Device1_2" d="M449.631,87.464L463.845,81.553C478.058,75.643,506.486,63.821,532.462,57.911C558.438,52,581.961,52,593.723,52L605.484,52"></path><path marker-end="url(#mermaid-48_flowchart-v2-pointEnd)" marker-start="url(#mermaid-48_flowchart-v2-pointStart)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Dashboard_Device2_3" d="M449.631,154.536L463.845,160.447C478.058,166.357,506.486,178.179,532.462,184.089C558.438,190,581.961,190,593.723,190L605.484,190"></path></g><g class="edgeLabels"><g transform="translate(218.171875, 64)" class="edgeLabel"><g transform="translate(-34.453125, -15)" class="label"><foreignObject height="30" width="68.90625"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"><p>browser</p></span></div></foreignObject></g></g><g transform="translate(218.171875, 178)" class="edgeLabel"><g transform="translate(-34.453125, -15)" class="label"><foreignObject height="30" width="68.90625"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"><p>browser</p></span></div></foreignObject></g></g><g transform="translate(534.9140625, 52)" class="edgeLabel"><g transform="translate(-49.5703125, -27)" class="label"><foreignObject height="54" width="99.140625"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"><p>heartbeat <br> terminal I/O&nbsp;</p></span></div></foreignObject></g></g><g transform="translate(534.9140625, 190)" class="edgeLabel"><g transform="translate(-49.5703125, -27)" class="label"><foreignObject height="54" width="99.140625"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"><p>heartbeat <br> terminal I/O&nbsp;</p></span></div></foreignObject></g></g></g><g class="nodes"><g transform="translate(83.359375, 64)" id="flowchart-Phone-0" class="node default"><rect height="64" width="150.71875" y="-32" x="-75.359375" style="" class="basic label-container"></rect><g transform="translate(-35.359375, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="70.71875"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>📱 Phone</p></span></div></foreignObject></g></g><g transform="translate(83.359375, 178)" id="flowchart-Tablet-1" class="node default"><rect height="64" width="146.859375" y="-32" x="-73.4296875" style="" class="basic label-container"></rect><g transform="translate(-33.4296875, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="66.859375"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>💻 Tablet</p></span></div></foreignObject></g></g><g transform="translate(368.984375, 121)" id="flowchart-Dashboard-3" class="node default"><rect height="64" width="182.71875" y="-32" x="-91.359375" style="" class="basic label-container"></rect><g transform="translate(-51.359375, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="102.71875"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>📊 Dashboard</p></span></div></foreignObject></g></g><g transform="translate(710.3984375, 52)" id="flowchart-Device1-7" class="node default"><rect height="88" width="201.828125" y="-44" x="-100.9140625" style="" class="basic label-container"></rect><g transform="translate(-60.9140625, -24)" style="" class="label"><rect></rect><foreignObject height="48" width="121.828125"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>🖥️ Device 1<br>Agents · Projects</p></span></div></foreignObject></g></g><g transform="translate(710.3984375, 190)" id="flowchart-Device2-9" class="node default"><rect height="88" width="201.828125" y="-44" x="-100.9140625" style="" class="basic label-container"></rect><g transform="translate(-60.9140625, -24)" style="" class="label"><rect></rect><foreignObject height="48" width="121.828125"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>⚙️ Device 2<br>Agents · Projects</p></span></div></foreignObject></g></g></g></g></g></svg></span></p>
<p>The system follows a <strong>heartbeat model</strong>.
Each coding agent session reports its state to the dashboard backend at regular intervals.
This report includes project information, git status, context usage, active <a class="post-link " title="Introduction to the Model Context Protocol (MCP): The Future of AI Integration" href="/model-context-protocol-mcp-introduction">MCP</a> servers, and the agent's current task.</p>
<p>The heartbeat data is gathered through <strong>coding agent hooks</strong>.
In the case of Claude Code, these are notification hooks that fire when the agent transitions between states (working, idle, awaiting permission).
The hooks execute a lightweight script that posts the session state to the dashboard API.</p>
<p>For terminal attachment, the backend establishes a <strong>WebSocket relay</strong> between the browser and the remote machine's terminal session.
Since the agent and the human connect to the same underlying session, they can interact with it simultaneously.</p>
<p>The design decision I'm happiest with is the <strong>enricher pattern</strong>.
The raw data coming from agent hooks is passed through a chain of enrichers, each responsible for extracting or deriving specific information.
For example, one enricher parses the agent's transcript to extract model name, token usage, and context percentage.
Another enricher detects PR URLs from the git branch.</p>
<p>This pattern is what makes the system <strong>extensible and agent-agnostic in the core dashboard</strong>, with agent-specific hooks and enrichers handling the adaptation layer.
The dashboard doesn't need to know the internals of any particular coding agent.
It only needs a hook script that sends heartbeats and an enricher that knows how to interpret the raw data.
Supporting a new CLI agent, whether <a class="post-link " title="Introducing Goose, the on-machine AI agent" href="/goose-on-machine-ai-agent-cli-introduction">Goose</a>, Gemini CLI, or any future tool, is mostly a matter of writing a new hook and a new enricher.
The rest of the system remains unchanged.</p>
<p>The same composability applies to <strong>authentication</strong>.
A pluggable auth layer lets the dashboard run with a zero-config pre-shared key on a personal laptop, or scale up to browser password auth, SSH challenge-response, or OIDC and per-user API tokens for team deployments.
The goal is to adapt to each user's environment rather than force a single model.</p>
<p>Beyond standalone use, the <a href="https://github.com/manusa/ai-beacon" rel="noopener" title="Link to https://github.com/manusa/ai-beacon" aria-label="dashboard is designed to be embeddable" target="_blank">dashboard is designed to be embeddable</a>.
I'm already integrating it into my personal automation platform, and the same plugin and auth interfaces that keep the core agent-agnostic also keep it tool-agnostic — it can live inside a larger system instead of replacing one.</p>
<p>A quick note on scope: this is currently optimized for <strong>live orchestration and intervention</strong>, not long-term analytics.
The focus is real-time visibility and control across devices.
I can add historical views later, but the immediate win was reducing the cost of checking and steering active sessions.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard#what-ive-learned" aria-label="what-ive-learned permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="what-ive-learned"></span>What I've Learned Running This</h2>
<p>After several weeks of daily use, a few insights stand out.</p>
<p><strong>Visibility reduces cognitive load dramatically.</strong>
The single biggest improvement isn't any specific feature.
It's simply being able to see everything at once.
Before the dashboard, I spent mental energy tracking session states in my head.
Now that state is externalized, I can focus on higher-level decisions like what to assign next and which results to review first.</p>
<p><strong>Context percentage is the most actionable metric.</strong>
Of all the fields on the dashboard, context usage has been the best predictor of where I should look next.
When an agent is running high on context, it usually means I need to review progress, reset with a fresh session, or prepare for handoff.</p>
<p><strong>PR awareness shortens the review loop.</strong>
Seeing which sessions have produced pull requests, with direct links, lets me move from orchestration to review immediately instead of hunting through <a class="tag-link " title="GitHub" aria-label="GitHub" href="/tag/github">GitHub</a> notifications.</p>
<p><strong>Remote access changes when and where you can be productive.</strong>
This sounds obvious in retrospect, but actually experiencing it is different from theorizing about it.
Being able to start a session from my phone during a coffee break and review the results from my workstation an hour later has added genuinely productive moments to parts of the day that were previously dead time.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard#whats-next" aria-label="whats-next permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="whats-next"></span>What's Next</h2>
<p>The dashboard is functional and I use it daily.
The roadmap is driven by one goal: <strong>reducing the need to leave the dashboard for anything</strong>, while keeping the core flexible enough to support very different users and workflows.</p>
<p>Short-term, the focus is on features that eliminate round-trips to other tools:</p>
<ul>
<li><strong>Coding agent settings management</strong>: Configure agent behavior directly from the dashboard instead of editing configuration files on each machine.</li>
<li><strong>File attachment</strong>: Send files, context documents, or reference material to an agent session without switching to a terminal.</li>
<li><strong>Code diff viewer</strong>: Review the changes an agent has made without opening an IDE, closing a major gap in the orchestration workflow.</li>
<li><strong>Desktop application</strong>: A native desktop build for users who prefer a dedicated app over a browser tab.</li>
</ul>
<p>On the extensibility front, <strong>support for additional coding agents</strong> is a natural next step.
The enricher pattern I described above was designed with this in mind.
As more CLI-based coding agents emerge, the dashboard should be able to aggregate sessions from all of them into the same unified view, regardless of which agent is running underneath.
Beyond agent variety, <strong>integration with skills frameworks like <a class="post-link " title="Superpowers: The Claude Code Skills Framework Shipped as Markdown" href="/superpowers-claude-code-skills-framework">Superpowers</a></strong> is another extensibility direction I am watching: the dashboard tracks <em>what</em> each agent is doing, while a skills framework shapes <em>how</em> it does the work.</p>
<p>For team and multi-user scenarios, <strong>OIDC authentication and per-user API tokens</strong> are on the way, so the same tool that works zero-config on a personal laptop also fits shared deployments.</p>
<del>Most importantly, I'm seriously considering open-sourcing the dashboard, or at least the key components: the hook scripts, the heartbeat protocol, and the enricher framework. These pieces could be useful to anyone building similar orchestration workflows, and I'd love to see what others do with them.</del>
<p>Given the interest the project has already generated in the community, <strong>I'll be <a href="https://github.com/manusa/ai-beacon" rel="noopener" title="Link to https://github.com/manusa/ai-beacon" aria-label="releasing the source publicly" target="_blank">releasing the source publicly</a></strong>.
Beyond making it available to others, I want to use it as a way to research how developers actually adapt to AI-assisted workflows — what they orchestrate, what they intervene on, what they ignore — and let that shape where the tool goes next.</p>
<p>If you're running parallel coding agents and have thoughts on what would make a tool like this more useful, <a href="https://github.com/manusa/ai-beacon/discussions" rel="noopener" title="Link to https://github.com/manusa/ai-beacon/discussions" aria-label="I'd genuinely love to hear from you" target="_blank">I'd genuinely love to hear from you</a>.
Whether it's a feature idea, a different approach to the same problem, or just your experience managing multiple agents, reach out.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/ai-coding-agent-dashboard#conclusion" aria-label="conclusion permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="conclusion"></span>Conclusion</h2>
<p>The shift from implementer to orchestrator that I described in my <a class="post-link " title="Boosting My Developer Productivity with AI in 2025" href="/boosting-developer-productivity-ai-2025">productivity post</a> requires different tools.
Standard terminals and IDEs were not built for one developer managing a fleet of AI agents across multiple machines.</p>
<p>This dashboard is my current answer to that gap.
It gives me visibility, control, and a consistent way to monitor, intervene, and start work from anywhere.</p>
<p>Developers who learn to orchestrate <a class="category-link " title="Artificial Intelligence: Everything related to artificial intelligence (AI) and machine learning (ML)" aria-label="AI" href="/category/ai">AI</a> agents effectively will have a real advantage in the next era of software engineering.
And the tools we build today to support that orchestration, however rough around the edges, will shape what that era looks like.</p>
<p>I'm still iterating on this, and I'd welcome feedback, feature suggestions, and stories from anyone tackling similar challenges.</p>
  ]]></content:encoded>
            <category>Artificial Intelligence</category>
            <enclosure url="https://blog.marcnuri.com/static/f6e7a03c8e367dfa85de34c8b4e87fd1/818f3/ai-coding-agent-dashboard.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Eclipse JKube 1.19 is now available!]]></title>
            <link>https://blog.marcnuri.com/eclipse-jkube-1-19</link>
            <guid isPermaLink="false">https://blog.marcnuri.com/eclipse-jkube-1-19</guid>
            <pubDate>Tue, 10 Feb 2026 14:00:00 GMT</pubDate>
            <description><![CDATA[Eclipse JKube 1.19 is available! Check out the major changes and learn how you can contribute.]]></description>
            <content:encoded><![CDATA[
    <div><a href="https://blog.marcnuri.com/eclipse-jkube-1-19">Original post</a></div>
    <div class="admonition admonition__note"><p class="admonition-title"><i class="admonition-title-icon fa-solid fa-circle-info"></i>Note</p><div class="admonition-content">A newer version of Eclipse JKube is available, jump to <a class="post-link " title="Eclipse JKube 1.20 is now available!" href="/eclipse-jkube-1-20">Eclipse JKube 1.20 announcement</a>.</div></div>
<p>On behalf of the <a class="post-link " title="Eclipse JKube introduction: Java tools and plugins for Kubernetes and OpenShift" href="/eclipse-jkube-introduction-kubernetes-openshift">Eclipse JKube</a>
team and everyone who has contributed, I'm happy to announce that Eclipse JKube <code>1.19.0</code> has been
<a href="https://github.com/eclipse-jkube/jkube/releases/tag/v1.19.0" rel="noopener" title="Link to https://github.com/eclipse-jkube/jkube/releases/tag/v1.19.0" aria-label="released" target="_blank">released</a> and is now available from
<a href="https://repo1.maven.org/maven2/org/eclipse/jkube/kubernetes-maven-plugin/1.19.0/" rel="noopener" title="Link to https://repo1.maven.org/maven2/org/eclipse/jkube/kubernetes-maven-plugin/1.19.0/" aria-label="Maven Central" target="_blank">Maven Central</a> 🎉.</p>
<p>Thanks to all of you who have contributed with issue reports, pull requests, feedback, and spreading the word
with blogs, videos, comments, and so on.
We really appreciate your help, keep it up!</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-19#whats-new" aria-label="whats-new permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="whats-new"></span>What's new?</h2>
<p>Without further ado, let's have a look at the most significant updates:</p>
<ul>
<li><a class="post-link " title="Link to the Spring Boot section" href="/eclipse-jkube-1-19#spring-boot">Improved Spring Boot health probes configuration</a></li>
<li><a class="post-link " title="Link to the ECR authentication section" href="/eclipse-jkube-1-19#ecr-auth">ECR registry authentication with AWS SDK v2</a></li>
<li><a class="post-link " title="Link to the Ingress section" href="/eclipse-jkube-1-19#ingress">IngressClassName support for Ingress resources</a></li>
<li>Updated base images from UBI 8 to UBI 9</li>
<li>Reduced dependencies (Guava removal)</li>
<li>🐛 Many other bug-fixes and minor improvements</li>
</ul>
<h3 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-19#spring-boot" aria-label="spring-boot permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="spring-boot"></span>Improved Spring Boot health probes configuration</h3>
<p>This release brings several improvements for the Spring Boot health probes configuration:</p>
<ul>
<li>JKube now uses the correct <code>management.endpoint.health.probes.enabled</code> property. The previous property (<code>management.health.probes.enabled</code>) was deprecated in Spring Boot 2.3.2.</li>
<li>Added support for <code>server.ssl.enabled</code> and <code>management.server.ssl.enabled</code> properties to enable liveness/readiness probes for Spring Boot Actuator. This allows for easier environment-specific SSL configuration.</li>
</ul>
<h3 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-19#ecr-auth" aria-label="ecr-auth permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="ecr-auth"></span>ECR registry authentication with AWS SDK v2</h3>
<p>JKube now supports Amazon ECR registry authentication using AWS SDK Java v2.
This update ensures compatibility with the latest AWS SDK and provides a more robust authentication mechanism
when pushing images to Amazon Elastic Container Registry.</p>
<h3 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-19#ingress" aria-label="ingress permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="ingress"></span>IngressClassName support for Ingress resources</h3>
<p>The <code>IngressClassName</code> field is now supported in the <code>NetworkingV1IngressGenerator</code>.
This is essential for Kubernetes environments with multiple ingress controllers, allowing you to specify which ingress controller should handle your Ingress resources.</p>
<h2 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-19#using-this-release" aria-label="using-this-release permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="using-this-release"></span>Using this release</h2>
<p>If your project is based on Maven, you just need to add the Kubernetes Maven plugin or the OpenShift Maven
plugin to your plugin dependencies:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-xml" style="white-space:pre"><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">plugin</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>org.eclipse.jkube</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">groupId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>kubernetes-maven-plugin</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">artifactId</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span>  </span><span style="color:#e8bf6a">&lt;</span><span style="color:#e8bf6a">version</span><span style="color:#e8bf6a">&gt;</span><span>1.19.0</span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">version</span><span style="color:#e8bf6a">&gt;</span><span>
</span><span></span><span style="color:#e8bf6a">&lt;/</span><span style="color:#e8bf6a">plugin</span><span style="color:#e8bf6a">&gt;</span></code></pre></div>
<p>If your project is based on Gradle, you just need to add the Kubernetes Gradle plugin or the OpenShift Gradle
plugin to your plugin dependencies:</p>
<div class="code" style="background:#232323"><pre style="display:block;overflow-x:auto;padding:0.5em;background:#232323;color:#e6e1dc"><code class="language-groovy" style="white-space:pre"><span>plugins {
</span><span>  id </span><span style="color:#a5c261">'org.eclipse.jkube.kubernetes'</span><span> version </span><span style="color:#a5c261">'1.19.0'</span><span>
</span>}</code></pre></div>
<h2 class="heading"><a href="https://blog.marcnuri.com/eclipse-jkube-1-19#how-can-you-help" aria-label="how-can-you-help permalink" class="anchor"><i class="anchor__link fa-solid fa-link"></i></a><span id="how-can-you-help"></span>How can you help?</h2>
<p>If you're interested in helping out and are a first-time contributor, check out the
<a href="https://github.com/eclipse-jkube/jkube/labels/first-timers-only" rel="noopener" title="Link to https://github.com/eclipse-jkube/jkube/labels/first-timers-only" aria-label="&quot;first-timers-only&quot;" target="_blank">"first-timers-only"</a>
tag in the issue repository.
We've tagged extremely easy issues so that you can get started contributing to Open Source and the Eclipse organization.</p>
<p>If you are a more experienced developer or have already contributed to JKube, check the
<a href="https://github.com/eclipse-jkube/jkube/labels/help%20wanted" rel="noopener" title="Link to https://github.com/eclipse-jkube/jkube/labels/help%20wanted" aria-label="&quot;help wanted&quot;" target="_blank">"help wanted"</a> tag.</p>
<p>We're also excited to read articles and posts mentioning our project and sharing the user experience.
Feedback is the only way to improve.</p>
<p><a href="https://www.eclipse.org/jkube" rel="noopener" title="Link to https://www.eclipse.org/jkube" aria-label="Project Page" target="_blank">Project Page</a> |
<a href="https://github.com/eclipse-jkube/jkube" rel="noopener" title="Link to https://github.com/eclipse-jkube/jkube" aria-label="GitHub" target="_blank">GitHub</a> |
<a href="https://github.com/eclipse-jkube/jkube/issues" rel="noopener" title="Link to https://github.com/eclipse-jkube/jkube/issues" aria-label="Issues" target="_blank">Issues</a> |
<a href="https://gitter.im/eclipse/jkube" rel="noopener" title="Link to https://gitter.im/eclipse/jkube" aria-label="Gitter" target="_blank">Gitter</a> |
<a href="https://accounts.eclipse.org/mailing-list/jkube-dev" rel="noopener" title="Link to https://accounts.eclipse.org/mailing-list/jkube-dev" aria-label="Mailing list" target="_blank">Mailing list</a> |
<a href="https://stackoverflow.com/questions/tagged/jkube" rel="noopener" title="Link to https://stackoverflow.com/questions/tagged/jkube" aria-label="Stack Overflow" target="_blank">Stack Overflow</a></p>
<span class="post-image__pusher "></span><figure class="post-image "><span class="post-image__scrim"></span><a href="/static/af6673438e09cd98812665335c800cbf/e5189/JKube-blog-banner.jpg" class="post-image__link" title="The logo of Eclipse JKube"><span class="post-image__image-container"><div data-gatsby-image-wrapper="" class="gatsby-image-wrapper gatsby-image-wrapper-constrained post-image__image "><picture><source type="image/webp" data-srcset="/static/af6673438e09cd98812665335c800cbf/15be0/JKube-blog-banner.webp 200w,/static/af6673438e09cd98812665335c800cbf/f0f61/JKube-blog-banner.webp 400w,/static/af6673438e09cd98812665335c800cbf/eb6ca/JKube-blog-banner.webp 800w" sizes="(min-width: 800px) 800px, 100vw"><img data-gatsby-image-ssr="" data-main-image="" style="opacity: 1;" sizes="(min-width: 800px) 800px, 100vw" decoding="async" loading="lazy" data-src="/static/af6673438e09cd98812665335c800cbf/e5189/JKube-blog-banner.jpg" data-srcset="/static/af6673438e09cd98812665335c800cbf/f8ab9/JKube-blog-banner.jpg 200w,/static/af6673438e09cd98812665335c800cbf/a4161/JKube-blog-banner.jpg 400w,/static/af6673438e09cd98812665335c800cbf/e5189/JKube-blog-banner.jpg 800w" alt="The logo of Eclipse JKube" src="https://blog.marcnuri.com/static/af6673438e09cd98812665335c800cbf/e5189/JKube-blog-banner.jpg" srcset="https://blog.marcnuri.com/static/af6673438e09cd98812665335c800cbf/f8ab9/JKube-blog-banner.jpg 200w,https://blog.marcnuri.com/static/af6673438e09cd98812665335c800cbf/a4161/JKube-blog-banner.jpg 400w,https://blog.marcnuri.com/static/af6673438e09cd98812665335c800cbf/e5189/JKube-blog-banner.jpg 800w"></picture><script type="module">const t="undefined"!=typeof HTMLImageElement&&"loading"in HTMLImageElement.prototype;if(t){const t=document.querySelectorAll("img[data-main-image]");for(let e of t){e.dataset.src&&(e.setAttribute("src",e.dataset.src),e.removeAttribute("data-src")),e.dataset.srcset&&(e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset"));const t=e.parentNode.querySelectorAll("source[data-srcset]");for(let e of t)e.setAttribute("srcset",e.dataset.srcset),e.removeAttribute("data-srcset");e.complete&&(e.style.opacity=1,e.parentNode.parentNode.querySelector("[data-placeholder-image]").style.opacity=0)}}</script></div></span></a></figure>
  ]]></content:encoded>
            <category>Cloud Native</category>
            <enclosure url="https://blog.marcnuri.com/static/af6673438e09cd98812665335c800cbf/e5189/JKube-blog-banner.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>