ADR-002 Engine boundaries
Generated — do not edit
This page is produced by scripts/generate-runtime-adrs.sh from
engineering-runtime/docs/04-design-decisions/adr-002-engine-boundaries.md.
Source commit: 38eec00 (2026-08-04 UTC).
Hand edits are overwritten on the next sync and fail scripts/check-generated-adrs.sh.
Change the ADR in the runtime repo, then re-run the generator.
Relative links from the source ADR are rewritten to GitHub blob URLs.
Status¶
Adopted, load-bearing, and partially amended by adr-005-provider-layer.md — read that alongside this.
The core conclusion below stands: the runtime core carries no domain knowledge, and governance is an allow-list/deny-list over binaries rather than a semantics database. What ADR-005 changed is where domain knowledge lives when it does exist. It now has a named, bounded home — the Provider — instead of being pushed entirely onto AI and the industry tool. That is a deliberate, partial move toward Model A, and ADR-005 explains why the unbounded-maintenance objection that killed Model A does not apply to a curated provider surface.
Otherwise: explicitly do not re-litigate without new information — this was already argued through in depth. If you see "Provider Catalog" used elsewhere as a design proposal, it refers to the rejected model below, not current architecture. See ../03-core-concepts/provider-catalog.md for the concept-level summary of this same decision.
This ADR was independently re-derived and re-argued later in the project's history under the title "Runtime Command Knowledge Boundary" — same decision, same conclusion, with a more concrete worked example and diagrams. That material has been merged in below (Architecture flow, Responsibility boundary, Worked example, Design principles) rather than kept as a separate ADR-002, to avoid two differently-numbered files claiming the same decision.
Context¶
Every operation initiated by a human, a CI/CD pipeline, or an AI must produce the same outcome for the same request, regardless of who or what initiated it, which AI model was involved, what OS/client was used, or how the request was phrased — see ../03-core-concepts/deterministic-execution.md.
The hardest open question in this project's design history was how the runtime should relate to the domain knowledge of the industry tools it wraps — that is, how much a Runtime Engine or Provider integration should know about the meaning of the operations it exposes, versus just executing them generically. This became known as the Provider Catalog debate, and it is the one real unresolved tension that got fully argued through and resolved.
The two models debated¶
Model A — Heavy Provider Catalogs. Each provider (GitHub, Kubernetes, OpenShift, ...) ships an explicit, versioned catalog of engineering-level operations (e.g. "GitHub Provider Catalog v1 = 40 operations," "Kubernetes = 60 operations"). AI reasons only over this fixed vocabulary (github.create_repository, kubernetes.restart_deployment), never over raw CLI syntax.
- Pro: AI's reasoning surface shrinks; the runtime fully controls what's possible.
- Con: maintaining "500 kubectl + 300 oc + 400 gcloud + 200 gh + 250 terraform operations" by hand was called out explicitly as "a full-time job" that breaks on every upstream CLI release.
Model B — Thin runtime, AI reasons over industry tools directly. AI already knows kubectl, gh, terraform, helm, gcloud, etc. from training — so a capability just emits generic steps (command.run, rest.call, file.update) with the actual industry command as a parameter. The runtime's job is limited to deterministic execution, validation, auth, logging, audit, and policy (via Binary Governance) — never to owning per-vendor domain knowledge.
Decision¶
Model B. The runtime stays thin and contains no domain knowledge.
Engineering Runtime is a deterministic execution platform that abstracts execution, while AI abstracts reasoning and industry tools abstract implementation.
Reason once. Execute deterministically.
The runtime intentionally contains no domain knowledge.
This is also the mechanism that keeps Provider integrations thin — see ../02-architecture/provider-model.md.
Amended by ADR-005. A Provider does now ship a small, curated per-operation vocabulary (
repo list,pr list,workflow run), and chooses a transport for each. What it must never do is attempt completeness — the failure mode Model A was rejected for. Escape hatches (github api,github graphql,runtime command run <binary>) keep everything outside the curated set reachable, which is what makes a small surface acceptable rather than a cage.
Why¶
- Maintenance cost of Model A is unbounded and adversarial to upstream CLI releases. A per-operation catalog for every provider is a permanent, growing liability, disconnected from how fast
kubectl/gh/terraform/gcloudactually evolve. - AI already has this knowledge. Re-encoding
kubectl/terraform/ghsyntax into the runtime duplicates what any modern AI model already knows from training — the original insight is not "teach AI these tools," it's "let AI use what it knows, deterministically, through a governed boundary." - Governance doesn't require a catalog — it requires an allow-list. Binary Governance (
allowed_binaries+ subcommand-levelcommand_policy) gives the same safety property (only approved operations run) without needing to hand-maintain per-operation semantics. - What's actually original here is the combination, not any single piece. AI agents, CLIs, automation runtimes, and workflow engines all already exist individually — the original contribution is combining them so the runtime never re-encodes industry-tool knowledge, and instead only governs execution.
Implementation of this decision¶
- The Command Engine (
internal/engine/command.go) wraps approved CLI binaries generically (git,gh,kubectl,oc,docker,helm,terraform,flux,gcloud,aws,az) — there is no per-operation registry entry for any of them. policy-config.yaml'sallowed_binaries+command_policyis exactly the governance mechanism this debate concluded was needed — see../02-architecture/engine-model.md.- ~~Runtime Commands (the REST/File Engine registry in
internal/commands) remain fixed, compiled Go data.~~ Superseded by ADR-005: the central registry andallowed_commandswere removed. Operations now belong to the Provider that owns them (providers/<name>), still fixed compiled Go data, still deliberately narrow — but no longer sitting in the runtime core. The runtime core is more domain-free after this change than before it, sinceinternal/commandsused to hold GitHub REST paths. - Policy evaluation always happens on raw arguments, before Runtime Context injection — this ordering is itself a determinism guarantee: an injected flag can only ever narrow a command, never be used to smuggle a denied operation past a policy check.
The four-layer "never knows" model¶
A durable mental model that falls directly out of this decision — see ../03-core-concepts/abstraction-layers.md for the fuller write-up:
- User never knows
kubectl/oc/gh— only intent. - AI never worries about authentication, retries, or logging — only capability construction.
- Capability never knows how commands execute or REST is implemented — only describes what to do.
- Runtime never knows Kubernetes/GitHub/GCP/Terraform domain concepts — only how to execute generically.
Clarified by ADR-005. Layer 4 now reads: the runtime core never knows them; a Provider knows exactly one platform's concepts, and nothing else in the system does. Layers 1–3 are unchanged.
Architecture flow¶
Restating the decision as a request-flow diagram — this is the shape every engineering operation takes, regardless of which industry tool it ultimately reaches:
User Intent
↓
AI Layer
(reasoning + command construction —
understands provider commands, e.g. kubectl/gh/terraform syntax)
↓
Capability Request
(a provider + operation, or binary + args)
↓
Engineering Runtime Binary
(Bootstrap → Context → Policy → Auth → Execution → Audit)
↓
Provider
(owns the operation; picks the transport — ADR-005)
↓
Command / REST / GraphQL / File Engine
(generic dispatch — no per-operation knowledge)
↓
Industry Provider Binary
(kubectl, gh, terraform, gcloud, oc, helm, ...)
The runtime does not need to understand the business meaning of the command it executes — only how to authenticate, execute, capture output, handle errors, and enforce policy. See ../02-architecture/architecture-overview.md for the full lifecycle this diagram compresses.
Responsibility boundary, restated concretely¶
| Layer | Responsible for | Not responsible for |
|---|---|---|
| AI | Understanding user intent; reasoning about provider capabilities and industry-tool command syntax (kubectl get pods, gh pr list, ...); constructing the capability/execution request |
Authentication, execution, policy enforcement, audit |
| Engineering Runtime core | Authentication, configuration, policy, dispatch, output capture, error handling | Knowing what kubectl get pods means, or that any particular platform operation exists |
| Provider (ADR-005) | One platform's curated operation surface; choosing the transport per operation | Authentication, policy, audit — the core does all three identically for every provider |
Provider Tools (kubectl, gh, terraform, gcloud, ...) |
The actual engineering operation | Anything about the runtime's execution model |
Worked example¶
User request: "Show all running pods."
- AI reasoning — AI understands the goal (retrieve Kubernetes pods) and selects the provider command:
kubectl get pods. - Capability construction — AI expresses this using the actual Runtime block grammar (see
../02-architecture/capability-model.md) — a raw Command Engine invocation, sincekubectlis an allowed binary, not a registered Runtime Command:
(Grammar updated by ADR-005; the point is unchanged. There is no Kubernetes Provider today, so this remains a raw Command Engine invocation. If one were added, the step would become provider: kubernetes / args: [pod, list] and that provider — not the capability, and not AI — would decide whether to use kubectl or the Kubernetes API.)
3. Runtime execution — the Command Engine executes kubectl get pods exactly as given, subject to Binary Governance (allowed_binaries/command_policy), then returns the output and writes an audit record. The runtime never reasoned about "pods," "Kubernetes," or what the command accomplishes — it only executed an approved binary deterministically.
Design principles, restated¶
- Runtime = Execution Platform. The runtime should not know every provider command — that's not its job, and trying to make it its job is precisely Model A above.
- AI = Engineering Intelligence. AI reasons about provider operations and constructs the request; the runtime never re-derives or second-guesses that reasoning, it only validates and executes deterministically.
- Capabilities hide command complexity from users. Users interact with Capabilities, not
kubectl/helm/terraform/gcloudsyntax directly — see../01-vision/goals.md's "Login → Execute → Logout" thesis.
A related but distinct idea: an AI-side "provider command catalog"¶
Don't confuse this with the rejected Provider Catalog (Model A) above. A runtime-owned catalog of per-operation semantics was rejected. Nothing here rules out an AI-side, runtime-external knowledge source (documentation, training data, a future capability-authoring aid) that helps AI reason about available tools/commands/syntax faster — the runtime simply never depends on it, never ships it, and never validates against it. If such a thing is ever built, it lives entirely above the Engineering Runtime boundary (in the AI/Runtime Agent layer — see ../03-core-concepts/ai-and-runtime.md), the same way a human engineer's own knowledge of kubectl lives outside the runtime today.
Consequences¶
Positive¶
- Runtime binary remains lightweight. No per-operation catalog to compile, version, or ship — the Command Engine's registry is just an allow-list, not a semantics database.
- New CLI subcommands require no runtime changes. Once a binary (
kubectl,gh,terraform, ...) is inallowed_binaries, every subcommand it supports is reachable immediately — including subcommands that didn't exist when the binary was added. - AI capability generation can evolve independently. As AI models improve their knowledge of
kubectl/terraform/ghsyntax, capabilities written against them improve too, with zero runtime changes. - The provider ecosystem can scale without runtime complexity growing per-provider. Adding a tenth or hundredth CLI tool to
allowed_binariesdoesn't add a single new code path — see../02-architecture/provider-model.md.
Trade-offs¶
- AI/provider knowledge now lives outside the runtime's control, and the runtime has no way to verify that a generated command is semantically correct — only that it's an allowed binary/command. A capability can validate cleanly and still do the wrong thing.
- Capability generation and review need to compensate for what the runtime no longer checks. Since the runtime doesn't understand "what
kubectl get podsmeans," strong validation, testing, and human/AI review become the actual safety net — not runtime-side semantics. - The runtime must provide safe execution boundaries instead of semantic ones. Binary Governance (
allowed_binaries,denied_binaries, subcommandcommand_policy) has to do all the governance work a heavier catalog would have done implicitly — see../02-architecture/engine-model.md. - Error handling stays generic, not domain-aware. The runtime surfaces a CLI's raw output/exit code; it can't translate
kubectl's error into an engineering-level explanation, because it was never taught what the command means in the first place.
Related decisions¶
- Why Go was chosen as the implementation language: mature cross-platform tooling (Cobra/Viper-style CLI ecosystem, YAML/JSON/HTTP/OAuth support), simple single-binary deployment, and a language philosophy (simple, predictable, explicit) that mirrors this same deterministic-execution philosophy. Reviewed and explicitly marked "Language Selection: Approved" during the project's architecture self-review.
- Why capabilities never get a parallel execution path — see
../02-architecture/capability-model.md. adr-001-capabilities.md— the companion decision about where engineering knowledge lives (Capabilities, not AI Skills); this ADR is about how much domain knowledge the runtime itself carries.adr-004-execution-engine-selection.md— the companion decision about who chooses the Engine (REST/Command/File) for a given operation; this ADR is about how much domain knowledge the runtime carries, ADR-004 is about who decides the execution mechanism.