September 22, 2026 · Software Architecture & AI Engineering
The most important AI architecture shift this week is not another model benchmark. It is the rise of the agent harness: the runtime layer that keeps an AI agent working, bounded and observable while it uses tools across real systems.
Models can propose a sequence of actions. Production systems still need somewhere to enforce deadlines, budgets, permissions, approvals, isolation, retries and evidence. That “somewhere” is becoming a first-class platform concern.
This matters because enterprise agents are moving from short conversations to long-running work. A coding agent may inspect a backlog, change files, execute tests and prepare a pull request. An operations agent may query telemetry, correlate an incident and recommend remediation. Once an agent can act, the quality of its runtime matters as much as the quality of its reasoning.
The harness surrounds open-ended reasoning with controlled execution paths. AI-generated conceptual illustration.
Why this is today’s top topic for enterprise Java teams
This is an editorial choice for Homann Software’s audience, not a claim about a universal search ranking. The signal is unusually concentrated across independent product ecosystems.
On September 10, OpenAI introduced its Agents API in public beta. Its managed harness supports long-running sessions, tools, execution environments, context compaction and subagents. The official Java API reference already exposes sessions, events, artifacts, turns and subagents. The beta label matters: interfaces may still change, and the platform does not remove the need for application-specific business controls. OpenAI announcement and official Java API reference
On September 11, Salesforce described an Enterprise AI Harness and a control plane spanning context, action, identity, policy, lifecycle, evaluation, cost and third-party agents. On September 15, WSO2 announced general availability of an open agent control plane with sandboxing, identity, MCP governance, OpenTelemetry tracing and evaluation. These are vendor announcements, not independent proof of outcomes, but their architectural direction is consistent. Salesforce announcement and WSO2 announcement
Atlassian is holding its State of AI SDLC event today, September 22, after announcing governed agent loops, organizational standards, AI review and measurements that connect throughput, quality, adoption and cost. Several of those features remain in early access or are scheduled for later availability, so they should be read as product direction rather than a complete platform available to every customer today. Atlassian announcement
Cisco and Splunk are making the same operational point from another angle: agent observability should include behavior, runtime guardrails and token expenditure, not only infrastructure logs. Cisco announcement
The common message is more durable than any product name: the model is a component; the harness is the operating boundary.
An agent harness is more than an SDK loop
A basic tool-calling loop can be written in a few lines: send context to a model, receive a tool request, execute it and return the result. That is useful for a prototype. It is not yet an operational contract.
A production harness has to answer questions the model cannot answer for itself:
- Which authenticated tenant, user and business purpose does this run represent?
- Which tools and resources are in scope?
- Which actions require an exact human approval?
- When must the run stop because of time, cost, call count or cancellation?
- Where does untrusted code execute, and which network routes can it reach?
- What evidence will remain after success, failure or interruption?
- How will the team compare an accepted outcome with its cost and correction effort?
The harness may be managed by a provider, built into an application platform, or assembled from internal components. The architectural responsibilities remain.
Five boundaries to make explicit
1. Identity and delegation
Every run needs a server-derived identity. Record the tenant, initiating principal, delegated purpose and expiry. Do not let model output choose its own authority.
This extends the principle from our earlier article, AI Agent Security: Put Authorization Outside the Model. The policy decision belongs at an unavoidable execution boundary.
2. Capability discovery
Give the agent a small, task-relevant tool surface. Loading every available tool increases ambiguity, context consumption and blast radius. Tool search or capability discovery can help with selection, but discovery is not authorization: a visible tool still needs a policy decision before execution.
3. Bounded execution
Deadlines, call limits and cost budgets must stop work before the next side effect. A timeout attached only to the original HTTP request is insufficient for a durable agent that can continue in the background.
Retries also belong here. Retry only operations that are safe to repeat, and use idempotency keys for actions that create or mutate state. “Try again” is not a universal recovery policy.
4. Isolation and data handling
Code execution should happen in an environment whose filesystem, secrets, processes and network access match the task. Treat documents, issues, web pages and tool results as untrusted context. Sanitize telemetry so that better observability does not become a new path for leaking credentials or personal data.
5. Evidence and evaluation
Record inputs by reference, tool proposals, policy decisions, approvals, outcomes, timing, usage and final status. Separate the live event stream from the durable audit record. Traces help engineers understand a run; business evidence must also support retention rules, access control and later review.
Measure accepted outcomes, not raw activity. Token count, tool calls and generated lines are cost signals. They do not establish that the right change reached production.
A small, executable Java harness
The following Java 21 example demonstrates the control layer around tool execution. It is deliberately independent of any model or AI vendor: a model could produce the proposed calls, but it cannot bypass the harness.
The harness:
- rejects unknown tools and duplicate call identifiers;
- enforces a deadline, call count and estimated-cost budget before execution;
- requires an exact approval key for sensitive arguments;
- stops after a failed tool call;
- emits ordered events without logging exception messages; and
- returns an immutable event list.
Save this as AgentHarness.java:
import java.time.Clock;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public final class AgentHarness {
public enum Decision { ALLOW, DENY, REQUIRE_APPROVAL }
public enum EventType { STARTED, ALLOWED, DENIED, EXECUTED, FAILED, STOPPED, COMPLETED }
public record ToolCall(String id, String tool, Map<String, String> arguments,
int estimatedCostUnits) {
public ToolCall {
Objects.requireNonNull(id, "id");
Objects.requireNonNull(tool, "tool");
arguments = Map.copyOf(Objects.requireNonNull(arguments, "arguments"));
if (id.isBlank() || tool.isBlank()) {
throw new IllegalArgumentException("id and tool must not be blank");
}
if (estimatedCostUnits < 0) {
throw new IllegalArgumentException("estimated cost must not be negative");
}
}
public String approvalKey() {
return tool + "|" + arguments;
}
}
public record Limits(int maxCalls, int maxCostUnits, Instant deadline) {
public Limits {
if (maxCalls < 1 || maxCostUnits < 0) {
throw new IllegalArgumentException("invalid limits");
}
Objects.requireNonNull(deadline, "deadline");
}
}
public record Context(String runId, String tenant, Limits limits,
Set<String> approvalKeys) {
public Context {
if (runId == null || runId.isBlank() || tenant == null || tenant.isBlank()) {
throw new IllegalArgumentException("runId and tenant are required");
}
Objects.requireNonNull(limits, "limits");
approvalKeys = Set.copyOf(Objects.requireNonNull(approvalKeys, "approvalKeys"));
}
}
public record ToolResult(boolean success, String summary) {
public ToolResult {
Objects.requireNonNull(summary, "summary");
}
}
public record Event(long sequence, Instant at, EventType type,
String callId, String detail) {}
public record RunResult(boolean completed, int callsExecuted, int costUnits,
List<Event> events) {
public RunResult {
events = List.copyOf(events);
}
}
@FunctionalInterface
public interface Policy {
Decision decide(Context context, ToolCall call);
}
@FunctionalInterface
public interface Tool {
ToolResult execute(Context context, Map<String, String> arguments) throws Exception;
}
private final Clock clock;
public AgentHarness(Clock clock) {
this.clock = Objects.requireNonNull(clock, "clock");
}
public RunResult run(Context context, List<ToolCall> proposals,
Map<String, Tool> tools, Policy policy) {
Objects.requireNonNull(context, "context");
proposals = List.copyOf(Objects.requireNonNull(proposals, "proposals"));
tools = Map.copyOf(Objects.requireNonNull(tools, "tools"));
Objects.requireNonNull(policy, "policy");
var events = new ArrayList<Event>();
var seenIds = new HashSet<String>();
int executed = 0;
int cost = 0;
add(events, EventType.STARTED, null, "run accepted");
for (ToolCall call : proposals) {
if (!seenIds.add(call.id())) {
add(events, EventType.STOPPED, call.id(), "duplicate call id");
return result(false, executed, cost, events);
}
if (executed >= context.limits().maxCalls()) {
add(events, EventType.STOPPED, call.id(), "call limit reached");
return result(false, executed, cost, events);
}
if (!clock.instant().isBefore(context.limits().deadline())) {
add(events, EventType.STOPPED, call.id(), "deadline reached");
return result(false, executed, cost, events);
}
if (call.estimatedCostUnits() > context.limits().maxCostUnits() - cost) {
add(events, EventType.STOPPED, call.id(), "cost limit reached");
return result(false, executed, cost, events);
}
Tool tool = tools.get(call.tool());
if (tool == null) {
add(events, EventType.DENIED, call.id(), "unknown tool");
return result(false, executed, cost, events);
}
Decision decision = Objects.requireNonNull(policy.decide(context, call),
"policy decision");
if (decision == Decision.DENY) {
add(events, EventType.DENIED, call.id(), "policy denied");
return result(false, executed, cost, events);
}
if (decision == Decision.REQUIRE_APPROVAL
&& !context.approvalKeys().contains(call.approvalKey())) {
add(events, EventType.DENIED, call.id(), "exact approval required");
return result(false, executed, cost, events);
}
add(events, EventType.ALLOWED, call.id(), decision.name());
try {
ToolResult toolResult = Objects.requireNonNull(
tool.execute(context, call.arguments()), "tool result");
executed++;
cost += call.estimatedCostUnits();
add(events, toolResult.success() ? EventType.EXECUTED : EventType.FAILED,
call.id(), toolResult.summary());
if (!toolResult.success()) {
return result(false, executed, cost, events);
}
} catch (Exception exception) {
executed++;
cost += call.estimatedCostUnits();
add(events, EventType.FAILED, call.id(),
exception.getClass().getSimpleName());
return result(false, executed, cost, events);
}
}
add(events, EventType.COMPLETED, null, "all proposed calls completed");
return result(true, executed, cost, events);
}
private RunResult result(boolean completed, int executed, int cost,
List<Event> events) {
return new RunResult(completed, executed, cost, events);
}
private void add(List<Event> events, EventType type, String callId, String detail) {
events.add(new Event(events.size() + 1L, clock.instant(), type, callId, detail));
}
public static Map<String, Tool> tools(Object... nameAndToolPairs) {
if (nameAndToolPairs.length % 2 != 0) {
throw new IllegalArgumentException("expected name/tool pairs");
}
var result = new LinkedHashMap<String, Tool>();
for (int i = 0; i < nameAndToolPairs.length; i += 2) {
result.put((String) nameAndToolPairs[i], (Tool) nameAndToolPairs[i + 1]);
}
return Map.copyOf(result);
}
}
The estimatedCostUnits field is intentionally abstract. In a real system it could represent a conservative reservation derived from model tokens, external API prices or an internal quota. Charge actual usage separately and reconcile the reservation after execution.
The approval key in this example uses the canonical Java representation of a map. That is sufficient for a deterministic demonstration, not for a distributed production protocol. A real approval should bind a versioned, canonical serialization of every meaningful field and be stored in trusted state with identity, expiry, revocation and single-use semantics where appropriate.
Run the tests
The companion AgentHarnessTest.java verifies success, exact approval, changed arguments, unknown tools, cost and call limits, the exact deadline boundary, duplicate identifiers, explicit tool failure, exceptions and event immutability.
// Compile AgentHarness.java together with the complete AgentHarnessTest.java
// supplied with this article's reproducible source package.
javac --release 21 -Xlint:all AgentHarness.java AgentHarnessTest.java
java AgentHarnessTest
Verified result on September 22, 2026:
21 tests passed
The sources compiled without warnings and all tests passed on Eclipse Temurin 21.0.11 and OpenJDK 27 build 27+35-2325 on Windows x64.
This verifies the deterministic behavior of the sample. It does not test a live model, distributed cancellation, sandbox isolation, transactional side effects, persistent approvals or a telemetry backend.
Where this fits in a Spring architecture
Keep the harness in the application layer, not inside a controller and not inside a prompt. A Spring-based system can expose a durable command such as StartIncidentInvestigation, persist the run identity and limits, then dispatch work to a bounded executor or workflow engine.
Tool adapters should be ordinary application ports with narrow request types. Decorate them with authentication, policy evaluation, idempotency, rate limiting and telemetry. The agent sees capabilities; the domain sees authenticated use cases.
That separation makes the model replaceable. A team can move between a managed agent API, Spring AI, an internal planner or another provider without rebuilding the rules that protect business operations.
For long-running work, model the run as a state machine with explicit terminal states such as COMPLETED, FAILED, CANCELLED, EXPIRED and WAITING_FOR_APPROVAL. A browser connection disappearing should not create an ambiguous run.
Build or buy the harness?
A managed harness can reduce undifferentiated work around sessions, context, environments and orchestration. A platform control plane can give an organization common inventory, identity, policy and observability across many agents. A custom harness can provide precise fit for regulated workflows or existing domain boundaries.
The decision should be based on responsibility, not feature count:
- Who owns isolation and incident response?
- Can you export enough evidence to investigate a failed run?
- Can policies be enforced independently of prompts and models?
- Can you cancel a run and revoke its credentials quickly?
- Can you attribute cost to a business outcome?
- What happens when a beta API or model changes?
Most organizations will use a layered answer: provider infrastructure for generic execution, internal application services for business authority, and shared observability for operational evidence.
A practical first rollout
Choose one workflow whose result is easy to review and whose side effects can remain disabled. A useful first target is incident investigation that reads telemetry and produces a proposed remediation plan without executing the remediation.
Define the identity, tools, data classification, limits and terminal states before selecting a model. Add negative tests for unauthorized resources, changed approvals, timeouts, duplicate actions and partial failures. Then run the workflow in shadow mode and compare its evidence with the work of experienced engineers.
Only widen autonomy when accepted outcomes, correction effort, failure modes and cost support the decision. Moving the merge or deployment button later is an architectural change, not a confidence setting.
AI agents are becoming operational software. Operational software needs a runtime contract.
The agent harness is where flexible reasoning meets deterministic responsibility — and that makes it one of the most consequential architecture layers emerging today.
If your team is designing governed AI workflows in Java or Spring, get in touch with Homann Software. Start with one business outcome, one bounded tool surface and evidence you can defend.