About this course
<p>Software automation is good at turning instructions into actions.</p>
<p>A pipeline can deploy a release, Terraform can provision infrastructure, a runbook can restart a service, or an agent can inspect telemetry and choose a remediation step.</p>
<p>But all of those systems share the same weakness: they're usually better at executing instructions than at representing the intent behind those instructions.</p>
<p>That gap is easy to ignore while automation is simple. If a script only runs one command, the script itself may be enough to explain what should happen.</p>
<p>But as systems become more distributed, more policy-driven, and more autonomous, execution logic starts carrying responsibilities it was never designed to own.</p>
<p>A deployment workflow may now encode:</p>
<pre><code class="language-text">what should be deployed
where it may run
which constraints must hold
which evidence must be checked
who may approve the change
when rollback is required
which failures are acceptable
</code></pre>
<p>At that point, the automation is no longer just executing. It's also becoming the place where operational intent is defined.</p>
<p>And that's a problem.</p>
<p>Because when intent and execution are fused together, changing the executor can also change the meaning of the operation.</p>
<p>In <a href="https://www.freecodecamp.org/news/executable-operational-specifications-software-automation/">my previous article</a>, I introduced the idea of an executable operational specification: a machine-readable description of what an operation is supposed to achieve and how its outcome can be evaluated.</p>
<p>In this article, I want to go one level deeper.</p>
<p>The central question is:</p>
<blockquote>
<p>What should exist between human intent and the system that executes it?</p>
</blockquote>
<p>I will explore:</p>
<ul>
<li><p>why automation tools often become accidental specifications</p>
</li>
<li><p>why intent and execution evolve at different speeds</p>
</li>
<li><p>what gets lost when intent is embedded in pipelines and scripts</p>
</li>
<li><p>why desired state is useful but still incomplete</p>
</li>
<li><p>how to introduce an explicit operational intent layer</p>
</li>
<li><p>how preconditions, constraints, evidence, and recovery rules fit into that layer</p>
</li>
<li><p>how execution plans differ from operational specifications</p>
</li>
<li><p>how this structure helps with AI agents</p>
</li>
<li><p>and what this separation still can't solve</p>
</li>
</ul>
<p>The goal isn't to add another abstraction for its own sake. The goal is to make operational decisions easier to understand, evaluate, and change without rewriting the meaning of the operation every time the execution mechanism changes.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You should be comfortable with:</p>
<ul>
<li><p>key software architecture concepts</p>
</li>
<li><p>CI/CD</p>
</li>
<li><p>infrastructure automation</p>
</li>
<li><p>observability</p>
</li>
<li><p>distributed systems</p>
</li>
<li><p>basic policy concepts</p>
</li>
<li><p>TypeScript or a similar language</p>
</li>
</ul>
<p>You don't need a specific orchestration tool or cloud platform. The examples are deliberately small and tool-independent.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-automation-tools-often-become-accidental-specifications">Automation Tools Often Become Accidental Specifications</a></p>
</li>
<li><p><a href="#heading-intent-and-execution-change-at-different-speeds">Intent and Execution Change at Different Speeds</a></p>
</li>
<li><p><a href="#heading-what-gets-lost-when-intent-lives-inside-the-executor">What Gets Lost When Intent Lives Inside the Executor</a></p>
</li>
<li><p><a href="#heading-desired-state-helps-but-its-not-the-whole-intent">Desired State Helps, But It's Not the Whole Intent</a></p>
</li>
<li><p><a href="#heading-the-missing-layer">The Missing Layer</a></p>
</li>
<li><p><a href="#heading-a-small-deployment-example">A Small Deployment Example</a></p>
</li>
<li><p><a href="#heading-separate-preconditions-from-execution-steps">Separate Preconditions from Execution Steps</a></p>
</li>
<li><p><a href="#heading-separate-constraints-from-the-mechanism">Separate Constraints from the Mechanism</a></p>
</li>
<li><p><a href="#heading-make-evidence-requirements-explicit">Make Evidence Requirements Explicit</a></p>
</li>
<li><p><a href="#heading-define-recovery-before-execution-starts">Define Recovery Before Execution Starts</a></p>
</li>
<li><p><a href="#heading-operational-specification-vs-execution-plan">Operational Specification vs Execution Plan</a></p>
</li>
<li><p><a href="#heading-why-this-matters-for-ai-agents">Why This Matters for AI Agents</a></p>
</li>
<li><p><a href="#heading-a-minimal-typescript-model">A Minimal TypeScript Model</a></p>
</li>
<li><p><a href="#heading-a-practical-workflow">A Practical Workflow</a></p>
</li>
<li><p><a href="#heading-what-this-separation-doesnt-solve">What This Separation Doesn't Solve</a></p>
</li>
<li><p><a href="#heading-from-automation-logic-to-operational-contracts">From Automation Logic to Operational Contracts</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-automation-tools-often-become-accidental-specifications">Automation Tools Often Become Accidental Specifications</h2>
<p>Consider a deployment workflow. It may begin as something simple:</p>
<pre><code class="language-yaml">steps:
- build
- test
- deploy
</code></pre>
<p>Then production requirements appear.</p>
<p>You add:</p>
<pre><code class="language-text">approval
region validation
health checks
canary percentage
rollback logic
error-rate thresholds
latency thresholds
artifact verification
</code></pre>
<p>Eventually, the pipeline becomes the only place where the operation is fully described.</p>
<p>That creates an accidental architecture:</p>
<pre><code class="language-text">Operational intent
↓
pipeline configuration
↓
execution
</code></pre>
<p>The pipeline is now doing two jobs: it describes <strong>what the operation means</strong> and <strong>how the operation is executed</strong>.</p>
<p>Those responsibilities are related, but they're not identical.</p>
<p>Suppose this workflow says:</p>
<pre><code class="language-yaml">deploy:
image: orders:v42
verify:
errorRateBelow: 0.01
rollback:
whenVerificationFails: true
</code></pre>
<p>That may work perfectly. But now imagine moving the deployment to another platform.</p>
<p>If the operational rules only exist inside this pipeline, you must rediscover and reimplement them while moving the executor.</p>
<p>The real migration becomes:</p>
<pre><code class="language-text">move execution mechanism
+
reconstruct operational intent
</code></pre>
<p>That's much more dangerous than just changing tooling.</p>
<h2 id="heading-intent-and-execution-change-at-different-speeds">Intent and Execution Change at Different Speeds</h2>
<p>Operational intent is often relatively stable.</p>
<p>For example:</p>
<pre><code class="language-text">Deploy the approved version.
Maintain availability.
Do not exceed the error-rate threshold.
Keep rollback possible.
Do not deploy outside the approved region.
</code></pre>
<p>Those expectations may remain valid for years. But execution mechanisms change much more frequently.</p>
<p>A team may move through:</p>
<pre><code class="language-text">shell scripts
Jenkins
GitHub Actions
Argo CD
Kubernetes operators
cloud-native deployment services
AI agents
</code></pre>
<p>If intent is tightly coupled to those mechanisms, every tooling change risks becoming an operational redesign.</p>
<p>That creates unnecessary churn.</p>
<p>You want:</p>
<pre><code class="language-text">stable intent
↓
replaceable execution
</code></pre>
<p>not:</p>
<pre><code class="language-text">new executor
↓
rediscover the meaning of the operation
</code></pre>
<p>This separation becomes more important as automation becomes more sophisticated.</p>
<p>The more intelligence the executor contains, the easier it becomes for intent to disappear inside implementation details.</p>
<h2 id="heading-what-gets-lost-when-intent-lives-inside-the-executor">What Gets Lost When Intent Lives Inside the Executor</h2>
<p>When operational intent is embedded directly in scripts and pipelines, several things become harder.</p>
<h3 id="heading-1-review">1. Review</h3>
<p>A reviewer may see:</p>
<pre><code class="language-yaml">if: error_rate < 0.01
</code></pre>
<p>but not know whether that threshold is:</p>
<pre><code class="language-text">a business requirement
an SRE policy
a temporary rollout rule
a historical workaround
</code></pre>
<p>The value is executable. Its meaning isn't explicit.</p>
<h3 id="heading-2-auditability">2. Auditability</h3>
<p>Six months later, somebody asks:</p>
<blockquote>
<p>Why was this deployment allowed?</p>
</blockquote>
<p>The answer may require reconstructing:</p>
<pre><code class="language-text">pipeline version
feature flags
runtime variables
dashboards
approvals
operator decisions
</code></pre>
<h3 id="heading-3-portability">3. Portability</h3>
<p>Changing tools means reimplementing rules.</p>
<h3 id="heading-4-testing">4. Testing</h3>
<p>You can test whether the pipeline works. But it's harder to test whether the operational intent itself is complete.</p>
<h3 id="heading-5-governance">5. Governance</h3>
<p>Authorization rules may become buried in workflow code.</p>
<p>For example:</p>
<pre><code class="language-text">production changes require approval
</code></pre>
<p>may be implemented as a platform-specific branch protection rule instead of being represented as part of the operation.</p>
<h3 id="heading-6-ai-execution">6. AI Execution</h3>
<p>An AI agent may generate a valid sequence of commands while violating an unstated operational constraint.</p>
<p>The problem isn't that the executor is weak. It's that the intent isn't independently represented.</p>
<h2 id="heading-desired-state-helps-but-its-not-the-whole-intent">Desired State Helps, But It's Not the Whole Intent</h2>
<p>Many modern systems already use desired-state models.</p>
<p>For example:</p>
<pre><code class="language-yaml">replicas: 3
image: orders:v42
</code></pre>
<p>That's useful.</p>
<p>The system can compare:</p>
<pre><code class="language-text">desired state
vs.
observed state
</code></pre>
<p>and reconcile the difference.</p>
<p>But operational intent often includes more than final configuration.</p>
<p>Suppose the desired state is:</p>
<pre><code class="language-text">orders:v42
replicas: 3
</code></pre>
<p>The operation may also require:</p>
<pre><code class="language-text">do not reduce availability below 99.9%
do not deploy if payment dependency is degraded
keep error rate below 1%
require approval for production
rollback if latency exceeds 400 ms
</code></pre>
<p>Those aren't simply target-state properties.</p>
<p>Some are:</p>
<pre><code class="language-text">preconditions
runtime constraints
evidence requirements
governance rules
recovery rules
</code></pre>
<p>Desired state is part of operational intent, but it's not always the entire operational intent.</p>
<h2 id="heading-the-missing-layer">The Missing Layer</h2>
<p>A more explicit architecture looks like this:</p>
<pre><code class="language-text">Human / organizational intent
↓
Operational specification
↓
Execution planning / binding
↓
Executor
↓
Actions
↓
Evidence
↓
Evaluation
</code></pre>
<p>The operational specification is the missing layer.</p>
<p>Its job is not to execute commands. Its job is to preserve the meaning of the operation independently from the mechanism performing it.</p>
<p>That layer can contain:</p>
<pre><code class="language-text">objective
scope
preconditions
constraints
required evidence
authorization rules
recovery conditions
</code></pre>
<p>The executor then answers a different question:</p>
<blockquote>
<p>How can I satisfy this specification using the tools available to me?</p>
</blockquote>
<p>That's a cleaner separation.</p>
<h2 id="heading-a-small-deployment-example">A Small Deployment Example</h2>
<p>Suppose we want to deploy version <code>v42</code> of the Orders service.</p>
<p>The operational objective is:</p>
<pre><code class="language-text">Deploy Orders v42 to production.
</code></pre>
<p>But that's not enough.</p>
<p>We also know:</p>
<pre><code class="language-text">the payment dependency must be healthy
at least 3 replicas must remain available
error rate must remain below 1%
p95 latency must remain below 400 ms
production deployment requires approval
rollback must remain possible
</code></pre>
<p>We can represent those concerns separately.</p>
<pre><code class="language-typescript">type DeploymentIntent = {
service: string;
version: string;
environment: string;
preconditions: {
paymentDependencyHealthy: boolean;
approved: boolean;
};
constraints: {
minAvailableReplicas: number;
maxErrorRate: number;
maxP95LatencyMs: number;
};
recovery: {
rollbackOnViolation: boolean;
};
};
</code></pre>
<p>Then:</p>
<pre><code class="language-typescript">const intent: DeploymentIntent = {
service: "orders",
version: "v42",
environment: "production",
preconditions: {
paymentDependencyHealthy: true,
approved: true,
},
constraints: {
minAvailableReplicas: 3,
maxErrorRate: 0.01,
maxP95LatencyMs: 400,
},
recovery: {
rollbackOnViolation: true,
},
};
</code></pre>
<p>Notice what's missing. There's no:</p>
<pre><code class="language-text">kubectl
Terraform
GitHub Actions
Argo
cloud API
</code></pre>
<p>The intent doesn't yet care how the deployment happens.</p>
<p>That's deliberate.</p>
<h2 id="heading-separate-preconditions-from-execution-steps">Separate Preconditions from Execution Steps</h2>
<p>A precondition answers:</p>
<blockquote>
<p>Is this operation allowed to start?</p>
</blockquote>
<p>That's different from:</p>
<blockquote>
<p>Which step should run first?</p>
</blockquote>
<p>For example:</p>
<pre><code class="language-text">payment dependency must be healthy
production approval must exist
artifact must be signed
maintenance window must be open
</code></pre>
<p>These aren't execution instructions. They're conditions that should be true before execution begins.</p>
<p>A simple evaluator might look like this:</p>
<pre><code class="language-typescript">type Preconditions = {
paymentDependencyHealthy: boolean;
approved: boolean;
};
function canStart(
preconditions: Preconditions
): boolean {
return (
preconditions.paymentDependencyHealthy &&
preconditions.approved
);
}
</code></pre>
<p>If:</p>
<pre><code class="language-typescript">canStart({
paymentDependencyHealthy: false,
approved: true,
});
</code></pre>
<p>returns:</p>
<pre><code class="language-text">false
</code></pre>
<p>the operation shouldn't begin.</p>
<p>That decision shouldn't depend on whether the executor is Kubernetes, a script, or an AI agent. The precondition belongs to the operational intent.</p>
<h2 id="heading-separate-constraints-from-the-mechanism">Separate Constraints from the Mechanism</h2>
<p>Constraints answer what must remain true while or after this operation runs.</p>
<p>Examples:</p>
<pre><code class="language-text">replicas >= 3
error rate <= 1%
latency <= 400 ms
cost increase <= 20%
region must remain us-east-1
</code></pre>
<p>An executor may use many different strategies to satisfy them.</p>
<p>For example, to maintain three replicas:</p>
<pre><code class="language-text">Kubernetes may use a rolling deployment
another platform may use blue/green deployment
an agent may temporarily scale capacity before switching traffic
</code></pre>
<p>The mechanism differs, but the constraint does not.</p>
<p>That's exactly why the constraint should live outside the executor.</p>
<h2 id="heading-make-evidence-requirements-explicit">Make Evidence Requirements Explicit</h2>
<p>A constraint is only useful if you can evaluate it.</p>
<p>Suppose you define:</p>
<pre><code class="language-text">error rate <= 1%
</code></pre>
<p>Then you need a source of evidence.</p>
<p>For example:</p>
<pre><code class="language-text">Prometheus
Datadog
CloudWatch
OpenTelemetry
custom application metrics
</code></pre>
<p>The specification shouldn't necessarily hard-code one vendor. But it should say that evidence is required.</p>
<p>For example:</p>
<pre><code class="language-typescript">type EvidenceRequirement = {
name: string;
metric: string;
};
const evidenceRequirements:
EvidenceRequirement[] = [
{
name: "availability",
metric: "availableReplicas",
},
{
name: "errors",
metric: "errorRate",
},
{
name: "latency",
metric: "p95LatencyMs",
},
];
</code></pre>
<p>The executor or evidence collector can decide where those values come from. The specification defines what must be known. That distinction is important.</p>
<h2 id="heading-define-recovery-before-execution-starts">Define Recovery Before Execution Starts</h2>
<p>Many operational workflows define rollback only after something goes wrong. But that's too late. Recovery is part of operational intent.</p>
<p>For example:</p>
<pre><code class="language-text">If error rate exceeds 1%, stop rollout.
If p95 latency exceeds 400 ms, restore previous version.
If evidence is unavailable, require human review.
</code></pre>
<p>These aren't just implementation details. They express acceptable operational behavior.</p>
<p>A simple representation might be:</p>
<pre><code class="language-typescript">type RecoveryPolicy = {
rollbackOnConstraintViolation: boolean;
requireHumanReviewWhenEvidenceMissing:
boolean;
};
</code></pre>
<p>The executor can implement rollback differently. The policy still belongs to the operation.</p>
<h2 id="heading-operational-specification-vs-execution-plan">Operational Specification vs Execution Plan</h2>
<p>This distinction is important.</p>
<p>An operational specification answers:</p>
<pre><code class="language-text">What should happen?
What must be true?
What evidence is required?
What happens if constraints fail?
</code></pre>
<p>An execution plan answers:</p>
<pre><code class="language-text">Which actions will we perform?
In what order?
Using which tools?
</code></pre>
<p>For example:</p>
<h3 id="heading-specification">Specification</h3>
<pre><code class="language-text">Deploy Orders v42.
Keep at least 3 replicas available.
Error rate <= 1%.
Rollback if the constraint is violated.
</code></pre>
<h3 id="heading-execution-plan">Execution plan</h3>
<pre><code class="language-text">1. Scale Orders to 6 replicas.
2. Deploy v42 to 1 replica.
3. Route 10% of traffic.
4. Wait 5 minutes.
5. Check metrics.
6. Increase traffic.
7. Remove old replicas.
</code></pre>
<p>The plan is one possible way to satisfy the specification.</p>
<p>Another executor might produce a different plan. That's useful. It means the same operational intent can survive changes in tooling and execution strategy.</p>
<h2 id="heading-why-this-matters-for-ai-agents">Why This Matters for AI Agents</h2>
<p>This separation becomes especially important when the executor is an AI agent.</p>
<p>A traditional script usually has a predictable path while an agent may decide dynamically.</p>
<p>For the same incident, it might choose:</p>
<pre><code class="language-text">restart
scale
reroute
disable feature
change configuration
</code></pre>
<p>If correctness is defined by following one exact sequence, dynamic execution becomes difficult to verify.</p>
<p>But if correctness is defined by an operational specification, the agent has flexibility.</p>
<p>For example:</p>
<pre><code class="language-text">Objective:
restore checkout availability
Preconditions:
payment database reachable
Constraints:
authentication remains enabled
no payment duplication
error rate <= 1%
cost increase <= 20%
Evidence:
checkout success rate
duplicate payment count
error rate
cost delta
Recovery:
escalate if constraints cannot be satisfied
</code></pre>
<p>The agent can choose different actions, but its behavior is still bounded.</p>
<p>That gives us:</p>
<pre><code class="language-text">Intent
↓
Specification
↓
Agent-generated plan
↓
Execution
↓
Evidence
↓
Evaluation
</code></pre>
<p>The agent is free to decide <strong>how</strong>. It's not free to redefine <strong>what success means</strong>.</p>
<p>That distinction will become increasingly important as autonomous systems gain more operational authority.</p>
<h2 id="heading-a-minimal-typescript-model">A Minimal TypeScript Model</h2>
<p>We can combine these ideas into a small generic model.</p>
<pre><code class="language-typescript">type OperationalSpec<TContext, TEvidence> = {
name: string;
canStart(
context: TContext
): boolean;
evaluate(
evidence: TEvidence
): EvaluationResult;
recovery: RecoveryPolicy;
};
type EvaluationResult = {
conformant: boolean;
failures: string[];
};
type RecoveryPolicy = {
rollbackOnConstraintViolation: boolean;
};
</code></pre>
<p>For a deployment:</p>
<pre><code class="language-typescript">type DeploymentContext = {
paymentDependencyHealthy: boolean;
approved: boolean;
};
type DeploymentEvidence = {
version: string;
replicas: number;
errorRate: number;
p95LatencyMs: number;
};
</code></pre>
<p>Then:</p>
<pre><code class="language-typescript">const deploymentSpec:
OperationalSpec<
DeploymentContext,
DeploymentEvidence
> = {
name: "deploy-orders-v42",
canStart: (context) =>
context.paymentDependencyHealthy &&
context.approved,
evaluate: (evidence) => {
const failures: string[] = [];
if (evidence.version !== "v42") {
failures.push(
"wrong-version"
);
}
if (evidence.replicas < 3) {
failures.push(
"insufficient-replicas"
);
}
if (evidence.errorRate > 0.01) {
failures.push(
"error-rate-too-high"
);
}
if (
evidence.p95LatencyMs > 400
) {
failures.push(
"latency-too-high"
);
}
return {
conformant:
failures.length === 0,
failures,
};
},
recovery: {
rollbackOnConstraintViolation:
true,
},
};
</code></pre>
<p>Now execution can be handled elsewhere.</p>
<p>For example:</p>
<pre><code class="language-typescript">interface DeploymentExecutor {
execute(
spec: typeof deploymentSpec
): Promise<DeploymentEvidence>;
}
</code></pre>
<p>That executor could be backed by:</p>
<pre><code class="language-text">Kubernetes
a cloud platform
a test harness
a local simulator
an AI agent
</code></pre>
<p>The specification remains the reference point.</p>
<h2 id="heading-a-practical-workflow">A Practical Workflow</h2>
<p>If you want to introduce this separation into an existing automation system, start with one operation.</p>
<h3 id="heading-1-pick-a-repeated-operation">1. Pick a Repeated Operation</h3>
<p>For example:</p>
<pre><code class="language-text">deploy service
rotate certificate
scale worker pool
restore backup
</code></pre>
<h3 id="heading-2-extract-the-objective">2. Extract the Objective</h3>
<p>Write down what the operation is trying to achieve. Avoid commands.</p>
<h3 id="heading-3-extract-preconditions">3. Extract Preconditions</h3>
<p>Ask:</p>
<blockquote>
<p>What must already be true before this operation can begin?</p>
</blockquote>
<h3 id="heading-4-extract-constraints">4. Extract Constraints</h3>
<p>Ask:</p>
<blockquote>
<p>What must remain true during or after execution?</p>
</blockquote>
<h3 id="heading-5-define-evidence">5. Define Evidence</h3>
<p>For every constraint, identify the observation required to evaluate it.</p>
<h3 id="heading-6-define-recovery">6. Define Recovery</h3>
<p>Decide what should happen when evidence shows that a constraint was violated.</p>
<h3 id="heading-7-leave-execution-separate">7. Leave Execution Separate</h3>
<p>Let the current automation tool remain the executor. Don't rewrite everything at once.</p>
<h3 id="heading-8-evaluate-the-existing-executor">8. Evaluate the Existing Executor</h3>
<p>Ask whether the current script, pipeline, or agent satisfies the extracted specification.</p>
<p>This is useful because the separation can be introduced incrementally. You don't need a new operations platform to begin.</p>
<h2 id="heading-what-this-separation-doesnt-solve">What This Separation Doesn't Solve</h2>
<p>Separating intent from execution doesn't automatically make operations correct.</p>
<p>You can still have:</p>
<pre><code class="language-text">wrong objectives
bad thresholds
missing evidence
misleading metrics
conflicting constraints
unsafe executors
security bugs
incorrect recovery policies
</code></pre>
<p>The specification itself can be wrong. That matters.</p>
<p>A formal representation doesn't make an assumption true. It only makes the assumption easier to inspect and evaluate.</p>
<p>There's also a cost. More explicit operational intent means more artifacts to maintain.</p>
<p>If specifications aren't versioned and reviewed, they can become stale. If evidence definitions are weak, conformance results can become misleading. And some operational decisions remain difficult to formalize.</p>
<p>Human judgment doesn't disappear. The value comes from making more of that judgment explicit.</p>
<h2 id="heading-from-automation-logic-to-operational-contracts">From Automation Logic to Operational Contracts</h2>
<p>There's another way to think about this separation.</p>
<p>A mature operational specification starts to behave like a contract.</p>
<p>It says:</p>
<pre><code class="language-text">This operation may begin under these conditions.
It must preserve these constraints.
It must produce this evidence.
Its outcome will be evaluated using these rules.
If it fails, these recovery conditions apply.
</code></pre>
<p>That's much stronger than:</p>
<pre><code class="language-text">run this workflow
</code></pre>
<p>The workflow is an implementation. The operational contract describes what that implementation is responsible for achieving.</p>
<p>This gives us a structure like:</p>
<pre><code class="language-text">Intent
↓
Operational contract
↓
Execution plan
↓
Executor
↓
Evidence
↓
Evaluation
</code></pre>
<p>That separation creates room for something important:</p>
<pre><code class="language-text">multiple execution strategies
for the same operational intent
</code></pre>
<p>And once that's possible, another question appears:</p>
<blockquote>
<p>Can two different executors satisfy the same operational specification?</p>
</blockquote>
<p>That's the question I want to explore next.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Automation has made software operations faster and more repeatable.</p>
<p>But execution is only one part of an operation. We still need to know:</p>
<pre><code class="language-text">why the operation is allowed
what outcome is intended
what constraints must hold
what evidence is required
what happens when the constraints fail
</code></pre>
<p>When all of that logic lives inside a pipeline, script, or agent, the executor becomes the accidental owner of operational meaning.</p>
<p>That coupling makes systems harder to review, migrate, audit, and verify.</p>
<p>A better separation is:</p>
<pre><code class="language-text">Intent
↓
Operational specification
↓
Execution plan
↓
Executor
↓
Evidence
↓
Evaluation
</code></pre>
<p>The specification describes <strong>what success means</strong>. The executor decides <strong>how to achieve it</strong>.</p>
<p>That distinction becomes increasingly valuable as execution mechanisms become more dynamic. Especially when those executors are AI agents.</p>
<p>Because the more freedom we give a system to decide how work gets done, the more explicit we need to be about what the work is supposed to achieve.</p>