• OpenCode 2.0 Migration – Ubuntu 24.04

    OpenCode 2.0 Migration – Ubuntu 24.04

    OpenCode is a free, open-source AI coding agent that runs locally on your machine and connects to over 75 different AI model providers. If you are like me you may use it as the agent/harness for local model inference.

    As of today (or yesterday) OpenCode has shipped version 2.0 (or v2 depending on where you look), which comes with a few nice improvements. Of particular interest to me is the rebuilt API, background sub-agents, and enhanced plugin system (plugins are no longer considered experimental). Lets take a look at what is new in 2.0.

    What’s new

    FeatureWhat’s changedWhy it matters
    Centralized background serviceOne persistent OpenCode service supports the terminal, desktop, web interface, and API clients.Sessions and automation can be managed across interfaces.
    Rebuilt APICompletely redesigned API with a new JavaScript client. Agents can manage sessions, worktrees, and other OpenCode operations.Better scripting and external orchestration.
    Code ModeAgents execute JavaScript to discover, combine, and call MCP tools.Potentially significant reductions in tool-call overhead and context consumption.
    Background subagentsAgents can delegate tasks to other agents, including those running different models.Parallel coding, testing, research, and code reviews.
    Background shell commandsLong-running commands execute asynchronously without blocking conversations.Builds and tests can run while the agent continues working.
    Enhanced plugin systemMore than 80 internal plugins, stable TUI plugins, and hot-reloading.Easier customization without restarting OpenCode.
    Embeddable SDKNew @opencode/sdk package exposes the engine as a library.Build customized agents and integrations using OpenCode’s underlying infrastructure.

    Personally I use switch back and forth between both OpenCode and PI for local LLM work. Both work well, and both can be a bit chatty. In particular PI, which even with Deepseek shares each and every though that is has with you – its a very chatty harness, OpenCode is much less chatty, but a bit less light weight than PI. So lets try OpenCode 2.0 and see how it performs.


    Updating NPM Installed Opencode to Opencode 2.0

    Note that if you installed OpenCode via npn the latest available version is 1.19.0, 2.0 is not available via npm at this time. Here are the steps to preserve your opencode configs (skills/MCPs) and seemlessly migrate to OpenCode 2.0

    Back up the config. Version 1 and version 2 read the same config files, so keep a copy until 2.0 is working

     cp -a ~/.config/opencode ~/.config/opencode.v1-backup
     cp -a ~/.local/share/opencode ~/.local/share/opencode.v1-backup
    
    
    

    Remove the npm opencode. This deletes only the program:

    sudo npm uninstall -g --prefix /usr opencode-ai
    hash -r && which opencode # should print nothing

    Install 2.0 with the official install script. It installs to ~/.opencode/bin/opencode, doesn’t need sudo, and adds that folder to your PATH in ~/.bashrc

    curl -fsSL https://opencode.ai/v2/install | bash
    exec $SHELL
    opencode --version # should report 2.x


    Things to look out for:

    Don’t reinstall opencode-ai from npm, or use the old install script. Both versions use the opencode command, and either one would put 1.x back or clash with 2.0.

    No config changes are needed. 2.0 reads your existing opencode.json and converts the old format in memory without rewriting the file. The guide says converting the file to the new format is optional (for example, mcp. becomes mcp.servers., agent becomes agents, command becomes commands).

    CLAUDE.md is no longer read. Your global AGENTS.md is still read. If any project relies on CLAUDE.md for opencode, copy that guidance into an AGENTS.md.

    Skills might not be picked up. The migration guide only lists .opencode/skills/ folders inside a project; it doesn’t mention ~/.claude/skills or ~/.agents/skills. If your skills don’t appear in step 4, point the skills array in opencode.json at those two folders.

    Plugins from 1.x don’t work in 2.0.

    Personally I did not run into any issues, and did not need to restore my backup configs. Skills loaded, MCPs loaded, no issues thus far.

    More info on OpenCode can be found here.

  • We Told Everyone to Use AI. Then We Saw the Bill

    A humorous cartoon depicting a museum exhibit titled 'Museum of Meaningless Metrics'. Features displays labeled 'Lines of Code', 'Story Points', 'Pull Requests', and 'Tokens Spent'. A guide explains the exhibit to two visitors.

    In early 2026, Meta reportedly created an internal token-spend leaderboard called “Claudeonomics,” ranking employees by how many AI tokens they consumed.

    The predictable result was enormous consumption. When token usage becomes the metric, people optimize for token usage.

    Meta may have been an extreme example, but the underlying idea was everywhere for a while. Across the technology industry, employees were being encouraged to experiment aggressively with AI, agents, automation, and new workflows.

    Use AI.

    That was often the entire strategy.

    Perhaps you had a similar experience. I know I did.


    Riding the Adoption Wave

    I was happy to take my organization up on the offer.

    These were powerful new tools, becoming more capable almost weekly. I wanted to understand them, so I dove in.

    My workflows changed repeatedly. I adopted tools, abandoned others, experimented with models and agent harnesses, learned MCPs and Skills, and started integrating AI into the infrastructure and operational work I was already doing.

    The objective seemed obvious:

    These tools aren’t going away. Learn how to use them.

    For me, that meant leaning even harder into Infrastructure-as-Code and AI-assisted operations. Services became reproducible installs committed to Git, documented, idempotent, and accompanied by health checks and operational tooling.

    AI wasn’t replacing the work. It was changing how I approached the work.

    And for a while, many organizations seemed to want exactly that.

    The mythical “10x engineer” was back, this time carrying a token budget.

    Then somebody saw the bill.


    Coin a Phrase “Token-Min’ing”

    Fast-forward a few months.

    Token budgets were reduced. Monthly quotas appeared. Approved-model lists became smaller. Suddenly the employees who had been encouraged to integrate AI deeply into their workflows were being asked to economize.

    Thus began the age of token-min’ing.

    Tools and techniques appeared specifically to reduce context, shrink prompts, compress agent communication, and squeeze more work out of fewer tokens.

    And that is not inherently bad.

    Frontier models cost real money. Efficient use of them matters. Learning how to accomplish the same task with fewer tokens is itself an important engineering skill.

    But the rapid swing from token-maxxing to token-min’ing raises a more important question:

    What did organizations build while everyone was learning?

    Did they develop internal AI capability?

    Or did they simply develop dependency on external AI providers?

    And if an organization’s AI strategy stops working the moment token budgets are reduced, was it really an AI strategy?


    Adoption Was Never Supposed to Be the Destination

    A robot and a smaller robot stand in a museum, pointing at a display case containing a human brain, with the larger robot explaining its significance.

    The early AI adoption wave should have been a discovery phase.

    Give engineers access. Let them experiment. Find out what works. Learn which workflows generate real value.

    Then turn those discoveries into organizational capability.

    That could mean:

    • shared inference infrastructure;
    • model gateways and routing;
    • internal knowledge and memory systems;
    • RAG;
    • reusable Skills and MCP integrations;
    • standardized agent workflows;
    • security and secrets management;
    • governance and guardrails;
    • selectively hosted internal models;
    • and business-specific automation built around APIs and existing systems.

    Not every company needs racks full of GPUs.

    In many cases, the valuable intellectual property isn’t the model anyway. It is the workflow surrounding the model: the integrations, business logic, knowledge, guardrails, prompts, Skills, agents, and automation that allow employees to solve actual problems.

    Some organizations understood this.

    Others treated AI primarily as another SaaS product to provision.

    That distinction matters now.

    Organizations that used the experimentation period to identify useful workflows can move those capabilities into shared platforms, optimize model selection, route workloads intelligently, and decide which workloads belong on frontier models and which can run elsewhere.

    Organizations that didn’t are left with a much simpler lever:

    Reduce the token budget.


    Employees Have Already Made a Different Decision

    There is another problem.

    The people who successfully incorporated AI into their work aren’t necessarily going backward.

    AI subscriptions are rapidly becoming another part of the professional toolkit — not fundamentally different, from the employee’s perspective, from buying a better monitor, keyboard, IDE, or productivity application.

    I see this in my own experimentation.

    As I write this, I have OpenCode working through custom firmware for an open-source ESP32 watch using a locally hosted model running on an AMD GPU.

    At home I experiment with local inference, different models, agent harnesses, routing, MCPs, Skills, and the infrastructure required to support them.

    I don’t use personal systems for non-personal work, but they are increasingly where I continue learning.

    That creates an uncomfortable problem for employers.

    If employees reach their corporate AI quota and simply move some of their experimentation to personally funded tools, the organization hasn’t eliminated AI usage.

    It may simply have eliminated its visibility into it.

    That is a considerably worse outcome for governance, security, knowledge sharing, and organizational learning.

    Once someone has experienced a meaningful productivity improvement from AI, telling them to stop using it doesn’t necessarily eliminate the demand.

    It can simply make the usage invisible.


    The Incentive to Hide AI Productivity

    Now consider a different problem.

    Suppose an employee discovers a workflow that lets them accomplish eight hours of work in six.

    What incentive do they have to announce that?

    If the likely response is:

    • “Great, here are two more hours of work,”
    • “Maybe we don’t need as many people,”
    • “Everyone should now achieve this output,”
    • or “This is simply the new baseline,”

    then the rational behavior may be to say nothing.

    The employee becomes invisibly more productive.

    They continue delivering good work, but the organization never learns exactly why.

    The employee keeps the workflow.

    The company gets the output.

    And the larger organization loses the opportunity to learn from the person who figured it out.

    That is a terrible trade.


    Harvest the Gain or Reinvest It

    Organizations have a choice in what they do with AI-driven productivity improvements.

    They can harvest the gain, or they can reinvest it.

    Harvesting is easy.

    An employee automates 25% of their workload, so management fills that newly available 25% with more tickets, meetings, projects, and responsibilities.

    The reward for becoming more efficient is a larger workload.

    There is another option.

    Let some of that recovered capacity be reinvested into:

    • finding the next automation opportunity;
    • improving existing workflows;
    • turning personal tooling into reusable organizational tooling;
    • teaching coworkers;
    • creating shared Skills and integrations;
    • documenting successful patterns;
    • experimenting with new use cases;
    • and identifying where additional AI investment would produce the greatest return.

    Consider a team whose job is to catch fish.

    One person discovers better fishing holes, develops better techniques, and builds better lures.

    Management can respond:

    “You catch twice as many fish, so your quota is now twice as high.”

    Or it can ask:

    “Why are you catching twice as many fish, and how do we teach everyone else to do it?”

    The first approach harvests the individual’s productivity.

    The second creates organizational capability.

    And if you consistently choose the first, don’t be surprised when your best fisherman stops telling you where the fishing holes are.ly how all organizations work.


    The Employee Who Automates Their Job May Have Found Their Next Job

    This may be the largest missed opportunity of all.

    Some employees are naturally good at spotting repetitive work, undocumented processes, disconnected systems, and information that should flow automatically but doesn’t.

    AI dramatically increases what those people can do.

    If someone repeatedly automates parts of their own job, the best use of their newly available capacity may not be assigning them more of the same work.

    It may be asking them to automate parts of everyone else’s.

    Those employees can become force multipliers: building integrations, reusable agents, internal platforms, operational tooling, and workflows that turn individual productivity gains into organizational ones.

    That requires organizations to think differently about AI productivity.

    The objective shouldn’t be to maximize tokens.

    It shouldn’t be to minimize tokens either.

    It should be to maximize capability created per token spent.

    The organizations that understand that distinction will come out of this period with more than a collection of AI subscriptions.

    They’ll come out of it with people, platforms, workflows, and institutional knowledge that didn’t exist before.

    And that was probably the real opportunity all along.

  • Local vLLM and llama.cpp Dashboard

    Local vLLM and llama.cpp  Dashboard

    Introduction

    Over the last several months I have been doing a bit of experimentation locally hosted LLMs, primarily with vLLM, llama.cpp, different quantization methods, GPU configurations, and models. My overall goal is a smart enough local agent that can code and troubleshoot, and ask smarter frontier models for help/review.

    Part of this journey has been learning about all the tuning knobs. As knowing the api is responding is not really enough.

    I also want to know things like:

    • What model is actually loaded?
    • How much VRAM is it consuming?
    • How much VRAM do I still have available?
    • Is the GPU actually doing anything?
    • What context size am I running?
    • How much KV cache is being consumed?
    • What parameters did I start the inference engine with?
    • How fast is the model actually responding?

    Most of this information is available somewhere, but I wanted to not have to hunt and peck for data.

    Normally, I would check Docker, query an API endpoint, run amd-smi (or nvidia-smi), look through vLLM startup logs, inspect environment variables, and then run another script just to benchmark the model. Not hard, but requires a few steps or maybe a script.

    Instead, I built a dashboard.

    The project is called the Inference Engine Dashboard, and the goal is pretty simple: give me one place to see what my local inference server is doing.

    This is not a proxy, or a chat engine. There are already plenty of those.

    This is an operations and monitoring dashboard for vLLM and llama.cpp.


    What I Wanted From the Dashboard

    My primary use case is a workstation running a local LLM while I am also using the machine normally.

    That makes VRAM particularly important, as I need to keep enough VRAM available if I want to fire up the desktop (DE).

    For example, I may intentionally configure vLLM to use less than the maximum available GPU memory because I still need enough VRAM left for my desktop, browser, IDE, and other applications.

    In that environment, I want to be able to open a webpage and immediately see something like:

    • GPU: AMD Radeon AI PRO R9700
    • VRAM used: 23 GB / 32 GB
    • Inference process: 21 GB
    • Other GPU applications: 2 GB
    • Remaining headroom: 9 GB
    • Model: Qwen
    • Context: 32K
    • KV cache utilization
    • GPU utilization
    • GPU temperature
    • Current token throughput

    Basically, the things I normally end up checking manually.


    Local-First Architecture

    I deliberately designed the dashboard to run on the same physical machine as the inference engine as some information is easy to get remotely from the vLLM or llama.cpp API, but detailed hardware and process information is not.

    To accurately determine what is happening on the host, the dashboard may need access to:

    • GPU devices
    • GPU process information
    • Docker container metadata
    • Inference process IDs
    • ROCm libraries
    • Model cache directories
    • Host CPU and memory information

    The application itself runs using Docker Compose.

    docker compose ps

    I also did not want to simply give the application unrestricted access to /var/run/docker.sock, so Docker access is provided through a restricted Docker socket proxy.

    Model caches and other host resources are mounted read-only wherever possible.

    That was an important design decision because eventually I may allow the dashboard to manage inference engines, but I don’t want to jump directly from “monitoring dashboard” to “application that can arbitrarily manipulate containers.”


    Current Dashboard Information

    The main dashboard provides a quick overview of the host and inference environment.

    It includes information such as:

    Host

    • Hostname
    • IP address
    • Operating system
    • Kernel
    • CPU
    • Total system memory

    GPU

    • GPU model
    • GPU utilization
    • Temperature
    • Power consumption
    • VRAM used
    • VRAM available
    • Inference-process VRAM
    • Other GPU process usage

    Inference Engine

    • Active model
    • Backend
    • API health
    • Metrics health
    • Maximum context
    • KV-cache utilization
    • Current throughput
    • Runtime configuration

    The dashboard also displays locally cached models so I can see what is already available on the host.

    If the inference engine or GPU tooling doesn’t expose a particular value, the dashboard displays it as unavailable rather than trying to estimate it.


    Monitoring vLLM

    vLLM exposes quite a bit of useful information, but it isn’t all available from one location.

    The dashboard therefore combines information from several sources, including:

    /health
    /v1/models
    /metrics

    along with:

    • Docker inspection
    • Container environment variables
    • vLLM startup logs
    • GPU process telemetry

    From those sources the dashboard can display information including:

    • Active model
    • Served model name
    • vLLM version
    • Container image
    • Maximum model length
    • GPU memory utilization target
    • Precision
    • Quantization
    • Maximum sequences
    • Maximum batched tokens
    • Prefix caching
    • KV-cache type
    • KV-cache allocation
    • KV-cache utilization
    • KV-cache token capacity
    • CPU offload
    • Swap configuration
    • Maximum concurrency
    • Prompt throughput
    • Output throughput
    • Model-weight VRAM
    • Inference-process VRAM
    • Other GPU VRAM usage
    • Remaining VRAM headroom

    Some of the more detailed memory information is only available in the vLLM startup logs.

    This is another reason the dashboard runs locally.

    If vLLM doesn’t provide enough information to reliably calculate a value, the dashboard simply reports it as unavailable.


    Monitoring llama.cpp

    One of the more interesting parts of the project was adding llama.cpp support as llama.cpp and vLLM expose very different information.

    My first thought was to simply use the same dashboard fields and populate whatever llama.cpp could provide.

    That quickly resulted in a screen containing a whole bunch of:

    N/A
    N/A
    N/A
    N/A

    which isn’t particularly useful.

    So the dashboard now uses provider-specific runtime views.

    For llama.cpp it queries endpoints including:

    /health
    /v1/models
    /props
    /metrics
    /v1/completions

    Depending on what the server exposes, the llama.cpp runtime panel can display:

    • Active GGUF model
    • Configured context window
    • Native model context window
    • GGUF quantization
    • Model parameter count
    • Model file size
    • Parallel slots
    • GPU layers
    • Batch size
    • Microbatch size
    • Temperature
    • Top-K
    • Top-P
    • Min-P
    • Repeat penalty
    • llama.cpp build version
    • Container image
    • Token throughput
    • Inference-process GPU memory

    This makes the llama.cpp view much more useful than trying to force llama.cpp into a vLLM-shaped dashboard.


    GGUF Model Discovery

    The dashboard can also scan a configured directory containing GGUF models.

    For example:

    /models

    or wherever your llama.cpp model library happens to live.

    The directory is mounted read-only into the dashboard container and any discovered GGUF files are displayed in the model selector.

    At the moment this is informational.

    The dashboard can see the models, but it cannot switch to them yet.

    That is intentional.


    AMD GPU Monitoring

    My current workstation uses an AMD GPU, so ROCm support was a requirement from the beginning.

    On AMD systems the dashboard primarily uses:

    amd-smi

    with ROCm SMI available as a fallback.

    In addition to total GPU memory consumption, I wanted to know who was using the VRAM.

    That allows the dashboard to distinguish between:

    Inference Process
    Other GPU Processes
    Unattributed GPU Memory
    Free VRAM

    This is particularly useful on a workstation.

    If my inference engine is configured to consume around 23 GB of a 32 GB GPU, I want to know whether the remaining GPU memory is actually available or whether Chrome, my desktop environment, an IDE, or something else has already consumed part of it.


    The Interactive Benchmark

    I also wanted a quick way to test a model after changing its configuration. Wasn’t looking for a massive benchmark suite.

    Just something that answers:

    “Does this model actually feel fast when I interact with it?“

    The dashboard includes a:

    Run TTFT + token rate test button.

    The benchmark sends a deterministic streaming request through the OpenAI-compatible endpoint.

    /v1/completions

    The current test uses:

    Temperature: 0
    Seed: 1
    Maximum output: 128 tokens
    Concurrency: 1
    Warm-up requests: 0

    The test records three values that I care about for interactive use.

    Time to First Token

    How long did I wait before the model actually started responding?

    Output Tokens Per Second

    Once generation started, how quickly did the model generate tokens?

    End-to-End Latency

    How long did the complete request take?

    The benchmark results are stored in SQLite along with information including:

    • Model
    • Prompt tokens
    • Completion tokens
    • Seed
    • Time to first token
    • Generation time
    • End-to-end latency
    • Output tokens/sec
    • Raw streaming evidence

    That means I can start building some history as I experiment with different models and runtime settings.


    What This Benchmark Is Not

    The built-in benchmark is intended as a quick interactive performance test.It is not intended to replace a proper inference benchmarking suite.

    Currently it does not test:

    • Multiple simultaneous users
    • P50/P95/P99 latency
    • Long-context performance
    • Sustained GPU utilization
    • Warm versus cold requests
    • Maximum batch throughput
    • Extended resource sampling

    Those are all things I may eventually add.

    For now I mainly want to answer:

    I changed something. Did the model get faster or slower?

    For that, the test works pretty well.


    Deployment

    Deployment is intentionally simple.

    Clone the repository, configure the environment, and run:

    ./scripts/deploy.sh

    Host-specific settings are stored in a gitignored:

    .env

    file.

    The same dashboard can be configured for either inference engine by setting:

    INFERENCE_BACKEND=vllm

    or:

    INFERENCE_BACKEND=llama_cpp

    and providing the appropriate API endpoint and model-cache location.

    The containers use Docker’s:

    unless-stopped

    restart policy, so the dashboard will come back automatically after the host reboots and Docker starts.


    Why Model Switching Is Disabled

    You may notice that the dashboard can discover models but currently doesn’t allow you to activate one.

    This is intentional.

    It would actually be fairly easy to add a button that stops an inference container and starts another model.

    The problem is everything that can happen after I push that button.

    For example:

    1. Stop the running inference engine.
    2. Start the new model.
    3. Model doesn’t fit in VRAM.
    4. Container crashes.
    5. API never becomes healthy.
    6. Previous configuration is now gone.
    7. My inference server is offline.

    Not particularly useful.

    Before enabling model switching I want the application to support something closer to a transaction:

    Save current configuration
    ↓
    Stop current model
    ↓
    Start requested model
    ↓
    Validate container
    ↓
    Validate API
    ↓
    Validate model
    ↓
    SUCCESS

    If something fails:

    FAIL
    ↓
    Restore previous configuration
    ↓
    Restart previous model
    ↓
    Validate health

    Once that logic exists, enabling lifecycle controls makes much more sense.


    Security

    Authentication is also not implemented yet.

    So at this point the dashboard should only be exposed on a trusted network.

    That is less concerning while the application is primarily read-only, but it becomes much more important once lifecycle controls are introduced.

    Before enabling model switching or container management I plan to add authentication and additional authorization controls.


    Why I Built It

    This really came out of repeatedly doing the same troubleshooting process.

    I would SSH into the workstation and run something like:

    docker ps

    Then:

    amd-smi

    Then:

    curl http://localhost:8000/v1/models

    Then:

    curl http://localhost:8000/metrics

    Then look through the container logs:

    docker logs <container>

    Then maybe run another script to see how fast the model was responding.

    None of those things are particularly difficult.

    It is just a lot of little pieces of information spread across several different tools.

    Now I can open one page and immediately see:

    • What model is running
    • Whether the inference server is healthy
    • How much VRAM it is consuming
    • What else is using the GPU
    • How much VRAM I have left
    • What context size is configured
    • How much KV cache is being used
    • How the inference engine was configured
    • How quickly the model starts responding
    • How many tokens/sec it is generating

    Which is basically what I wanted.


    What’s Next

    There are several things I would eventually like to add.

    The big one is obviously safe model switching.

    Beyond that I would like to explore:

    • Model configuration profiles
    • Transactional model switching
    • Automatic rollback
    • Authentication
    • Historical GPU metrics
    • Historical VRAM consumption
    • Benchmark history and comparison charts
    • Longer-context benchmarks
    • Concurrent request testing
    • NVIDIA GPU support
    • Additional inference engines

    There is also an interesting possibility of having the dashboard recommend runtime settings based on available GPU memory and the selected model.

    But I don’t want this to become another giant AI management platform.

    The original goal is still the most important one:

    Open one page and understand what my local inference server is doing.

    So far, it has made experimenting with vLLM and llama.cpp considerably easier.

    Resources

    1. Project Repository
      https://github.com/christopherpaquin/vLLM-Llama.ccp-Dashboard
    2. vLLM
      https://github.com/vllm-project/vllm
    3. llama.cpp
      https://github.com/ggml-org/llama.cpp
  • The AMD R9700 – Ubuntu 24.04 Quickstart Guide

    The AMD R9700 – Ubuntu 24.04 Quickstart Guide

    ….and I bought an AMD GPU.

    It’s the first time I’ve gone non-Nvidia since before 2010 as I’ve been a Nvidia loyalist since the GeForce 256 back in 1999 (was 3dfx prior). But Nvidia GPU pricing on anything with decent VRAM has gotten absurd, even on the used market. I kept hearing that ROCm (Radeon Open Compute) support for inference has finally matured. So I decided to find out for myself.

    I picked up the AMD Radeon AI PRO R9700, 32GB. Not cheap, but brand new its cheaper than an RTX 3090 24gb from 2020.

    Since this is my first AMD in very long while, going to have to figure out drivers. So let’s get started. First lets gather some info by running the commands below.

    lspci -nn | grep -Ei 'VGA|Display|AMD|ATI'
    lsmod | grep amdgpu
    dmesg | grep -i amdgpu | tail -50

    Verify GPU Detection

    Prior to card swap, purged my Ubuntu 24.04 system of all Nvidia drivers and tools.

    Lets check to see if the card is detected properly

    lspci -nn | grep -Ei 'VGA|Display|AMD|ATI'

    This command searches the PCI device list for anything matching VGA, Display, AMD, or ATI. In the output below we see the following

    • 03:00.0 is the PCI id of the R9700 GPU
    • Vendor ID 1002 confirms AMD Device
    • 7551 is how the kernel identifies this GPU generation/device
    • The two PCI bridge entries show the GPU exposes an internal PCIe switch/bridge topology
    01:00.0 PCI bridge: AMD Navi 10 XL Upstream Port of PCI Express Switch
    02:00.0 PCI bridge: AMD Navi 10 XL Downstream Port of PCI Express Switch
    03:00.0 VGA compatible controller: AMD Device [1002:7551]
    03:00.1 Audio device: AMD Device [1002:ab40]

    The important takeaway is that Ubuntu already recognizes the R9700 correctly and the in-kernel AMDGPU driver is successfully initializing it.

    lsmod | grep amdgpu

    This command above shows whether the AMDGPU kernel module is loaded and what other kernel modules depend on it.

    amdgpu 21188608 26

    This tells us:

    • amdgpu is loaded
    • the module occupies roughly 21 MB of kernel module memory
    • 26 references/users currently depend on it
    dmesg | grep -i amdgpu | tail -50

    The command above shows the last 50 kernel messages associated with AMDGPU initialization.

    The most important things we see are:

    MEM ECC is active.

    The GPU’s VRAM ECC capability is active.

    VRAM: 32624M

    The kernel detects approximately 32 GB of VRAM.

    Detected VRAM RAM=32624M, BAR=32768M

    Preparing for ROCm Install

    First lets make sure that the kernel has created the ROCm compute device

     ls -l /dev/kfd
    crw-rw---- 1 root render 235, 0 Aug  9 16:20 /dev/kfd
    
    
    

    /dev/kfd is the Kernel Fusion Driver device that ROCm/HIP uses to submit compute workloads to the AMD GPU. AMD’s ROCm installation process relies on access through the render/video device groups.

    In the output above, you can see that the Kernel Fusion Driver is owned by root with group membership of render. We need to add my user to the render group. Note that I had to reboot to pick up the changes.

    sudo usermod -aG render cpaquin

    Now we can add the ROCm 7.2.2 package repo, which is two steps. This one below…

    sudo mkdir --parents --mode=0755 /etc/apt/keyrings
    wget https://repo.radeon.com/rocm/rocm.gpg.key -O - | \
    gpg --dearmor | sudo tee /etc/apt/keyrings/rocm.gpg > /dev/null

    And this next one to actually add the repo.

    sudo tee /etc/apt/sources.list.d/rocm.list <<'EOF'
    deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/rocm/apt/7.2.2 noble main
    deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/graphics/7.2.1/ubuntu noble main
    EOF

    Now let’s be smart and ensure that the repo was added properly.

    cat /etc/apt/sources.list.d/rocm.list
    deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/rocm/apt/7.2.2 noble main
    deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/graphics/7.2.1/ubuntu noble main

    Now before we install anything, per AMD recommendation, we need to pin the above repo priority. Pinning means settings which repository Ubuntu prefers when the same package is available from multiple repositories, which means prefer the AMD repos over Ubuntu repos for AMD bits and bobbles.

    sudo tee /etc/apt/preferences.d/rocm-pin-600 <<'EOF'
    Package: *
    Pin: release o=repo.radeon.com
    Pin-Priority: 600
    EOF

    Now we update metadata.

    sudo apt update

    We can see the repo preference via pinning below.

     apt policy rocm
    rocm:
      Installed: (none)
      Candidate: 7.2.2.70202-86~24.04
      Version table:
         7.2.2.70202-86~24.04 600
            600 https://repo.radeon.com/rocm/apt/7.2.2 noble/main amd64 Packages
    
    
    

    ROCm Install

    First, lets make sure we have the following installed.

    sudo apt install python3-setuptools python3-wheel

    Now we can install ROCm. It takes a bit so be patient, or don’t.

    sudo apt install rocm

    Now lets verify that ROCm can see the GPU

    rocminfo

    Truncated output below

    rocminfo
    ROCk module is loaded
    =====================
    HSA System Attributes
    =====================
    Runtime Version: 1.18
    Runtime Ext Version: 1.15
    System Timestamp Freq.: 1000.000000MHz
    Sig. Max Wait Duration: 18446744073709551615 (0xFFFFFFFFFFFFFFFF) (timestamp count)
    Machine Model: LARGE
    System Endianness: LITTLE
    Mwaitx: DISABLED
    XNACK enabled: NO
    DMAbuf Support: YES
    VMM Support: YES
    *******
    Agent 2
    *******
    Name: gfx1201
    Uuid: GPU-407fcdae2bc6b11d
    Marketing Name: AMD Radeon AI PRO R9700
    Vendor Name: AMD
    Feature: KERNEL_DISPATCH
    Profile: BASE_PROFILE
    Float Round Mode: NEAR
    Max Queue Number: 128(0x80)
    Queue Min Size: 64(0x40)
    Queue Max Size: 131072(0x20000)
    Queue Type: MULTI
    Node: 1
    Device Type: GPU

    And now run amd-smi.

    amd-smi
    +------------------------------------------------------------------------------+
    | AMD-SMI 26.2.2+671d39a71e amdgpu version: 7.0.0-28 ROCm version: 7.2.2 |
    | VBIOS version: 00158746 |
    | Platform: Linux Baremetal |
    |-------------------------------------+----------------------------------------|
    | BDF GPU-Name | Mem-Uti Temp UEC Power-Usage |
    | GPU HIP-ID OAM-ID Partition-Mode | GFX-Uti Fan Mem-Usage |
    |=====================================+========================================|
    | 0000:03:00.0 ...Radeon AI PRO R9700 | 1 % 31 °C 0 9/300 W |
    | 0 0 N/A N/A | 3 % 20.0 % 661/32624 MB |
    +-------------------------------------+----------------------------------------+
    +------------------------------------------------------------------------------+
    | Processes: |
    | GPU PID Process Name GTT_MEM VRAM_MEM MEM_USAGE CU % |
    |==============================================================================|
    | No running processes found |

    What we are looking at:

    • ROCm 7.2.2 is active.
    • The Radeon AI PRO R9700 is correctly identified.
    • Temperature is 31 °C.
    • Power draw is only 9 W / 300 W at idle.
    • Fan is at 20%.
    • VRAM usage is 661 MB / 32,624 MB, which is normal for the display/driver stack.
    • No uncorrectable ECC errors: UEC 0.
    • No compute processes are currently using the GPU.

    Test ROCm inside Docker

    Can a Docker container access the R9700 through ROCm? Lets find out.

    docker run --rm \
    --device=/dev/kfd \
    --device=/dev/dri \
    --group-add 992 \
    --group-add 44 \
    rocm/rocm-terminal:latest \
    rocminfo

    What this does:

    • starts a temporary AMD ROCm container
    • passes /dev/kfd into it for ROCm compute
    • passes /dev/dri into it for the GPU device nodes
    • gives the container the needed video (44) and render (992) group access
    • runs rocminfo inside the container
    • deletes the container afterward because of --rm

    If it works, we should see the same R9700 identification inside Docker that we saw on the host, including something like:


    Pytorch Test

    Next we’ll use AMD’s validated PyTorch container for ROCm 7.2 / Ubuntu 24.04 / Python 3.12 / PyTorch 2.9.1. AMD recommends Docker for PyTorch because the image already contains a tested PyTorch+ROCm combination.

    docker run --rm \
    --device=/dev/kfd \
    --device=/dev/dri \
    --group-add 992 \
    --group-add 44 \
    --security-opt seccomp=unconfined \
    --ipc=host \
    --shm-size 8G \
    rocm/pytorch:rocm7.2_ubuntu24.04_py3.12_pytorch_release_2.9.1 \
    python3 -c "import torch; print('PyTorch:', torch.__version__); print('ROCm:', torch.version.hip); print('GPU available:', torch.cuda.is_available()); print('GPU:', torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'NONE')"

    This will likely download a fairly large container image the first time.

    What we want to see is essentially:

    PyTorch: 2.9.1+rocm7.2.0.git7e1940d4
    ROCm: 7.2.26015-fc0010cf6a
    GPU available: True
    GPU: AMD Radeon AI PRO R9700

    One potentially confusing point: PyTorch keeps the API name torch.cuda even when using AMD ROCm.

    torch.cuda.is_available() returning True means PyTorch has a usable GPU backend; on this container that backend is HIP/ROCm, not NVIDIA CUDA. AMD uses this same test in its ROCm PyTorch verification instructions.

    So the R9700 is now validated for actual ROCm/PyTorch compute inside Docker. Next Step VLLM and test some models.

  • Fixing the OpenSSH Post-Quantum Warning on RHEL 9

    Fixing the OpenSSH Post-Quantum Warning on RHEL 9

    Fired up my RHEL9 server today, something that I have not done in a while due to the savage summer heat in the southern US. All my Dell servers have had the summer off for this reason.

    Upon login via ssh I saw a warning message, which I do not think I have seen before on this machine – all my other Dell servers are RHEL 10, and this one was built with RHEL 9 due to deprecated support for older processors in RHEL 10. Specifically the Intel Xeon E5-2697 (Ivy Bridge) v2.

    Anyway, below is the warning message.

    ** WARNING: connection is not using a post-quantum key exchange algorithm.
    ** This session may be vulnerable to "store now, decrypt later" attacks.
    ** The server may need to be upgraded. See https://openssh.com/pq.html

    This warning means that the SSH client supports post-quantum key exchange, but the RHEL 9 SSH server did not offer one. It does not indicate that the current connection is broken; it warns about possible future decryption of captured traffic.

    Hence the “store now, decrypt later“ message above.


    SNDL/HNDL

    “Store Now, Decrypt Later” (also called Harvest Now, Decrypt Later) is when an attacker intercepts encrypted traffic, not with the idea of decrypting it now, but rather once they have access to a more powerful quantum computer running advanced algorithms. Today’s standard SSH connections rely on classical asymmetric cryptography (like Elliptic Curve Diffie-Hellman) which quantum computers can theoretically break.

    Meaning, you may not be vulnerable now, but you could be later.

    Most newer OpenSSH clients warn when a connection does not negotiate a post-quantum key-exchange algorithm because long-lived or sensitive data captured today could remain valuable in the future.

    Honestly this is not a big concern in my lab, but the warning will drive me up a wall.


    The RHEL 9 Fix

    The good news is that RHEL 9.1 and later support the following hybrid post-quantum SSH key-exchange method. So we can remediate.

    sntrup761x25519-sha512@openssh.com

    This combines the post-quantum sntrup761 algorithm with the traditional X25519 elliptic-curve key exchange. Because it is hybrid, the connection remains protected as long as either component remains secure.

    Red Hat added support for this algorithm to the RHEL 9 system-wide cryptographic-policy framework, but it must be explicitly enabled through a custom subpolicy.


    Confirm the RHEL Version

    First, lets confirm that the server is running RHEL 9.1 or later:

    cat /etc/redhat-release

    You can also check the installed OpenSSH and crypto-policy package versions:

    rpm -q openssh-server crypto-policies

    Update OpenSSH and the Crypto-Policy Packages

    sudo dnf update openssh-server crypto-policies

    This ensures that the server has the RHEL packages that include support for the SNTRUP hybrid key-exchange algorithm.

    In my case, this step initially revealed a separate Red Hat subscription problem:

    Status code: 403 for https://cdn.redhat.com/...

    A 403 from the Red Hat CDN generally means that the server can reach the repository, but the system is not currently authorized to download its content. This may be caused by an expired developer subscription, stale registration data, an invalid entitlement, or a system identity left over from an earlier installation.

    Useful commands for checking the registration state include:

    sudo subscription-manager identity
    sudo subscription-manager status
    sudo subscription-manager repos --list-enabled

    As I said this machine has been powered off for a while, so lets refresh registration and repository metadata.

    sudo subscription-manager refresh
    sudo dnf clean all
    sudo rm -rf /var/cache/dnf
    sudo dnf makecache

    In my case, the system registration is stale, so I need to unregister and re-register.

    sudo subscription-manager unregister
    sudo subscription-manager clean
    sudo subscription-manager register

    After registration, we ensure that the standard RHEL 9 repositories are enabled:

    sudo subscription-manager repos \
    --enable=rhel-9-for-x86_64-baseos-rpms \
    --enable=rhel-9-for-x86_64-appstream-rpms

    Then rebuild the DNF cache:

    sudo dnf clean all
    sudo rm -rf /var/cache/dnf
    sudo dnf makecache

    Create a Custom Crypto-Policy Module

    Next we create a new policy module named SNTRUP:

    sudo tee /etc/crypto-policies/policies/modules/SNTRUP.pmod >/dev/null <<'EOF'
    key_exchange = +SNTRUP
    EOF

    The module name must use uppercase characters because crypto-policy module filenames are conventionally uppercase and end with .pmod.

    The policy adds the SNTRUP algorithm to the key-exchange methods permitted by the existing system-wide policy.


    Apply the Policy

    Apply the custom module on top of the normal RHEL DEFAULT policy:

    sudo update-crypto-policies --set DEFAULT:SNTRUP

    Confirm that the new policy is active:

    update-crypto-policies --show

    You are looking for expected output as shown below.

    DEFAULT:SNTRUP

    RHEL uses system-wide cryptographic policies to configure applications including OpenSSH, TLS libraries, IPsec, DNSSEC, and Kerberos. Using a crypto-policy module is therefore preferable to manually changing generated OpenSSH backend files.


    Restart SSH

    Next we restart sshd so that it reads the updated cryptographic policy:

    sudo systemctl restart sshd

    And we confirm that the service restarted without issue

    sudo systemctl status sshd --no-pager

    Verify the Server Configuration

    Run the command below to check the current SSH server key-exchange configuration:

    sudo sshd -T | grep -i '^kexalgorithms'

    The output should include:

    sntrup761x25519-sha512@openssh.com

    We can also inspect the generated OpenSSH server crypto-policy backend:

    grep -i kexalgorithms \
    /etc/crypto-policies/back-ends/opensshserver.config

    Files beneath /etc/crypto-policies/back-ends/ are generated by the crypto-policy framework and should not normally be edited directly. Manual changes may be overwritten the next time the system policy is updated.

    Now it’s time to test.

    7. Test a New SSH Connection

    Open a new terminal window, leaving the existing window open just in case we broke something. In the second window, connect with verbose logging:

    ssh -vv user@server

    Look for a line similar to:

    debug1: kex: algorithm: sntrup761x25519-sha512@openssh.com

    The original post-quantum warning should no longer appear.

    A more focused test can be performed by explicitly requesting the algorithm:

    ssh \
    -o KexAlgorithms=sntrup761x25519-sha512@openssh.com \
    user@server

    If this succeeds, both the SSH client and server support the hybrid key exchange.

    We can check whether the local SSH client recognizes the algorithm with:

    ssh -Q kex | grep -i sntrup

    Expected output:

    sntrup761x25519-sha512@openssh.com

    Depending on the client version, another SNTRUP variant may also be listed.


    Troubleshooting

    If the algorithm does not appear in the output from sshd -T, first confirm that the custom policy is active:

    update-crypto-policies --show

    Then confirm that the policy module contains the correct entry:

    cat /etc/crypto-policies/policies/modules/SNTRUP.pmod

    It should contain:

    key_exchange = +SNTRUP

    Reapply the policy and restart SSH:

    sudo update-crypto-policies --set DEFAULT:SNTRUP
    sudo systemctl restart sshd

    Check the SSH service logs for errors:

    sudo journalctl -u sshd -b --no-pager

    You can also validate the SSH configuration before restarting the service:

    sudo sshd -t

    No output means that the configuration passed validation.


    Important FIPS Consideration

    Do not replace an existing FIPS policy with DEFAULT:SNTRUP on a system required to operate in FIPS mode.

    Check the current policy first:

    update-crypto-policies --show

    Also check whether the kernel is operating in FIPS mode:

    cat /proc/sys/crypto/fips_enabled

    A value of 1 means that FIPS mode is active.

    Algorithms permitted by the normal RHEL DEFAULT policy are not automatically permitted under the RHEL FIPS policy. Red Hat documents that certain Curve25519-based SSH key exchanges are not allowed in FIPS mode, so the SNTRUP hybrid method should not be enabled without validating the applicable compliance requirements.


    Rolling Back the Change

    To remove the SNTRUP subpolicy and return to the standard RHEL default policy:

    sudo update-crypto-policies --set DEFAULT
    sudo systemctl restart sshd

    Confirm the result:

    update-crypto-policies --show

    Expected output:

    DEFAULT

    The custom policy file can then be removed if it is no longer needed:

    sudo rm -f /etc/crypto-policies/policies/modules/SNTRUP.pmod

    Final Result

    After enabling the RHEL crypto-policy module, restarting sshd, and creating a new connection, the server and client should negotiate:

    sntrup761x25519-sha512@openssh.com

    The SSH session still uses familiar symmetric encryption and authentication mechanisms. The portion being changed is the initial key exchange used to establish the session keys.

    This does not make the entire SSH protocol “post-quantum.” It adds a hybrid post-quantum key-exchange mechanism designed to protect the session-establishment process against both traditional attacks and the future possibility of cryptographically capable quantum computers.

    This server that has a few years of useful service (specifically for my Infiniband testing), but cannot move to RHEL 10. So enabling the supported RHEL 9 hybrid key exchange is “good enough”.

  • The July 2026 Agentic Coding Cheat Sheet: Frontier Tools, Cost Models, and Quota Mechanics – V2

    The July 2026 Agentic Coding Cheat Sheet: Frontier Tools, Cost Models, and Quota Mechanics – V2

    The tool sprawl in AI engineering has officially outrun our collective memory, at least it has mine.

    Between native terminal agents, newly minted desktop clients, and entirely divergent API cost models, keeping the ecosystem straight is a full-time job.

    I built this reference guide for a very simple reason: I needed a single, no-nonsense source of truth to track what tools each frontier provider actually offers right now, and exactly how running autonomous developer loops will affect my wallet.

    Here is the state of the stack as I know it and use it. Sure there are plenty of options I am missing.


    The Tooling Matrix: Interfaces, CLIs, and IDEs

    This matrix maps out exactly where each provider expects you to interact with their models, highlighting the shift toward dedicated desktop environments and autonomous terminal agents.

    Provider / EcosystemFlagship Web / Workspace InterfaceOfficial Desktop AppNative CLI / Terminal AgentIDE Integration Strategy
    GitHub CopilotGitHub.com & Copilot Workspace (Issue-to-PR sandbox)GitHub Copilot App (Native agent-centric control center)GitHub Copilot CLI (TUI via gh copilot with local execution capabilities)Official Extension (VS Code, JetBrains, Visual Studio, Neovim)
    Anthropic (Claude)Claude.ai (with Artifacts for rendering code/UI side-by-side)Claude Desktop App (Supports local filesystem/tool execution)Claude Code (State-of-the-art terminal agent for codebase refactoring)No official extension; relies on CLI or third-party tools like Cline/Continue
    OpenAI (ChatGPT)ChatGPT (utilizes Canvas for inline text and code editing)ChatGPT Desktop App (Mac & Windows)OpenAI API CLI (Primarily for asset, file, and model management)No direct IDE tool; serves as the model layer for Cursor, Aider, etc.
    Google (Gemini)Gemini Web InterfaceAntigravity 2.0 Desktop ClientAntigravity CLI (Replaces old gemini-cli; orchestrates parallel agent tasks)Gemini Code Assist & Antigravity plugins (VS Code, Android Studio, IntelliJ)
    DeepSeekDeepSeek Chat (V3 & R1 reasoning models)Mobile Apps (Relies on web wrappers for desktop)None natively (Relies on community tools like Ollama or Aider)Native integration into open-source extensions like Continue and Cline
    PerplexityPerplexity.ai (Pro search & “Pages” workspace)Perplexity Desktop (Mac & Windows)None (Relies on API keys for third-party scripts)None (Primarily used as a data-grounding search API)

    The Economic Matrix: Quotas, Costs, and Loop Behavior

    This is the most critical matrix, IMHO. As someone who is experimenting with numerous paid frontier model services, each with a limited budge, I am not all in on any service yet, and want to maximize my low-paid token tiers.

    This table explains how the subscription models and APIs.

    Provider / ServiceDeveloper Plan & Cost (USD)IDE / CLI Billing MechanismQuota / Rate Limit MechanicsThe “Agentic Loop” Behavior (The Cost Trap)
    GitHub CopilotPro: $10/mo
    Max: $100/mo
    Flat-rate subscription for native tools.Hybrid Metered: Unlimited standard completions + a fixed pool of premium model credits.The Safest Route: Because it’s integrated, Microsoft absorbs a massive amount of the token cost, but heavy loop usage will hit a speed throttle unless on the Max tier.
    Anthropic (Claude)Pro: $20/mo
    Max 5x: $100/mo
    Billed via web account or separate Pay-As-You-Go API.5-Hour Rolling Window: Dynamic usage budget that resets every 5 hours based on server demand.High Risk for Pro: Running Claude Code on a large infrastructure repository will hit your 5-hour limit fast. Full-time agentic workflows require switching to Claude Max or the raw API.
    OpenAI (ChatGPT)API Account
    (Pay-As-You-Go)
    BYOK (Bring Your Own Key): Paid per token used in Cursor, Aider, etc.Volumetric Tiers: Limits depend on lifetime account funding (Tiers 1–5), scaling from 200k to millions of tokens/min.Predictable but Costly: Perfect for episodic troubleshooting, but an out-of-control recursive autonomous loop can easily rack up a $20–$50 bill in an afternoon.
    Google (Gemini)Code Assist Standard
    ~$19/mo
    Flat-rate per user or metered hourly via Google Cloud.Concurrently Throttled: Throttles users if background parallel multi-agent loops request too much compute at once.Great for Standard Dev: Highly stable for individual coding, but hard to hook into un-vetted, third-party autonomous command-line tools.
    DeepSeekPlatform API
    (Pay-As-You-Go)
    BYOK: Funded via a pre-paid developer wallet.Wallet Funded: Strictly bound by whatever dollar amount you load into your account.The Economy Champion: Because of their unified architecture and heavy 90%+ prompt caching discounts, running heavy agentic loops costs literal pennies compared to western models. $10 can last a month.
    PerplexityPerplexity Pro
    $20/mo
    Subscription includes a small monthly API credit.Fixed Credit Ceiling: API access stops or charges your card when your small monthly search token allowance runs dry.Not For Coding Loops: Great for pulling real-time documentation or API schemas into your agent, but financially unviable for writing or refactoring code blocks.

    Understanding the Layers of Agentic LLM Tool Execution

    To expand this post, it is useful to define several concepts that are now common in agentic AI systems: tools, skills, MCP servers, local execution layers, and IDE-integrated actions.

    Out of the box, an LLM is primarily a reasoning and text-generation system. You can ask it questions, request explanations, have it draft a script, or ask it to review configuration, and it will respond with generated text. By itself, however, the model does not inherently have access to your operating system, local files, terminal, IDE, logs, repositories, ticketing systems, monitoring platforms, or infrastructure.

    That distinction is important.

    A hosted model may appear to have additional capabilities, such as reading uploaded documents, searching the web, generating files, or analyzing code, but those capabilities are usually provided by the surrounding platform, client application, or agent runtime rather than by the model alone.

    This is where concepts such as tools, skills, MCP servers, and agent runtimes become important. These components extend an LLM from a passive chatbot into an interactive agentic system. The model can reason about what needs to happen, request the appropriate tool, inspect the result, and continue working through a task. The runtime around the model is responsible for exposing those tools, enforcing policy, executing approved actions, and returning results back to the model.

    When discussing LLMs that can inspect logs, modify files, run scripts, query systems, or interact with development environments, it is important to separate the layers involved. These capabilities are often described broadly as “agentic AI,” but agentic behavior is usually the result of several distinct components working together.

    The model provides reasoning and structured tool-call requests. The orchestration layer manages the workflow, state, prompts, and tool-calling loop. The local execution layer performs approved actions against the operating system, filesystem, terminal, repository, or other target environment. IDE-integrated actions expose workspace-specific capabilities such as file editing, diagnostics, Git state, and terminal access. Skills provide reusable task-specific instructions or workflows. MCP servers provide a standardized way to expose tools, resources, and prompts to compatible AI clients.

    In other words, the LLM is tool-aware, but the surrounding runtime is tool-authoritative.

    The model may decide that it needs to read a log file, edit a configuration file, run a script, or inspect a repository. However, the runtime determines which tools exist, which actions are allowed, where those actions execute, what policy controls apply, and what results are returned to the model.

    The table below provides a high-level comparison of the major concepts involved in LLM-driven local execution and tool integration.

    LLM Agent Execution Layers and Integration Concepts

    ConceptFormal NameWhat It MeansWhat It Controls or ProvidesExample in Practice
    Tool callingTool calling / function calling / structured tool invocationThe model’s ability to request an external action using a defined schema instead of free-form text.Provides the structured interface between the LLM and external capabilities.The model requests read_file(path="/var/log/app.log") or run_tests(command="pytest").
    ToolAgent tool / callable tool / local execution toolA specific function or action that can be invoked by the agent workflow.Provides a single capability, such as reading a file, tailing logs, running a script, or checking Git status.tail_log, write_file, run_shell, git_diff, restart_service.
    AgentLLM agent / tool-using agent / agentic workflowThe loop that allows the model to plan, call tools, observe results, and continue until the task is complete.Coordinates reasoning, tool requests, observations, and final responses.The agent reads an error log, edits a config file, restarts a service, checks the result, and reports back.
    Agent orchestration frameworkAgent framework / workflow orchestration frameworkA framework used to build and manage the agent loop, tools, prompts, state, and execution flow.Provides abstractions for models, prompts, tools, memory, state, routing, and workflow control.LangChain, LangGraph, Semantic Kernel, AutoGen, CrewAI.
    Local execution layerGoverned local execution layer / OS execution layer / tool execution runtimeThe runtime component that actually performs approved actions against the local operating system or workspace.Controls OS-level access, command execution, filesystem changes, script execution, and result capture.Running journalctl, editing a YAML file, executing a validation script, or applying a patch.
    IDE-local actionsIDE agent tools / workspace tools / editor-integrated actionsTool capabilities provided inside an IDE or local coding assistant environment.Gives the agent access to files, terminals, diagnostics, search, Git state, and editor context within a workspace.Cursor, Cline, Continue, Zed, VS Code Chat, Aider, OpenHands.
    SkillsAgent skills / repo-local skills / workflow skillsPackaged instructions, scripts, conventions, or workflows that teach an agent how to perform a specific task in a specific context.Provides repeatable task guidance and project-specific operating instructions.A repo skill that tells the agent how to validate changes using shellcheck, pytest, yamllint, and ./health-check.sh.
    MCPModel Context Protocol / MCP server / MCP tool / MCP resourceA standard protocol for exposing tools, resources, and prompts to compatible AI clients.Standardizes how external systems provide capabilities and context to AI applications.An MCP server exposes filesystem access, GitHub issues, database queries, lab inventory, or monitoring data.
    Policy and guardrailsExecution policy / tool governance / approval policyThe rules that determine what tools are available, what actions are allowed, and whether approval is required.Controls access boundaries, command restrictions, file path restrictions, approval gates, logging, and auditability.Allow reading logs but require approval before modifying /etc/ or restarting a service.
    Observability and audit trailAgent observability / execution logging / audit trailThe record of what the agent requested, what was approved, what was executed, and what changed.Provides troubleshooting, reviewability, compliance, and rollback support.Logging each tool call, command output, file diff, approval decision, and final result.
  • From Phrasing to Choreography: An Exceptionally Brief History of Agentic Coding

    From Phrasing to Choreography: An Exceptionally Brief History of Agentic Coding

    The future is now… which is now “then.”

    Do you remember where you were on November 30th, 2022. If you were in the US (or perhaps Liberia or parts of Mexico as I have recently come to learn), you may have been sitting around the house eating leftover turkey, or a nice ham sandwich (if you were fortunate to have both available on Thanksgiving).

    November 30th, 2022 was the launch date for ChatGPT.

    Although it was very rough around the edges and not ready for prime-time, many immediately saw into the future. I cannot say that I did immediately, but I was out there experimenting, watching it “lie” to me. Those “lies” were quickly labeled hallucinations and sounded much cooler. Anyway the race was on apparently. Not that I was fully paying attention at the time.

    Like most engineers across infrastructure, hardware, or software, I’ve spent the last four years just trying to keep my head above water. Figuring out what to learn and what to ignore has become almost as exhausting as the actual learning process. On one side, you have the performative hype from influencers desperate to prove how far ahead of the curve they are. On the other, there’s a deafening wall of noise from frontier model providers and newly christened startups, all frantically trying to dump a product before their razor-thin window of entrepreneurship slams shut.

    As a traditional infra-engineer, LLMs and agentic coding have been quick to deliver value. I have never identified as a software engineer, but rather a hardware and infra-centric practitioner. I’ve spent my work-life architecting, deploying, and supporting Unix/Linux, virtualization platforms, and enabling hardware functions such as SR-IOV for Telcos across North America.

    I’m the guy who knows the difference between raid 0+5 and 5+0.

    Suddenly, I am now a technologist who now has the tools to quickly deliver true infrastructure as code without having to become an evangelist for any specific automation platform. Suddenly, its much easier to deploy tools, supporting services, create dashboards, and troubleshoot and resolve system/hardware issues.

    Strangely I have always loved creating documentation, and have spent many years being frustrated with the lack of documentation that I have seen from others. To me, a measure of success is the fact that a solution that you built, is still relied upon once you are gone. Without proper documentation, and the ability for your successors to truly understand, operate, and enhance something that you built is dependent on great documentation. If you can not transfer the appropriate level of operational and design knowledge to those who replace you, they will replace your solution as well. With AI, everyone now has the power of documentation. “Review this repo and describe how it works, update the README.md”. It’s truly a wonderful thing.

    The Micro-Eras of Agentic Coding

    Having done my best to keep up via reading, experimenting, and building, I have learned quite a bit in a very short time. Those who share the same passions and beyond are often learning the same things at the same time. Separately, but also together, as they uniformly change the vernacular of agentic coding. First, we learn how to prompt from the perspective of a persona, defining outcomes. Next, we discover the need for guardrails and frameworks, especially when the word security rears its ugly head.

    Nothing below is unique or original, and I am not sure that there is actually a difference between 3 & 4, or possibly the Eras are in the wrong order, but here is my table for trying to keep straight concepts and terminologies that describe those contexts

    Era & FocusThe Primary Problem SolvedThe Technical Stumbling BlockThe AI Engineer’s Job
    1. Prompt Engineering
    (2022-2023)
    Expression: Shaping the tone, persona, and output formatting of the LLM.Hallucinations: Cannot conjure up data or facts it wasn’t trained on.Writing clear, contextual instructions and few-shot examples.
    2. Context Engineering
    (2023-2024)
    Information: Injecting real-time, proprietary data via RAG and Vector DBs.Passivity: The model can read your data, but cannot act on it or use tools.Managing token budgets, chunking data, and optimizing semantic retrieval.
    3. Harness Engineering
    (2024-2025)
    Environment: Giving the model tools, secure sandboxes, and safety guardrails.Rigidity: Linear chains break completely when the model makes a minor mistake.Building secure execution environments, MCP servers, and input/output parsers.
    4. Loop Engineering
    (2025-2026)
    Autonomy: Letting the system evaluate, self-correct, and iterate recursively.Compute Cost: High token consumption due to repetitive agent cycles.Designing state machines, testable termination conditions, and exit criteria.

    “It’s all vibecoding, right”

    The term “vibe-coding” was officially coined by Andrej Karpathy (former Director of AI at Tesla and co-founder of OpenAI) in a viral post on X (Twitter) on February 8, 2025.

    And it’s a term that I despise, despite fully understanding how fitting that term was at the time.

    Fast forward 1.5 years, and now “vibe-coding” sounds silly and very passe, as to me it implies sitting down with a chat window and an idea, and nothing more. This is what kids do in the basement to build a fun yet forgettable android game, right?

    And this is not where we are today. Today we are knee-deep in learning, and imagining what the next step is in this agentic evolution.

    Planning, context, guardrails, loops, governance, frameworks, and harnesses is where we are, at least mainly in theory, and sometimes in practice.