AI

Agent workflows should outlive their models

I migrated scheduled agent workflows from OpenCode to Codex without rewriting their rules, and it changed how I design durable automation.

On this page
  1. The workflow is the product
  2. GitHub Actions is a useful boundary
  3. Keep the contract small
  4. Make replacement boring

I do not want my agent workflows to belong to a model. A scheduled code review, an architecture scan, or an implementation loop should describe the work it needs to do. The model and the agent harness should be replaceable parts of the machine that runs it.

I recently tested that idea in my own automation. I moved a set of reusable GitHub Actions workflows from OpenCode to the Codex CLI. The migration touched the runtime, authentication, and generated metadata. It did not require me to redesign the workflow lifecycle. That distinction is the part worth keeping.

The work started with a dual-platform workflow sync, then moved reusable workflows to Codex. A self-hosted pull request review job consumes that shared workflow. Those projects gave me a useful test of whether the abstraction was real or something I had only claimed in documentation.

The workflow is the product

The durable part of an agent system is the lifecycle. Mine has stages such as reading an issue, planning the work, implementing it, reviewing the result, and opening a pull request. The exact stages vary by job, but they are all independent of whether a session runs through OpenCode, Codex, or another harness.

That is why skills became the canonical format in the sync repository. A skill contains the procedure, constraints, and expected result. OpenCode commands are thin adapters. Codex agent metadata is generated alongside them. The sync tool can route the same source to Codex, OpenCode, or both, while validating paths and manifests and removing old generated files explicitly.

This is the same principle behind my earlier argument that AI configuration should live in portable plain text. The file format is not the whole solution, but it gives the workflow a home outside a particular chat product. The instructions are mine. The runtime interprets them.

The important design decision is where to draw the line. Workflow intent belongs in the source skill and the GitHub Actions job. Runtime details belong in an adapter that can be replaced without changing the intent.

GitHub Actions is a useful boundary

GitHub Actions gives the workflow a stable place to live. It handles triggers, inputs, permissions, repository checkout, runner selection, and the relationship between a reusable workflow and its caller. Those concerns remain meaningful even when the agent command changes.

The reusable workflows now call Codex, verify that the CLI is installed and authenticated, and pass model-specific settings as inputs. The caller still asks for a PR review on the same pull request events. It still grants the permissions the job needs. It still gets the same kind of result: a review, labels, fixes, commits, or a clear failure.

That makes the action workflow an orchestration layer rather than a Codex wrapper. It knows when the work runs and what evidence the job must produce. The adapter knows how to start a particular agent.

Actions can also apply deterministic rules before starting an agent. For example, a scheduled job can use the GitHub API to check whether any open pull requests have merge conflicts. If there are none, it can finish without spending tokens on an agent. If there are conflicts, it can hand only that work to the agent for resolution.

I have been thinking about this as a second level of harness engineering. The harness around one session controls context, tools, and local rules. The GitHub Actions layer controls how sessions become a repeatable process. Keeping those layers separate means a runtime migration does not leak through every workflow file.

Keep the contract small

When I design a reusable agent workflow now, I want the contract to answer a few practical questions:

  • What event or manual input starts the job?
  • What repository context and permissions does it receive?
  • What should the agent inspect, change, and verify?
  • What output proves that the job completed its responsibility?

The contract should not answer which vendor owns the model. That belongs in the adapter inputs and the runner configuration. Model selection, CLI arguments, login checks, and harness-specific skill invocation can change without changing what a PR review means.

This also changes how I think about choosing models. I moved to OpenAI because the newer GPT-5 models were a better fit for the work I wanted these jobs to do. That is a runtime decision. If I later move to Claude or back to OpenCode, I should be changing an invocation and its validation, not rebuilding the issue-to-PR lifecycle.

The reverse is useful too. If the workflow is poorly defined, changing models will not save it. A vague job remains vague with a different CLI. Portability forces me to put the useful detail in the workflow itself: the scope, the constraints, the checks, and the definition of done.

Make replacement boring

The goal is not to avoid ever choosing a model. Models have different strengths, interfaces, and operational requirements. The goal is to stop that choice from becoming an architectural commitment.

I want to be able to try a better model because it fits a task, then keep the surrounding automation. That is only possible when the workflow owns the process and the harness owns execution. GitHub Actions supplies the schedule and the boundary. Skills supply the durable intent. The runtime is the part I should be able to swap.

If changing an agent requires rewriting the workflow, the workflow was coupled to the wrong layer. Build the process so that the next migration is boring. That is how an automation system becomes something you can keep using after the model underneath it changes.

← Older
Agentic Engineering Patterns
Newer →
Agent Skills

Newsletter

A weekly newsletter on React, Next.js, AI-assisted development, and engineering. No spam, unsubscribe any time.