<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Richard Okonicha</title>
    <description>The latest articles on DEV Community by Richard Okonicha (@konichar).</description>
    <link>https://dev.to/konichar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F418426%2Fea346827-06ab-4ce9-b3b1-78252c784862.jpeg</url>
      <title>DEV Community: Richard Okonicha</title>
      <link>https://dev.to/konichar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/konichar"/>
    <language>en</language>
    <item>
      <title>Deterministic Agentic Governance: Bringing Order to Autonomous Software Engineering</title>
      <dc:creator>Richard Okonicha</dc:creator>
      <pubDate>Mon, 17 Aug 2026 09:14:04 +0000</pubDate>
      <link>https://dev.to/konichar/deterministic-agentic-governance-bringing-order-to-autonomous-software-engineering-1dn8</link>
      <guid>https://dev.to/konichar/deterministic-agentic-governance-bringing-order-to-autonomous-software-engineering-1dn8</guid>
      <description>&lt;h1&gt;
  
  
  Deterministic Agentic Governance: Bringing Order to Autonomous Software Engineering
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;How we built a framework that makes AI agents reliable enough for production&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Agents Don't Play Nice at Scale
&lt;/h2&gt;

&lt;p&gt;You've seen the demo. An AI agent writes a feature, runs tests, commits to git. Magic.&lt;/p&gt;

&lt;p&gt;Then you try it on a real codebase. Ten tasks in, the agent hallucinates an API. Fifteen tasks in, it forgets the architectural constraints you gave it in the first prompt. Twenty tasks in, you're manually reverting broken commits and wondering why you didn't just write the code yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The math is brutal.&lt;/strong&gt; If each step has a 95% success rate (optimistic), a 20-step task chain succeeds only 36% of the time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;P(success) = 0.95^n

n=5   → 77%
n=10  → 60%
n=20  → 36%
n=50  → 8%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is &lt;strong&gt;compositional decay&lt;/strong&gt;—the exponential failure rate of chaining probabilistic operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Current Approaches Fail
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Context Rot
&lt;/h3&gt;

&lt;p&gt;Every conversation turn adds to the LLM's context window. After 20 turns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initial instructions get diluted (attention thinning)&lt;/li&gt;
&lt;li&gt;Error logs pollute the context (cascade pollution)&lt;/li&gt;
&lt;li&gt;The agent "forgets" constraints it was given initially&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. No Objective Verification
&lt;/h3&gt;

&lt;p&gt;Asking an LLM to review its own code is like asking a student to grade their own exam. Hallucinated "looks good to me" responses are the norm.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. No Safety Boundaries
&lt;/h3&gt;

&lt;p&gt;Agents with shell access can &lt;code&gt;rm -rf /&lt;/code&gt;, commit secrets, or open PRs to production branches.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Human-in-the-Loop Doesn't Scale
&lt;/h3&gt;

&lt;p&gt;Manual approval for every step kills velocity. But no approval means broken code in main.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Insight: Treat the LLM as a CPU, Not a Brain
&lt;/h2&gt;

&lt;p&gt;What if we stop treating the LLM as an autonomous decision-maker and start treating it as a &lt;strong&gt;stateless processing unit&lt;/strong&gt;—like a CPU?&lt;/p&gt;

&lt;p&gt;The CPU doesn't manage memory, enforce permissions, or decide which program runs next. The &lt;strong&gt;operating system kernel&lt;/strong&gt; does that.&lt;/p&gt;

&lt;p&gt;So we built a &lt;strong&gt;governance kernel&lt;/strong&gt; around the LLM.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Three Pillars of Deterministic Governance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pillar 1: Persistent State Ledger (PROGRESS.json)
&lt;/h3&gt;

&lt;p&gt;Instead of stuffing task state into the LLM's context, we externalize it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"projectName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"electron-to-tauri-migration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tasks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TASK-003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DATA_STORAGE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"referencePath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"electron/store/index.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"targetPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src-tauri/src/storage/mod.rs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PENDING"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"retryCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maxRetries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"verificationLogs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Every agent turn gets a fresh, clean context&lt;/strong&gt; containing only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The single task specification&lt;/li&gt;
&lt;li&gt;The reference code (read-only)&lt;/li&gt;
&lt;li&gt;Acceptance criteria&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No history. No error logs from previous tasks. No instruction dilution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pillar 2: Declarative Permission Sandboxing
&lt;/h3&gt;

