About this course
<p>In <a href="https://www.freecodecamp.org/news/software-automation-intent-execution-layer/">my previous article</a>, I argued that software automation needs a layer between operational intent and execution.</p>
<p>The reason is simple: the specification should describe what success means, and the executor should decide how to achieve it.</p>
<p>That separation creates a useful possibility.</p>
<p>If the operational specification is independent from the execution mechanism, then more than one executor should be able to satisfy the same specification.</p>
<p>That sounds straightforward. But in practice, it raises several difficult questions:</p>
<pre><code class="language-text">Can two different executors achieve the same operational outcome?
How do we compare them?
What must remain stable when the executor changes?
Which differences are acceptable?
What evidence should each executor produce?
How do we know whether executor independence is real or just theoretical?
</code></pre>
<p>These questions matter because modern software systems rarely keep one execution mechanism forever.</p>
<p>Teams change:</p>
<pre><code class="language-text">CI/CD platforms
cloud providers
deployment systems
infrastructure tools
orchestration engines
incident automation
AI agents
</code></pre>
<p>If changing the executor also changes the meaning of the operation, then the system isn't really specification-driven. The executor still owns too much of the intent.</p>
<p>In this article, I’ll show you how to separate an operational specification from its executors, run the same specification through two different implementations, collect evidence, compare outcomes, and identify where executor independence breaks down.</p>
<p>The goal isn't to prove that two executors behave identically internally.</p>
<p>The goal is to determine whether they can satisfy the same operational contract.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You should be comfortable with:</p>
<ul>
<li><p>software architecture</p>
</li>
<li><p>interfaces and dependency inversion</p>
</li>
<li><p>TypeScript or a similar language</p>
</li>
<li><p>CI/CD and deployment concepts</p>
</li>
<li><p>observability</p>
</li>
<li><p>basic testing</p>
</li>
<li><p>operational specifications</p>
</li>
</ul>
<p>You don't need Kubernetes, Terraform, or any specific cloud platform. The examples are intentionally small and in-memory so the architecture stays visible.</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-what-executor-independence-actually-means">What Executor Independence Actually Means</a></p>
</li>
<li><p><a href="#heading-why-executor-independence-matters">Why Executor Independence Matters</a></p>
</li>
<li><p><a href="#heading-start-with-one-stable-operational-specification">Start with One Stable Operational Specification</a></p>
</li>
<li><p><a href="#heading-define-an-executor-contract">Define an Executor Contract</a></p>
</li>
<li><p><a href="#heading-build-a-first-executor">Build a First Executor</a></p>
</li>
<li><p><a href="#heading-build-a-second-executor">Build a Second Executor</a></p>
</li>
<li><p><a href="#heading-run-the-same-specification-through-both-executors">Run the Same Specification Through Both Executors</a></p>
</li>
<li><p><a href="#heading-compare-evidence-not-internal-steps">Compare Evidence, Not Internal Steps</a></p>
</li>
<li><p><a href="#heading-normalize-executor-specific-evidence">Normalize Executor-Specific Evidence</a></p>
</li>
<li><p><a href="#heading-separate-execution-evidence-from-conformance">Separate Execution Evidence from Conformance</a></p>
</li>
<li><p><a href="#heading-what-counts-as-equivalent-execution">What Counts as Equivalent Execution?</a></p>
</li>
<li><p><a href="#heading-where-executor-independence-usually-breaks">Where Executor Independence Usually Breaks</a></p>
</li>
<li><p><a href="#heading-how-to-test-executor-portability">How to Test Executor Portability</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-small-end-to-end-example">A Small End-to-End Example</a></p>
</li>
<li><p><a href="#heading-a-practical-workflow">A Practical Workflow</a></p>
</li>
<li><p><a href="#heading-what-executor-independence-does-not-mean">What Executor Independence Does Not Mean</a></p>
</li>
<li><p><a href="#heading-from-replaceable-tools-to-stable-operational-intent">From Replaceable Tools to Stable Operational Intent</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-executor-independence-actually-means">What Executor Independence Actually Means</h2>
<p>Executor independence means that the operational specification remains valid even if the mechanism that performs the work changes.</p>
<p>For example, suppose the specification says:</p>
<pre><code class="language-text">Deploy Orders version v42.
Constraints:
- at least 3 replicas available
- error rate <= 1%
- p95 latency <= 400 ms
- rollback must remain possible
</code></pre>
<p>One executor may implement that using:</p>
<pre><code class="language-text">Kubernetes rolling deployment
</code></pre>
<p>Another may use:</p>
<pre><code class="language-text">blue/green deployment
</code></pre>
<p>Another may use:</p>
<pre><code class="language-text">a managed cloud deployment service
</code></pre>
<p>And later, an AI agent may generate its own execution plan.</p>
<p>If every executor can be evaluated against the same specification, then the specification is doing its job.</p>
<p>Conceptually:</p>
<pre><code class="language-text"> ┌── Executor A
Operational Spec ───┼── Executor B
├── Executor C
└── AI Agent
</code></pre>
<p>The specification stays stable while the execution mechanism changes.</p>
<p>That's executor independence.</p>
<h2 id="heading-why-executor-independence-matters">Why Executor Independence Matters</h2>
<p>Software teams replace tools constantly. A deployment workflow might move from:</p>
<pre><code class="language-text">Jenkins
↓
GitHub Actions
↓
Argo CD
↓
Kubernetes operator
</code></pre>
<p>If every migration requires rediscovering:</p>
<pre><code class="language-text">what success means
what constraints matter
what evidence is required
when rollback is allowed
</code></pre>
<p>then the operational meaning was never truly independent from the old tool.</p>
<p>This creates several risks.</p>
<ol>
<li><p>Tool lock-in: The system may be technically portable while the operational rules are not.</p>
</li>
<li><p>Hidden behavior changes: A new executor may preserve the same deployment steps but lose an important constraint.</p>
</li>
<li><p>Reimplementation drift: Teams may recreate the old behavior approximately rather than exactly.</p>
</li>
<li><p>Audit gaps: It becomes difficult to prove that the new executor preserves the same operational contract.</p>
</li>
</ol>
<p>Executor independence gives you a stronger migration target: preserve the specification, replace the mechanism.</p>
<h2 id="heading-start-with-one-stable-operational-specification">Start with One Stable Operational Specification</h2>
<p>To test executor independence, first define something stable.</p>
<p>For example:</p>
<pre><code class="language-typescript">type DeploymentSpec = {
service: string;
version: string;
minReplicas: number;
maxErrorRate: number;
maxP95LatencyMs: number;
};
</code></pre>
<p>Then:</p>
<pre><code class="language-typescript">const spec: DeploymentSpec = {
service: "orders",
version: "v42",
minReplicas: 3,
maxErrorRate: 0.01,
maxP95LatencyMs: 400,
};
</code></pre>
<p>This specification shouldn't contain:</p>
<pre><code class="language-text">kubectl
helm
terraform
AWS
Azure
Argo
GitHub Actions
</code></pre>
<p>Those belong to executors.</p>
<p>The specification describes the operational contract.</p>
<h2 id="heading-define-an-executor-contract">Define an Executor Contract</h2>
<p>Now define the minimum interface an executor must satisfy.</p>
<p>For example:</p>
<pre><code class="language-typescript">type ExecutionEvidence = {
deployedVersion: string;
availableReplicas: number;
errorRate: number;
p95LatencyMs: number;
};
interface DeploymentExecutor {
execute(
spec: DeploymentSpec
): Promise<ExecutionEvidence>;
}
</code></pre>
<p>This interface doesn't say how deployment happens.</p>
<p>It only says:</p>
<pre><code class="language-text">given a specification,
perform the operation,
return evidence
</code></pre>
<p>In this article, <strong>evidence</strong> means the observable facts produced or collected after execution that let us evaluate what actually happened. For a deployment, that might include the version that is running, the number of available replicas, the measured error rate, and p95 latency.</p>
<p>Evidence isn't the executor's opinion that the operation succeeded. It's the data we can compare against the specification.</p>
<p>That's important. If the executor interface includes tool-specific concepts, portability starts leaking.</p>
<p>For example, this would be more coupled:</p>
<pre><code class="language-typescript">interface DeploymentExecutor {
executeKubectlCommand(
namespace: string,
manifestPath: string
): Promise<void>;
}
</code></pre>
<p>Now the interface already assumes Kubernetes.</p>
<p>That's not executor-independent.</p>
<h2 id="heading-build-a-first-executor">Build a First Executor</h2>
<p>Let’s create a simple in-memory executor.</p>
<pre><code class="language-typescript">class RollingDeploymentExecutor
implements DeploymentExecutor {
async execute(
spec: DeploymentSpec
): Promise<ExecutionEvidence> {
return {
deployedVersion: spec.version,
availableReplicas:
spec.minReplicas,
errorRate: 0.004,
p95LatencyMs: 280,
};
}
}
</code></pre>
<p>This executor simulates a rolling deployment.</p>
<p>Internally, you can imagine that it:</p>
<pre><code class="language-text">starts new replicas
waits for health
gradually replaces old replicas
</code></pre>
<p>But none of that appears in the specification. The executor owns the mechanism.</p>
<h2 id="heading-build-a-second-executor">Build a Second Executor</h2>
<p>Now create a different strategy.</p>
<pre><code class="language-typescript">class BlueGreenExecutor
implements DeploymentExecutor {
async execute(
spec: DeploymentSpec
): Promise<ExecutionEvidence> {
return {
deployedVersion: spec.version,
availableReplicas:
spec.minReplicas + 2,
errorRate: 0.003,
p95LatencyMs: 260,
};
}
}
</code></pre>
<p>This executor may conceptually:</p>
<pre><code class="language-text">create a parallel environment
verify it
switch traffic
keep the old environment available
</code></pre>
<p>Its internal process is different, but its evidence shape is the same. That means both can be evaluated against the same operational specification.</p>
<h2 id="heading-run-the-same-specification-through-both-executors">Run the Same Specification Through Both Executors</h2>
<p>Now execute both.</p>
<pre><code class="language-typescript">const rolling =
new RollingDeploymentExecutor();
const blueGreen =
new BlueGreenExecutor();
const rollingEvidence =
await rolling.execute(spec);
const blueGreenEvidence =
await blueGreen.execute(spec);
</code></pre>
<p>At this point, we have:</p>
<pre><code class="language-text">same specification
different executors
different internal behavior
different evidence values
</code></pre>
<p>The important question isn't whether they performed the same steps. They didn't. The question is if they both satisfied the same operational contract.</p>
<h2 id="heading-compare-evidence-not-internal-steps">Compare Evidence, Not Internal Steps</h2>
<p>Executor independence depends on comparing outcomes rather than implementation details.</p>
<p>Suppose:</p>
<pre><code class="language-text">Rolling deployment:
replicas = 3
error rate = 0.4%
p95 = 280 ms
Blue/green:
replicas = 5
error rate = 0.3%
p95 = 260 ms
</code></pre>
<p>Those outputs aren't identical, but both may be conformant. That matters.</p>
<p>Executor independence doesn't require:</p>
<pre><code class="language-text">same commands
same number of steps
same topology
same timing
same infrastructure
</code></pre>
<p>It requires:</p>
<pre><code class="language-text">same operational intent
satisfied constraints
required evidence
acceptable outcome
</code></pre>
<p>This is similar to interface-based programming. Two implementations can behave differently internally while satisfying the same contract.</p>
<h2 id="heading-normalize-executor-specific-evidence">Normalize Executor-Specific Evidence</h2>
<p>Real executors often return different evidence formats.</p>
<p>Suppose executor A returns:</p>
<pre><code class="language-json">{
"readyReplicas": 3,
"image": "orders:v42",
"latencyP95": 280
}
</code></pre>
<p>Executor B returns:</p>
<pre><code class="language-json">{
"instancesHealthy": 5,
"releaseVersion": "v42",
"p95Ms": 260
}
</code></pre>
<p>These can't be compared directly. You need adapters.</p>
<p>For example:</p>
<pre><code class="language-typescript">type CanonicalEvidence = {
version: string;
availableReplicas: number;
p95LatencyMs: number;
};
</code></pre>
<p>Adapter A:</p>
<pre><code class="language-typescript">function normalizeRolling(
raw: {
readyReplicas: number;
image: string;
latencyP95: number;
}
): CanonicalEvidence {
return {
version:
raw.image.split(":")[1],
availableReplicas:
raw.readyReplicas,
p95LatencyMs:
raw.latencyP95,
};
}
</code></pre>
<p>This adapter translates the rolling executor's native output into the canonical evidence model. It extracts the version from the image tag, maps <code>readyReplicas</code> to <code>availableReplicas</code>, and renames <code>latencyP95</code> to <code>p95LatencyMs</code>.</p>
<p>The important point is that the adapter doesn't change the operational meaning. It only converts executor-specific field names and formats into the shared representation expected by the specification layer.</p>
<p>Adapter B:</p>
<pre><code class="language-typescript">function normalizeBlueGreen(
raw: {
instancesHealthy: number;
releaseVersion: string;
p95Ms: number;
}
): CanonicalEvidence {
return {
version:
raw.releaseVersion,
availableReplicas:
raw.instancesHealthy,
p95LatencyMs:
raw.p95Ms,
};
}
</code></pre>
<p>This adapter does the same job for the blue/green executor. Its raw output uses different names, but those values represent the same operational concepts: version, available capacity, and p95 latency.</p>
<p>With both adapters in place, the rest of the system no longer needs to understand the executor-specific shapes. It can evaluate both results using the same canonical model.</p>
<p>Now both executors produce evidence that can be evaluated using the same model.</p>
<p>This is an important architectural boundary. Executor-specific evidence stays near the executor, while canonical evidence belongs to the specification layer.</p>
<h2 id="heading-separate-execution-evidence-from-conformance">Separate Execution Evidence from Conformance</h2>
<p>The executor should produce evidence. It shouldn't decide whether the operation was successful from the perspective of the specification.</p>
<p>That distinction prevents another form of coupling.</p>
<p>For example, avoid:</p>
<pre><code class="language-typescript">return {
success: true,
};
</code></pre>
<p>A generic success flag tells you very little.</p>
<p>Instead, return evidence:</p>
<pre><code class="language-typescript">return {
deployedVersion: "v42",
availableReplicas: 3,
errorRate: 0.004,
p95LatencyMs: 280,
};
</code></pre>
<p>Then evaluate it separately.</p>
<pre><code class="language-typescript">type ConformanceResult = {
conformant: boolean;
failures: string[];
};
function evaluateConformance(
spec: DeploymentSpec,
evidence: ExecutionEvidence
): ConformanceResult {
const failures: string[] = [];
if (
evidence.deployedVersion !==
spec.version
) {
failures.push(
"wrong-version"
);
}
if (
evidence.availableReplicas <
spec.minReplicas
) {
failures.push(
"insufficient-replicas"
);
}
if (
evidence.errorRate >
spec.maxErrorRate
) {
failures.push(
"error-rate-too-high"
);
}
if (
evidence.p95LatencyMs >
spec.maxP95LatencyMs
) {
failures.push(
"latency-too-high"
);
}
return {
conformant:
failures.length === 0,
failures,
};
}
</code></pre>
<p>The evaluator receives two things: the specification that defines what should be true, and the evidence that describes what was actually observed.</p>
<p>It checks each constraint independently. A wrong version adds <code>wrong-version</code>, too few replicas adds <code>insufficient-replicas</code>, and the error-rate and latency checks work the same way.</p>
<p>At the end, <code>conformant</code> is <code>true</code> only when no failures were recorded. Returning the individual failure names is useful because it explains <em>why</em> an execution didn't conform instead of collapsing everything into a generic <code>false</code>.</p>
<p>This is why the executor should return facts rather than a final verdict. The same evidence can be reevaluated later if the specification changes, if an audit needs to reconstruct the decision, or if you want to compare multiple executors using exactly the same rules.</p>
<p>Now:</p>
<pre><code class="language-text">executor → evidence
specification + evidence → conformance
</code></pre>
<p>This separation will become important later.</p>
<h2 id="heading-what-counts-as-equivalent-execution">What Counts as Equivalent Execution?</h2>
<p>Two executors don't need to produce identical evidence. They need to produce evidence that satisfies the same specification.</p>
<p>Suppose:</p>
<pre><code class="language-text">Executor A
replicas: 3
error rate: 0.4%
latency: 280 ms
Executor B
replicas: 5
error rate: 0.3%
latency: 260 ms
</code></pre>
<p>Both may pass.</p>
<p>Now suppose executor B produces:</p>
<pre><code class="language-text">replicas: 2
error rate: 0.3%
latency: 260 ms
</code></pre>
<p>It fails one constraint.</p>
<p>That means:</p>
<pre><code class="language-text">Executor A:
conformant
Executor B:
non-conformant
</code></pre>
<p>The executors are still independent implementations, but only one satisfied the specification for this execution.</p>
<p>That distinction matters.</p>
<p>Executor independence doesn't guarantee executor correctness. It only gives you a stable contract against which correctness can be evaluated.</p>
<h2 id="heading-where-executor-independence-usually-breaks">Where Executor Independence Usually Breaks</h2>
<p>There are several common failure modes.</p>
<h3 id="heading-tool-specific-fields-in-the-specification">Tool-Specific Fields in the Specification</h3>
<p>For example:</p>
<pre><code class="language-yaml">kubernetesNamespace: production
helmChart: orders
</code></pre>
<p>If those are truly implementation details, they shouldn't live in the operational specification.</p>
<h3 id="heading-executor-owned-success-rules">Executor-Owned Success Rules</h3>
<p>If each executor defines its own thresholds, the specification is no longer authoritative.</p>
<h3 id="heading-missing-evidence-normalization">Missing Evidence Normalization</h3>
<p>Different executors may expose different concepts that are never mapped into a common model.</p>
<h3 id="heading-hidden-preconditions">Hidden Preconditions</h3>
<p>Executor A may require an approval. Executor B may not.</p>
<p>If approval is part of operational intent, that rule shouldn't live only inside one executor.</p>
<h3 id="heading-hidden-recovery-behavior">Hidden Recovery Behavior</h3>
<p>One executor may rollback automatically, while another may leave the failed state running.</p>
<p>If recovery behavior matters, the specification should express that expectation.</p>
<h3 id="heading-semantic-mismatch">Semantic Mismatch</h3>
<p>Two tools may use the same words differently.</p>
<p>For example:</p>
<pre><code class="language-text">healthy
ready
available
running
</code></pre>
<p>Those concepts need explicit definitions. Otherwise executor portability is superficial.</p>
<h2 id="heading-how-to-test-executor-portability">How to Test Executor Portability</h2>
<p>You can test executor independence explicitly.</p>
<p>Start with a suite of specifications.</p>
<p>For example:</p>
<pre><code class="language-typescript">const cases: DeploymentSpec[] = [
{
service: "orders",
version: "v42",
minReplicas: 3,
maxErrorRate: 0.01,
maxP95LatencyMs: 400,
},
{
service: "payments",
version: "v18",
minReplicas: 5,
maxErrorRate: 0.005,
maxP95LatencyMs: 250,
},
];
</code></pre>
<p>Then run each specification against every executor.</p>
<pre><code class="language-typescript">const executors:
DeploymentExecutor[] = [
new RollingDeploymentExecutor(),
new BlueGreenExecutor(),
];
for (const spec of cases) {
for (const executor of executors) {
const evidence =
await executor.execute(spec);
const result =
evaluateConformance(
spec,
evidence
);
console.log({
spec: spec.service,
executor:
executor.constructor.name,
result,
});
}
}
</code></pre>
<p>This produces a useful matrix:</p>
<pre><code class="language-text"> Rolling BlueGreen
Orders v42 PASS PASS
Payments v18 PASS FAIL
</code></pre>
<p>Now you have evidence about executor portability.</p>
<p>That is much stronger than assuming that both tools support deployments, so they're equivalent.</p>
<h2 id="heading-why-this-matters-for-ai-agents">Why This Matters for AI Agents</h2>
<p>AI agents make executor independence even more interesting.</p>
<p>An AI agent may generate a new plan every time.</p>
<p>For example:</p>
<pre><code class="language-text">Execution 1:
scale
deploy
verify
route
Execution 2:
create parallel environment
verify
switch traffic
Execution 3:
deploy canary
observe
expand rollout
</code></pre>
<p>The plans differ, and the executor behavior is dynamic. That makes step-by-step equivalence unrealistic.</p>
<p>But the specification can still remain stable.</p>
<p>For example:</p>
<pre><code class="language-text">Deploy Orders v42.
Constraints:
replicas >= 3
error rate <= 1%
latency <= 400 ms
Evidence:
running version
replicas
error rate
latency
</code></pre>
<p>The AI agent can choose any acceptable plan, and its result is still evaluated using the same contract.</p>
<p>This creates a powerful boundary:</p>
<pre><code class="language-text">agent autonomy
inside
operational constraints
</code></pre>
<p>The agent can optimize execution. It can't silently redefine success.</p>
<h2 id="heading-a-small-end-to-end-example">A Small End-to-End Example</h2>
<p>Let’s put the pieces together.</p>
<p>Start with the specification:</p>
<pre><code class="language-typescript">const spec: DeploymentSpec = {
service: "orders",
version: "v42",
minReplicas: 3,
maxErrorRate: 0.01,
maxP95LatencyMs: 400,
};
</code></pre>
<p>Create two executors:</p>
<pre><code class="language-typescript">const executors:
DeploymentExecutor[] = [
new RollingDeploymentExecutor(),
new BlueGreenExecutor(),
];
</code></pre>
<p>Run them:</p>
<pre><code class="language-typescript">for (const executor of executors) {
const evidence =
await executor.execute(spec);
const conformance =
evaluateConformance(
spec,
evidence
);
console.log(
executor.constructor.name,
evidence,
conformance
);
}
</code></pre>
<p>Possible result:</p>
<pre><code class="language-text">RollingDeploymentExecutor
evidence:
version = v42
replicas = 3
error rate = 0.004
p95 = 280
conformance:
PASS
</code></pre>
<p>and:</p>
<pre><code class="language-text">BlueGreenExecutor
evidence:
version = v42
replicas = 5
error rate = 0.003
p95 = 260
conformance:
PASS
</code></pre>
<p>The executors didn't behave identically, and they didn't need to. They satisfied the same operational specification.</p>
<p>Now imagine the second executor reports:</p>
<pre><code class="language-text">replicas = 2
</code></pre>
<p>The result becomes:</p>
<pre><code class="language-text">BlueGreenExecutor
conformance:
FAIL
reason:
insufficient-replicas
</code></pre>
<p>That's exactly what we want: the specification remains stable, and the executor changes.</p>
<p>Evidence reveals whether the execution satisfies the contract.</p>
<h2 id="heading-a-practical-workflow">A Practical Workflow</h2>
<p>If you want to test executor independence in a real system, I would use this sequence.</p>
<h3 id="heading-1-pick-one-operation">1. Pick One Operation</h3>
<p>For example:</p>
<pre><code class="language-text">deploy service
restore backup
rotate certificate
scale worker pool
</code></pre>
<h3 id="heading-2-extract-the-operational-specification">2. Extract the Operational Specification</h3>
<p>Define:</p>
<pre><code class="language-text">objective
constraints
evidence requirements
recovery expectations
</code></pre>
<h3 id="heading-3-remove-tool-specific-language">3. Remove Tool-Specific Language</h3>
<p>Look for:</p>
<pre><code class="language-text">kubectl
Terraform
GitHub Actions
AWS CLI
specific resource IDs
</code></pre>
<p>Keep only what is truly part of the operational intent.</p>
<h3 id="heading-4-define-an-executor-interface">4. Define an Executor Interface</h3>
<p>Make the executor responsible for:</p>
<pre><code class="language-text">performing the operation
producing evidence
</code></pre>
<h3 id="heading-5-build-the-first-adapter">5. Build the First Adapter</h3>
<p>Wrap the current implementation, don't rewrite it.</p>
<h3 id="heading-6-build-a-second-executor">6. Build a Second Executor</h3>
<p>Use:</p>
<pre><code class="language-text">another tool
another deployment strategy
a simulator
a test double
an AI agent
</code></pre>
<h3 id="heading-7-normalize-evidence">7. Normalize Evidence</h3>
<p>Map executor-specific observations into a canonical evidence model.</p>
<h3 id="heading-8-evaluate-conformance-separately">8. Evaluate Conformance Separately</h3>
<p>Don't let the executor decide whether it passed.</p>
<h3 id="heading-9-compare-portability">9. Compare Portability</h3>
<p>Run the same specifications through multiple executors.</p>
<h3 id="heading-10-investigate-differences">10. Investigate Differences</h3>
<p>Ask:</p>
<pre><code class="language-text">Is the executor wrong?
Is the specification incomplete?
Is evidence missing?
Are the concepts actually equivalent?
</code></pre>
<p>Those differences are useful. They expose hidden coupling.</p>
<h2 id="heading-what-executor-independence-does-not-mean">What Executor Independence Does Not Mean</h2>
<p>Executor independence doesn't mean that all tools are interchangeable.</p>
<p>Different executors may have different:</p>
<pre><code class="language-text">capabilities
costs
latency
failure modes
security models
operational complexity
</code></pre>
<p>A specification may also require a capability that one executor simply can't provide.</p>
<p>For example:</p>
<pre><code class="language-text">zero-downtime deployment
</code></pre>
<p>may be feasible in one platform and impossible in another.</p>
<p>That's not a failure of the specification. It's useful information. The executor can't satisfy the contract.</p>
<p>Executor independence also doesn't mean ignoring implementation details. Implementation details still matter for:</p>
<pre><code class="language-text">performance
security
cost
reliability
maintainability
</code></pre>
<p>The point is narrower: the operational meaning shouldn't depend on one specific execution mechanism.</p>
<h2 id="heading-from-replaceable-tools-to-stable-operational-intent">From Replaceable Tools to Stable Operational Intent</h2>
<p>Software infrastructure changes constantly.</p>
<p>Tools come and go, execution strategies evolve, cloud platforms change, and agents become more capable.</p>
<p>If operational intent is embedded inside each executor, every change risks becoming a semantic migration.</p>
<p>But if intent is represented independently:</p>
<pre><code class="language-text">Operational specification
↓
stable contract
↓
replaceable executor
</code></pre>
<p>then the system becomes easier to evolve.</p>
<p>This also gives us something else:</p>
<pre><code class="language-text">evidence from multiple executors
evaluated against the same specification
</code></pre>
<p>At that point, we can stop asking:</p>
<blockquote>
<p>Did the tool finish?</p>
</blockquote>
<p>and start asking:</p>
<blockquote>
<p>To what degree did this execution conform to the specification?</p>
</blockquote>
<p>That's the next problem.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Executor independence isn't about pretending every tool is the same. It's about protecting operational intent from implementation churn.</p>
<p>A specification describes:</p>
<pre><code class="language-text">what should happen
what constraints must hold
what evidence is required
</code></pre>
<p>An executor decides:</p>
<pre><code class="language-text">how to make it happen
</code></pre>
<p>Then execution produces evidence. And evidence can be evaluated independently.</p>
<p>That gives us:</p>
<pre><code class="language-text">Specification
↓
Executor A ──→ Evidence A
Executor B ──→ Evidence B
↓
Evaluation
</code></pre>
<p>Two executors can use completely different strategies and still satisfy the same operational contract.</p>
<p>That's a useful property for ordinary automation.</p>
<p>It becomes even more important when executors are autonomous agents whose internal plans may change from one run to the next.</p>
<p>But once multiple executors can operate against the same specification, a new question becomes unavoidable:</p>
<blockquote>
<p>How should we measure conformance between the specification and what actually happened?</p>
</blockquote>
<p>That's where I want to go next.</p>