Building on the Mature Stack¶
There is not yet one mature, generic software-engineering agent framework with a decade of proven DevOps, DevSecOps or test-automation use behind it. The agent layer is still young. But there is a very mature ecosystem underneath it, and I would reuse it rather than rebuild it.
The division of labour is simple:
Let the AI decide what needs doing. Let the mature systems execute and enforce it.
Where each of these sits in the whole is shown in the reference architecture; the consolidated tool list is the stack table.
Argo Workflows¶
There are already very mature systems whose whole purpose is, essentially: give me a DAG of jobs, their dependencies, resources and execution environments, and I'll execute it reliably.
Argo Workflows is one of them. It is Kubernetes-native, represents workflows explicitly as DAGs, runs each job as a container, and uses the dependency graph to decide which jobs can execute concurrently — exactly the model on Work as a DAG.
Argo already handles most of the unglamorous infrastructure work:
- dependencies and parallel jobs
- execution state, failure and retry
- containers and resource allocation
- job lifecycle
- artefacts passed between steps
That's exactly why I would not implement my own DAG scheduler, at least initially.
Tekton is the other mature Kubernetes-native option, oriented specifically towards CI/CD: it describes itself as building blocks for constructing CI/CD systems.
| Engine | Shape |
|---|---|
| Argo Workflows | Broader, general-purpose workflow orchestration |
| Tekton | More specifically CI/CD-oriented |
For a generic modernization factory I lean towards Argo, because the jobs extend well beyond CI/CD: architecture recovery, characterization runs, differential testing, migration waves.
Replanning a living DAG¶
There is a tension to resolve here. The DAG is a plan, not a prophecy — real work discovers work. But an Argo workflow is submitted as a fixed graph.
The resolution is to keep the two roles apart:
- The planning layer owns the graph. The living DAG is state, stored alongside the work items, versioned like any other artefact.
- Each Argo run executes a snapshot — the currently ready frontier, or one wave of it.
- Results feed back. When nodes complete, fail or report discovered work, the planner adds, splits or re-orders work items and submits the next snapshot. Argo can fan out dynamically within a run, but structural change — new work, new dependencies, a different decomposition — belongs to the planner.
- Leases belong to work items, not to runs. A replan never silently hands a domain to a second writer, because the write lease survives from one snapshot to the next — see Single Write Authority.
Don't throw away Jenkins¶
Jenkins may feel old-fashioned next to AI agents, but that is part of its value. A Jenkins Pipeline already represents the automated process that takes software from source control through build, test and deployment.
So when an agent concludes "to verify that this modernization is safe, I need to run the existing regression suite", it should not recreate that test environment. It should call it:
jenkins.run(
pipeline="ultrasound-system-regression",
commit="agent/modernization-1837",
)
and consume the result.
A central idea of the whole architecture
Agents should orchestrate existing engineering knowledge rather than replace it.
That pipeline may contain ten years of institutional knowledge about how the product actually builds and gets verified. It is incredibly valuable to the agent — and it is already trusted by the people who will review the agent's work.
The same applies to GitHub Actions, GitLab CI or whatever else already gates your releases.
The other direction: CI invoking the agent¶
The boundary works in reverse too. A pipeline step can run an agent non-interactively and consume a structured result:
In the harnesses
- Claude Code —
claude -p --bare --output-format json, where--bareskips auto-discovery of hooks, skills and instruction files so the run is reproducible, and--allowedToolspins what it may do. - Codex —
codex exec --json, with--output-schemato constrain the final message and--ephemeralto avoid persisting session files on a shared runner. - OpenHands —
--headless --json, one JSON object per event — but headless always auto-approves, so the sandbox and hooks are the only remaining controls. - GitHub Copilot —
-pin the CLI for a single non-interactive run.
Two rules keep this safe: run the agent in the pipeline's own sandbox with a scoped token, and treat its output as a proposal that the same pipeline then verifies. An agent step that both writes and judges its own work has removed the gate the pipeline exists for.
Policy as code: OPA¶
Open Policy Agent lets you express rules as policy-as-code and evaluate them deterministically. It is already used for policy enforcement in CI/CD, Kubernetes, applications and API gateways — and it maps directly onto the deterministic shell around the agent described in Engineering Agents.
Policies can look like:
Agent may write: tests/**
Agent may NOT write: safety/**
Agent may run: build, test, clang-tidy
Agent may NOT: deploy production
Architecture change: requires human approval
Safety-related code: requires a second reviewer
CVE remediation: SAST must pass before commit
Note that these are all about writes and actions, never reads — consistent with Ownership: every agent may read the whole repository. In Rego, the write-scope rule is the work item's change surface checked against the domain metadata:
package agent.write
default allow := false
# A write is allowed only inside the change surface of the agent's
# active work item, and only while that item holds the domain lease.
allow if {
some pattern in input.work_item.scope.writes
glob.match(pattern, ["/"], input.path)
data.leases[input.work_item.scope.domain] == input.work_item.id
}
A harness hook is where that decision lands in practice: a PreToolUse hook queries OPA and denies
the call when the answer is no — see
How this is enforced in practice.
Notice what that means: the AI doesn't enforce these rules. OPA does. Even if the agent
concludes "I think I'm allowed to change this", the answer is simply DENIED. The
progressive-autonomy table belongs here too — it is
policy, not prose.
Backstage¶
Backstage is an open-source framework for developer portals and software catalogs. Its catalog represents components, services, libraries, data pipelines and similar engineering entities, and its templates standardize how new ones are created.
That makes it part of the world model available to agents:
Agent asks: "What is ReconstructionEngine?"
Backstage catalog →
Component: ReconstructionEngine
Owner: imaging-platform-team
Repository: ...
Language: C++
Criticality: ...
CI pipeline: ...
Dependencies: ...
Docs: ...
That is more robust than asking a model to rediscover everything from Git on every task. It is
also consistent with keeping knowledge in the repository:
catalog entries are normally declared in catalog-info.yaml files that live in the repositories
they describe, so the catalog is an index over repo-resident knowledge rather than a rival to it.
Enrich it with the domain knowledge model described in
Memory & Knowledge.
Testing: reuse, don't reinvent¶
I wouldn't invent an "AI test execution framework". Let agents invoke whatever already exists:
| Area | Existing frameworks |
|---|---|
| C / C++ | GoogleTest, CTest, Catch2 |
| Python | pytest |
| Java | JUnit |
| Web / UI | Playwright, Cypress |
| BDD | Cucumber |
| API | existing integration frameworks |
| Performance | existing benchmark harnesses |
| Medical imaging | existing image-quality and algorithm-verification tools |
That gives a very clean separation of responsibilities:
| The agent reasons | The framework determines |
|---|---|
| What is poorly tested? | Pass / fail |
| Which behaviour needs characterizing? | Expected vs actual |
| Which boundary conditions matter? | Coverage |
| Which tests should be generated? | Performance |
| Why did this test fail? | |
| Which test gives more evidence? |
The model proposes; the test framework is the source of truth. That is verifier-driven development with infrastructure you already trust.
MCP is an interoperability layer, not the architecture¶
The Model Context Protocol is useful for standardizing how agents reach tools and external systems — GitHub, Jira, the filesystem, documentation, build infrastructure — through one protocol instead of bespoke integrations.
It was created by Anthropic and has since been adopted across harnesses: Claude Code, Codex, Copilot's cloud agent and CLI, and OpenHands all act as MCP clients. That cross-vendor adoption is what makes it worth using — a tool you expose once stays reachable from whichever harness you run next year.
But MCP does not solve:
- planning
- concurrency and ownership
- verification
- memory architecture
- task scheduling
- safety policy
- recovery
It is closer to an interoperability layer. Treat MCP servers as one way of implementing the capability layer — with the credentials held by the server, never by the model.
What stays custom¶
Reuse is not the same as building nothing. The line runs like this:
| Reuse | Build |
|---|---|
| DAG execution — Argo / Tekton | Work items and the living DAG |
| CI and test execution — Jenkins, native frameworks | Ownership domains, leases, change surfaces |
| Policy evaluation — OPA | The policies themselves, derived from domains and risk classes |
| Software catalog — Backstage | The domain knowledge model on top of it |
| Isolation — worktrees, containers, Kubernetes | The reasoning layer: planning, agents, review |
The left column has an operational history. The right column is the part that encodes how your organisation works — which is why Five Primitives argues for making it first-class.