&lt;p&gt;Before any code runs, the governance kernel enforces boundaries via policy-as-code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"readOnlyPaths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"src/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"package.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cargo.toml"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"writeOnlyPaths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"src/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tests/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/**"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"forbiddenPaths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;".git/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".env*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*.key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"secrets/**"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allowedCommands"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"npm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cargo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"forbiddenCommands"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"rm -rf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sudo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"curl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wget"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ssh"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"networkAccess"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxExecutionTimeMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxMemoryMb"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't a suggestion. It's enforced at the OS level via environment variables and command validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pillar 3: Compiler Verification Gates
&lt;/h3&gt;

&lt;p&gt;No LLM self-evaluation. Only binary, deterministic gates:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gate&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TYPE_CHECK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;tsc --noEmit&lt;/code&gt;, &lt;code&gt;cargo check&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Type safety &amp;amp; compilation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SYNTAX_CHECK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;prettier --check&lt;/code&gt;, &lt;code&gt;cargo fmt --check&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Formatting &amp;amp; syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AST_LINT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;eslint&lt;/code&gt;, &lt;code&gt;cargo clippy&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Static analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ANTI_STUB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Regex/AST patterns&lt;/td&gt;
&lt;td&gt;Detect &lt;code&gt;TODO&lt;/code&gt;, &lt;code&gt;unimplemented!()&lt;/code&gt;, empty blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TEST_SUITE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;npm test&lt;/code&gt;, &lt;code&gt;cargo test&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Functional correctness&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;If any gate fails, the task is retried with the compiler error fed back to a fresh agent context.&lt;/strong&gt; After &lt;code&gt;maxRetries&lt;/code&gt;, the workspace is git-reset to the last clean commit.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Governance Loop
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nf"&gt;pending_tasks_exist&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_next_pending&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;base_commit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxRetries&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset_hard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_commit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Clean slate
&lt;/span&gt;            &lt;span class="n"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment_retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Fresh context, single task
&lt;/span&gt;        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_clean_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reference_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="c1"&gt;# Objective verification
&lt;/span&gt;        &lt;span class="n"&gt;verification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_all_gates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;verification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all_passed&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_completed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_verification_failures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verification&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;verification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all_passed&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_failed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset_hard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_commit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Zero context rot. Deterministic verification. Atomic commits. Full audit trail.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-World Example: Electron → Tauri 2.0 Migration
&lt;/h2&gt;

&lt;p&gt;We migrated a production desktop app (100k+ lines) from Electron/Node to Tauri 2.0 (Rust + React + SQLite).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10 coordinated tasks:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Verification Gates&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tauri main.rs entry point&lt;/td&gt;
&lt;td&gt;INFRASTRUCTURE&lt;/td&gt;
&lt;td&gt;TYPE, SYNTAX, LINT, ANTI_STUB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IPC layer replacement&lt;/td&gt;
&lt;td&gt;INFRASTRUCTURE&lt;/td&gt;
&lt;td&gt;TYPE, SYNTAX, LINT, ANTI_STUB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid SQLite + Markdown storage&lt;/td&gt;
&lt;td&gt;DATA_STORAGE&lt;/td&gt;
&lt;td&gt;TYPE, SYNTAX, LINT, ANTI_STUB, TEST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-blocking token streaming (10Hz)&lt;/td&gt;
&lt;td&gt;LLM_INFERENCE&lt;/td&gt;
&lt;td&gt;TYPE, SYNTAX, LINT, ANTI_STUB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React virtualization (@tanstack/react-virtual)&lt;/td&gt;
&lt;td&gt;UI_COMPONENT&lt;/td&gt;
&lt;td&gt;TYPE, SYNTAX, LINT, ANTI_STUB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integration tests&lt;/td&gt;
&lt;td&gt;TESTING&lt;/td&gt;
&lt;td&gt;TYPE, SYNTAX, LINT, TEST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build config &amp;amp; documentation&lt;/td&gt;
&lt;td&gt;DOCUMENTATION&lt;/td&gt;
&lt;td&gt;SYNTAX&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 tasks failed TYPE_CHECK on first attempt → auto-retried with compiler errors → passed&lt;/li&gt;
&lt;li&gt;1 task failed ANTI_STUB (empty function body) → auto-retried → passed&lt;/li&gt;
&lt;li&gt;All 10 committed atomically with structured messages&lt;/li&gt;
&lt;li&gt;Full audit trail in PROGRESS.json + git history&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Enterprise Governance: Three-Tier Authorization
&lt;/h2&gt;

&lt;p&gt;Not all actions are equal. The framework enforces a tiered model:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Actions&lt;/th&gt;
&lt;th&gt;Approval&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Autonomous&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Read code, create branches, run type checks, format files&lt;/td&gt;
&lt;td&gt;Pre-approved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Semi-Autonomous&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open PRs, modify schemas, update dependencies&lt;/td&gt;
&lt;td&gt;Human sign-off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Forbidden&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commit to protected branches, export secrets, unsandboxed network&lt;/td&gt;
&lt;td&gt;Hard-blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Compliance alignment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ISO/IEC 42001 (AI management systems)&lt;/li&gt;
&lt;li&gt;NIST AI RMF (Risk management)&lt;/li&gt;
&lt;li&gt;IEEE 7000 (Ethical system design)&lt;/li&gt;
&lt;li&gt;SFIA 9 (Competency framework)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Supported Agents
&lt;/h2&gt;

&lt;p&gt;The framework is agent-agnostic. Built-in adapters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Claude Code (Anthropic)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;claude-code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;claude&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-p&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--bare&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--dangerously-skip-permissions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Aider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aider&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aider&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--no-git&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--yes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// OpenCode&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;opencode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;opencode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;run&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--headless&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Kilo Code&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kilo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kilo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;run&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--headless&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Custom - implement AgentAdapter interface&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;deterministic-agentic-governance

&lt;span class="c"&gt;# Initialize in your workspace&lt;/span&gt;
npx govern init &lt;span class="nt"&gt;--workspace&lt;/span&gt; ./my-project &lt;span class="nt"&gt;--project&lt;/span&gt; my-migration

&lt;span class="c"&gt;# Define tasks (JSON)&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; tasks.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
[{
  "id": "TASK-001",
  "category": "UI_COMPONENT",
  "referencePath": "src/components/OldButton.tsx",
  "targetPath": "src/components/NewButton.tsx",
  "description": "Migrate button to new design system",
  "acceptanceCriteria": ["TypeScript compiles", "No stubs", "Passes ESLint"],
  "dependencies": [],
  "maxRetries": 3,
  "timeoutMs": 180000,
  "metadata": {}
}]
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Add and run&lt;/span&gt;
npx govern add &lt;span class="nt"&gt;--file&lt;/span&gt; tasks.json
npx govern run
npx govern status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  When to Use This (And When Not To)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Ideal For
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Large-scale migrations (framework ports, language rewrites)&lt;/li&gt;
&lt;li&gt;Automated refactoring (design systems, strict TypeScript, dead code)&lt;/li&gt;
&lt;li&gt;Cross-platform code generation (OpenAPI → clients, schema → ORM)&lt;/li&gt;
&lt;li&gt;Compliance-required environments (fintech, healthcare, defense)&lt;/li&gt;
&lt;li&gt;CI/CD automation (dependency updates, security patches)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Not Ideal For
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Quick prototyping → Use Cursor/Claude Code directly&lt;/li&gt;
&lt;li&gt;Single-file edits → IDE copilot&lt;/li&gt;
&lt;li&gt;Exploratory coding → Unconstrained agent&lt;/li&gt;
&lt;li&gt;Learning → Notebook/REPL&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Key Differentiator
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Every task either passes all compiler/linter gates and commits atomically, or rolls back cleanly with full diagnostic capture. No context rot. No hallucinated verifications. No partial broken states.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Architecture Diagram
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────┐
│                     GOVERNANCE ORCHESTRATOR                      │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐              │
│  │   LEDGER    │  │  SANDBOX    │  │ VERIFICATION│              │
│  │  MANAGER    │  │  ENFORCER   │  │ ORCHESTRATOR│              │
│  └─────────────┘  └─────────────┘  └─────────────┘              │
│         │               │                │                       │
│         ▼               ▼                ▼                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                    AGENT ADAPTERS                        │    │
│  │  ┌──────────┐ ┌────────┐ ┌──────────┐ ┌──────────────┐  │    │
│  │  │Claude Code│ │ Aider  │ │ OpenCode │ │   Kilo Code  │  │    │
│  │  └──────────┘ └────────┘ └──────────┘ └──────────────┘  │    │
│  └─────────────────────────────────────────────────────────┘    │
│         │                                                       │
│         ▼                                                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                     GIT MANAGER                          │    │
│  │  Atomic commits • Auto-rollback • Branch management      │    │
│  └─────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm:&lt;/strong&gt; &lt;code&gt;npm install deterministic-agentic-governance&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/richardokonicha/deterministic-agentic-governance" rel="noopener noreferrer"&gt;https://github.com/richardokonicha/deterministic-agentic-governance&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation:&lt;/strong&gt; See README.md for full API reference&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example:&lt;/strong&gt; &lt;code&gt;examples/electron-to-tauri/&lt;/code&gt; — complete 10-task migration&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;The industry is shifting from &lt;em&gt;writing code&lt;/em&gt; to &lt;em&gt;governing agents&lt;/em&gt;. But governance without determinism is just chaos with better marketing.&lt;/p&gt;

&lt;p&gt;This framework proves that you can have &lt;strong&gt;autonomous execution with deterministic guarantees&lt;/strong&gt;. The LLM does what it's good at (code generation from context). The governance kernel does what it's good at (state, permissions, verification, rollback).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The result: AI agents you can actually trust in production.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Published as part of the Deterministic Agentic Governance Framework v1.0.0. MIT License.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>governance</category>
      <category>ai</category>
      <category>softwareengineering</category>
      <category>typescript</category>
    </item>
    <item>
      <title>How To Set Up Kubernetes on AMD GPUs with ROCm</title>
      <dc:creator>Richard Okonicha</dc:creator>
      <pubDate>Sat, 08 Aug 2026 02:59:11 +0000</pubDate>
      <link>https://dev.to/konichar/how-to-set-up-kubernetes-on-amd-gpus-with-rocm-31l0</link>
      <guid>https://dev.to/konichar/how-to-set-up-kubernetes-on-amd-gpus-with-rocm-31l0</guid>
      <description>&lt;h1&gt;
  
  
  How To Set Up Kubernetes on AMD GPUs with ROCm
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Running Kubernetes on AMD GPU hardware is possible, but the path is not as well documented as the NVIDIA equivalent. The ROCm stack, the device plugin, the metrics exporter, and the container runtime each have their own quirks — and on AMD DevCloud, Docker and k3s use separate containerd instances, which breaks the normal image-pull flow.&lt;/p&gt;

&lt;p&gt;This tutorial walks you through provisioning an AMD MI300X instance with ROCm preinstalled, installing a single-node k3s cluster, importing the ROCm image into k3s containerd, deploying the AMD device plugin and metrics exporter, and verifying that Kubernetes can schedule pods onto the GPU. You will also see how to validate the gRPC health path between the device plugin and the metrics exporter, patch a known &lt;code&gt;rocm-smi --rasinject&lt;/code&gt; CLI bug, and recover from a stale kubelet checkpoint that blocks GPU scheduling.&lt;/p&gt;

&lt;p&gt;When you are finished, you will have a working ROCm-on-Kubernetes cluster that you can use as a test substrate for GPU workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access to an AMD DevCloud account with the ability to create GPU instances. You can request access through the &lt;a href="https://www.amd.com/en/developers/resources/amd-devcloud.html" rel="noopener noreferrer"&gt;AMD Developer website&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A local macOS or Linux machine with &lt;code&gt;kubectl&lt;/code&gt; and &lt;code&gt;docker&lt;/code&gt; installed and SSH access configured. You can install &lt;code&gt;kubectl&lt;/code&gt; by following the &lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/" rel="noopener noreferrer"&gt;Kubernetes documentation&lt;/a&gt; and Docker by following the &lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;Docker documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Familiarity with Kubernetes concepts such as DaemonSets, device plugins, and feature gates. If you are new to Kubernetes, the &lt;a href="https://kubernetes.io/docs/tutorials/kubernetes-basics/" rel="noopener noreferrer"&gt;Kubernetes Basics&lt;/a&gt; guide provides a helpful starting point.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1 — Provisioning the AMD MI300X Infrastructure
&lt;/h2&gt;

&lt;p&gt;Start by creating a droplet on AMD DevCloud that provides a single MI300X GPU with ROCm preinstalled. The &lt;code&gt;gpu-mi300x1-192gb-devcloud&lt;/code&gt; plan gives you 192GB of host memory and the ROCm software stack under &lt;code&gt;/opt/rocm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you create the droplet through the DevCloud web UI, select the &lt;code&gt;rocm-7-14&lt;/code&gt; image and attach your SSH key. The provisioning process takes roughly 90 seconds. Once the droplet is ready, SSH into it and verify the baseline ROCm installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rocm-smi &lt;span class="nt"&gt;--showid&lt;/span&gt; &lt;span class="nt"&gt;--showproduct&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;rocm-smi&lt;/code&gt; tool reports the GPU model, GFX architecture, and ROCm version. On a fresh MI300X droplet you should see output like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output
GPU[0]  : Device Name: AMD Instinct MI300X VF
GPU[0]  : GFX Version: gfx942
ROCm: /opt/rocm -&amp;gt; /opt/rocm-7.0.2
rocm-smi: ROCM-SMI version 4.0.0+2b22ab01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If these values match your droplet, the hardware and driver stack are ready.&lt;/p&gt;

&lt;p&gt;Next, confirm that RAS (Reliability, Availability, and Serviceability) blocks are enabled with zero errors at baseline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rocm-smi &lt;span class="nt"&gt;--showrasinfo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output
GPU[0]  : RAS UMC: Enabled, 0 correctable, 0 uncorrectable
GPU[0]  : RAS SDMA: Enabled, 0 correctable, 0 uncorrectable
GPU[0]  : RAS GFX: Enabled, 0 correctable, 0 uncorrectable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;UMC&lt;/code&gt;, &lt;code&gt;SDMA&lt;/code&gt;, and &lt;code&gt;GFX&lt;/code&gt; must show &lt;code&gt;Enabled&lt;/code&gt; with &lt;code&gt;0&lt;/code&gt; correctable and uncorrectable errors. A clean baseline matters because these blocks are what you will use to measure hardware health during testing.&lt;/p&gt;

&lt;p&gt;You have now verified the GPU hardware and baseline ROCm installation. Next, you will install Kubernetes and configure it for GPU workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Installing and Configuring k3s
&lt;/h2&gt;

&lt;p&gt;DevCloud droplets ship with both Docker and k3s installed, but they use separate containerd instances. Because k3s cannot see images pulled by Docker, you must transfer the ROCm image from Docker into k3s containerd after disabling Docker. For this reason, stop and disable Docker before installing Kubernetes:&lt;/p&gt;

&lt;p&gt;Execute the following commands to stop Docker and prevent it from restarting on boot. The &lt;code&gt;systemctl disable&lt;/code&gt; command ensures Docker does not interfere with k3s containerd:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl stop docker
systemctl disable docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install &lt;code&gt;k3s&lt;/code&gt;, a lightweight Kubernetes distribution that is well-suited for single-node clusters:&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;curl -sfL&lt;/code&gt; flags follow redirects silently, fail on HTTP errors, and show errors. The &lt;code&gt;INSTALL_K3S_SKIP_START=true&lt;/code&gt; environment variable tells the installer to skip starting k3s so you can configure it first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sfL&lt;/span&gt; https://get.k3s.io | &lt;span class="nv"&gt;INSTALL_K3S_SKIP_START&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;sh -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the installation script completes, import the ROCm Docker image into k3s containerd so that the k3s runtime can use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker save rocm:latest &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/rocm-latest.tar
ctr &lt;span class="nt"&gt;-n&lt;/span&gt; k8s.io &lt;span class="nt"&gt;--address&lt;/span&gt; /run/k3s/containerd/containerd.sock images import /tmp/rocm-latest.tar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command saves the image from the Docker runtime, and the second command imports it into the &lt;code&gt;k8s.io&lt;/code&gt; namespace of k3s containerd. Without this step, pods that request &lt;code&gt;rocm:latest&lt;/code&gt; fail with &lt;code&gt;ImagePullBackOff&lt;/code&gt; because k3s cannot access Docker's image store. This is not a theoretical problem — it is the first thing that breaks on a fresh DevCloud droplet.&lt;/p&gt;

&lt;p&gt;Next, configure &lt;code&gt;/etc/rancher/k3s/config.yaml&lt;/code&gt; to enable the feature gates and privileges that the AMD device plugin requires:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;kubelet-arg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;feature-gates=ResourceHealthStatus=true"&lt;/span&gt;
&lt;span class="na"&gt;allow-privileged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ResourceHealthStatus=true&lt;/code&gt; enables kubelet to surface per-device health in pod status. &lt;code&gt;allow-privileged=true&lt;/code&gt; is required because the AMD device plugin needs access to &lt;code&gt;/dev/kfd&lt;/code&gt;, &lt;code&gt;/dev/dri&lt;/code&gt;, and &lt;code&gt;/sys&lt;/code&gt;, along with the &lt;code&gt;SYS_ADMIN&lt;/code&gt; capability for the health-check variant.&lt;/p&gt;

&lt;p&gt;Start the k3s services and wait for the node to reach the &lt;code&gt;Ready&lt;/code&gt; state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl start k3s
kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a single control-plane node in the &lt;code&gt;Ready&lt;/code&gt; state running k3s v1.36.3 or later. Your node name, age, and exact IP will differ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output
NAME                                                 STATUS   ROLES           AGE   VERSION
rocm-7-14-software-gpu-mi300x1-192gb-devcloud-atl1   Ready    control-plane   6h49m   v1.36.3+k3s1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the cluster running, you will now deploy the metrics exporter that exposes health data to Prometheus and the device plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Configuring the Metrics Exporter
&lt;/h2&gt;

&lt;p&gt;The AMD Device Metrics Exporter (&lt;code&gt;rocm/device-metrics-exporter:v1.5.0&lt;/code&gt;) exposes Prometheus metrics and a gRPC health service on each node. Open a file named &lt;code&gt;metrics-exporter.yaml&lt;/code&gt; in your text editor and add the following DaemonSet and ConfigMap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metrics-exporter-config&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kube-system&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;exporter-config.yaml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;CommonConfig:&lt;/span&gt;
      &lt;span class="s"&gt;HealthService:&lt;/span&gt;
        &lt;span class="s"&gt;Enable: true&lt;/span&gt;
        &lt;span class="s"&gt;PollingRate: "10s"&lt;/span&gt;
      &lt;span class="s"&gt;Debug:&lt;/span&gt;
        &lt;span class="s"&gt;EnableAPI: true&lt;/span&gt;
&lt;span class="s"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DaemonSet&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amd-metrics-exporter&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kube-system&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amd-metrics-exporter&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amd-metrics-exporter&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;exporter&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rocm/device-metrics-exporter:v1.5.0&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./device-metrics-exporter"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--config"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/etc/metrics/exporter-config.yaml"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AMD_ENABLE_HEALTH_GRPC&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AMD_DEBUG_ENABLE_API&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
        &lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;privileged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;socket-dir&lt;/span&gt;
          &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/amd-metrics-exporter&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config&lt;/span&gt;
          &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/metrics&lt;/span&gt;
          &lt;span class="na"&gt;readOnly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;socket-dir&lt;/span&gt;
        &lt;span class="na"&gt;hostPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/amd-metrics-exporter&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DirectoryOrCreate&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config&lt;/span&gt;
        &lt;span class="na"&gt;configMap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metrics-exporter-config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ConfigMap enables the &lt;code&gt;gpu_health&lt;/code&gt; metric at a 10-second polling interval and turns on the debug gRPC API. The DaemonSet runs the exporter in privileged mode, mounts the host directory where the gRPC socket will be created, and passes the config via mounted volume. We use &lt;code&gt;rocm/device-metrics-exporter:v1.5.0&lt;/code&gt; because the &lt;code&gt;latest&lt;/code&gt; tag on Docker Hub does not exist for this image.&lt;/p&gt;

&lt;p&gt;Apply the manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; metrics-exporter.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify that the exporter is reachable on the node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:5000/metrics | &lt;span class="nb"&gt;grep &lt;/span&gt;gpu_health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a metric line similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output
gpu_health{card_model="",container="",gpu_compute_partition_type="spx",gpu_id="0",gpu_memory_partition_type="nps1",gpu_partition_id="NA",hostname="your_node_name",job_id="",job_partition="",job_user="",namespace="",pod="",serial_number=""} 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A value of &lt;code&gt;1&lt;/code&gt; indicates &lt;code&gt;Healthy&lt;/code&gt;. The exporter also exposes a gRPC socket at &lt;code&gt;/var/lib/amd-metrics-exporter/amdgpu_device_metrics_exporter_grpc.socket&lt;/code&gt;, which the device plugin uses to read health updates.&lt;/p&gt;

&lt;p&gt;The metrics exporter is now exposing health data. Next, you will deploy the AMD device plugin to advertise the GPU to Kubernetes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Deploying the AMD Device Plugin
&lt;/h2&gt;

&lt;p&gt;The ROCm Kubernetes device plugin repository provides a health-check DaemonSet that exposes per-GPU health through the &lt;code&gt;ListAndWatch&lt;/code&gt; stream. Apply the manifest directly from the upstream repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/ROCm/k8s-device-plugin/master/k8s-ds-amdgpu-dp-health.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This manifest is the experimental health-check variant. It mounts &lt;code&gt;/var/lib/amd-metrics-exporter/&lt;/code&gt; into the device plugin container so the plugin can read the gRPC socket exposed by the metrics exporter. It enables &lt;code&gt;-pulse=10&lt;/code&gt;, which sends a heartbeat every 10 seconds. It requests &lt;code&gt;SYS_ADMIN&lt;/code&gt; capability and mounts &lt;code&gt;/dev/kfd&lt;/code&gt;, &lt;code&gt;/dev/dri&lt;/code&gt;, and &lt;code&gt;/sys&lt;/code&gt; from the host.&lt;/p&gt;

&lt;p&gt;Verify that the DaemonSet pod is running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;amdgpu-dp-ds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see one pod per node in the &lt;code&gt;Running&lt;/code&gt; state with no restarts. If the pod restarts, inspect the logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl logs &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;amdgpu-dp-ds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for lines such as &lt;code&gt;Found 1 AMDGPUs&lt;/code&gt; and &lt;code&gt;Watching GPU with bus ID: 0000:83:00.0&lt;/code&gt;. These confirm that the plugin discovered the GPU and is actively watching it.&lt;/p&gt;

&lt;p&gt;The device plugin is now running and advertising the GPU. Next, you will verify that Kubernetes can schedule pods onto the GPU and that containers inside those pods can access the hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 — Verifying GPU Scheduling and Compute Access
&lt;/h2&gt;

&lt;p&gt;To confirm that the device plugin is advertising the GPU correctly, check the node allocatable resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get node &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.items[0].status.allocatable}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;amd.com/gpu: "1"&lt;/code&gt; alongside CPU, memory, and pod capacity. The full allocatable on a fresh MI300X droplet looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"amd.com/gpu"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cpu"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ephemeral-storage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"710372463469"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hugepages-1Gi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hugepages-2Mi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"memory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"247409204Ki"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pods"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"110"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the GPU is missing from &lt;code&gt;allocatable&lt;/code&gt;, the device plugin has not yet registered; wait a few moments and inspect the DaemonSet logs.&lt;/p&gt;

&lt;p&gt;Next, open a file named &lt;code&gt;rocm-health-test.yaml&lt;/code&gt; in your text editor and add the following pod manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rocm-health-test&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rocm&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rocm:latest&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sleep'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;600'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;amd.com/gpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Never&lt;/span&gt;
  &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Never&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply the manifest and watch the pod schedule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; rocm-health-test.yaml
kubectl get pod rocm-health-test &lt;span class="nt"&gt;-w&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the pod reaches the &lt;code&gt;Running&lt;/code&gt; state, execute a command inside it to verify GPU access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; rocm-health-test &lt;span class="nt"&gt;--&lt;/span&gt; rocm-smi &lt;span class="nt"&gt;--showid&lt;/span&gt; &lt;span class="nt"&gt;--showproduct&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output should show the same GPU model and GFX version that you observed on the host.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output
GPU[0]: Device Name: AMD Instinct MI300X VF
GPU[0]: GFX Version: gfx942
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;rocm-smi&lt;/code&gt; reports &lt;code&gt;No AMD GPUs were detected&lt;/code&gt;, the container is missing access to &lt;code&gt;/dev/kfd&lt;/code&gt; or &lt;code&gt;/dev/dri&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;GPU scheduling and compute access are working correctly. Next, you will validate the gRPC health path to confirm that the device plugin and metrics exporter can communicate across namespace boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6 — Validating the gRPC Health Path
&lt;/h2&gt;

&lt;p&gt;To prove that the device plugin can reach the metrics exporter across namespace boundaries, verify the gRPC socket permissions and test a cross-namespace connection.&lt;/p&gt;

&lt;p&gt;Inspect the socket on the host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; /var/lib/amd-metrics-exporter/amdgpu_device_metrics_exporter_grpc.socket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see permissions &lt;code&gt;0777&lt;/code&gt;. On our cluster, the socket inode is &lt;code&gt;253,1&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output
srwxrwxrwx 1 root root 0 Aug  7 15:24 /var/lib/amd-metrics-exporter/amdgpu_device_metrics_exporter_grpc.socket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The inode must match the bind-mounted path inside the device plugin container. Replace &lt;code&gt;&amp;lt;device-plugin-pod&amp;gt;&lt;/code&gt; with the actual pod name from Step 4:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system &amp;lt;device-plugin-pod&amp;gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-li&lt;/span&gt; /var/lib/amd-metrics-exporter/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output
total 0
253,1 7120801 srwxrwxrwx 1 root root 0 Aug  7 15:24 amdgpu_device_metrics_exporter_grpc.socket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare the inode numbers shown on the host and inside the container. Identical inodes confirm that the mount is the same filesystem object.&lt;/p&gt;

&lt;p&gt;If you want to test a live gRPC connection, you can build a small Go program that connects to the socket and calls the &lt;code&gt;GetGPUState&lt;/code&gt; or &lt;code&gt;List&lt;/code&gt; RPC. Copy the binary into the device plugin container with &lt;code&gt;kubectl cp&lt;/code&gt; and run it. A successful connection proves that network namespace isolation does not block the health path.&lt;/p&gt;

&lt;p&gt;The gRPC path is verified. Next, you will learn how to recover from a stale kubelet checkpoint that blocks GPU scheduling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7 — Recovering from Stale Kubelet State
&lt;/h2&gt;

&lt;p&gt;During testing, a stale kubelet checkpoint can hold a GPU allocation for a deleted pod, causing &lt;code&gt;Insufficient amd.com/gpu&lt;/code&gt; scheduling failures. If you see this error after deleting a pod, the kubelet has not yet released the device back to the pool.&lt;/p&gt;

&lt;p&gt;To recover, force-delete the stale pod directory under &lt;code&gt;/var/lib/kubelet/pods/&amp;lt;uid&amp;gt;&lt;/code&gt;, then restart the device plugin DaemonSet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl rollout restart daemonset amdgpu-device-plugin-daemonset &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the scheduler is still stuck, restart k3s to clear kubelet state entirely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl restart k3s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the restart, the device plugin re-registers and &lt;code&gt;allocatable&lt;/code&gt; returns to &lt;code&gt;1&lt;/code&gt;. This is a known operational issue with the device plugin lifecycle on k3s, and it is the first thing to check when GPU scheduling fails for no obvious reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8 — Patching the rocm-smi --rasinject CLI
&lt;/h2&gt;

&lt;p&gt;The shipped ROCm 7.14 &lt;code&gt;rocm-smi&lt;/code&gt; binary has a bug in its &lt;code&gt;--rasinject&lt;/code&gt; argument parser. The argument is defined with &lt;code&gt;nargs=1&lt;/code&gt; and &lt;code&gt;metavar="BLOCK"&lt;/code&gt;, but the code reads two arguments (block and error type). This means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rocm-smi &lt;span class="nt"&gt;--rasinject&lt;/span&gt; umc ce
&lt;span class="c"&gt;# Error: unrecognized arguments: ce&lt;/span&gt;
rocm-smi &lt;span class="nt"&gt;--rasinject&lt;/span&gt; umc
&lt;span class="c"&gt;# IndexError: list index out of range&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix is to patch &lt;code&gt;/opt/rocm/core-7.14/libexec/rocm_smi/rocm_smi.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before:
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--rasinject&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Inject RAS poison for specified block (ONLY WORKS ON UNSECURED BOARDS)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;metavar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BLOCK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;nargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="c1"&gt;# After:
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--rasinject&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Inject RAS poison for specified block and error type (ONLY WORKS ON UNSECURED BOARDS)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;metavar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BLOCK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ERRTYPE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;nargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the patch, the CLI accepts the correct syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rocm-smi &lt;span class="nt"&gt;--rasinject&lt;/span&gt; umc ce
&lt;span class="c"&gt;# This is experimental feature, use 'amdgpuras' tool for ras error manipulations for newer vbios&lt;/span&gt;
&lt;span class="c"&gt;# WARNING: GPU[%s]  : RAS control is not available&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--help&lt;/code&gt; output now correctly shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[--rasinject BLOCK ERRTYPE]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the MI300X VF firmware on DevCloud droplets does not expose RAS injection (&lt;code&gt;RAS control is not available&lt;/code&gt;), so the CLI accepts the args correctly but the hardware rejects the operation. The patch is still necessary for future hardware or local bare-metal deployments that do expose RAS injection.&lt;/p&gt;

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

&lt;p&gt;In this tutorial, you provisioned an AMD MI300X instance with ROCm 7.14, installed a single-node k3s cluster, imported the ROCm image into k3s containerd, deployed the metrics exporter and AMD device plugin with health-check support, and verified GPU scheduling, compute access, and the gRPC socket end to end. You also learned how to recover from a stale kubelet checkpoint that blocks GPU scheduling and how to patch the &lt;code&gt;rocm-smi --rasinject&lt;/code&gt; CLI parser for future RAS error injection testing.&lt;/p&gt;

&lt;p&gt;The metrics exporter exposes 192 Prometheus metrics, including &lt;code&gt;gpu_health&lt;/code&gt;. The gRPC socket at &lt;code&gt;/var/lib/amd-metrics-exporter/amdgpu_device_metrics_exporter_grpc.socket&lt;/code&gt; is reachable from the device plugin container, and the device plugin heartbeat runs every 10 seconds via &lt;code&gt;-pulse=10&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want to extend this setup, you can deploy a real ML workload by swapping the &lt;code&gt;rocm:latest&lt;/code&gt; image for a framework-specific image such as &lt;code&gt;rocm/tensorflow-installer:latest&lt;/code&gt; or &lt;code&gt;rocm/pytorch:latest&lt;/code&gt;. You can also experiment with DRA device taints (KEP-5055) on a cluster that has the DRA feature gate enabled, or use the patched &lt;code&gt;rocm-smi --rasinject&lt;/code&gt; command on hardware that exposes RAS injection to simulate degraded GPU states.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>amd</category>
      <category>gpu</category>
      <category>rocm</category>
    </item>
  </channel>
</rss>
