---
adr_number: 39
title: Container and Execution Environment Resource Types
status_history:
- - '2026-02-21'
  - Proposed
  - Jeffrey Phillips Freeman
- - '2026-02-21'
  - Accepted
  - Jeffrey Phillips Freeman
tier: 4
authors:
- Jeffrey Phillips Freeman
superseded_by: null
related_adrs:
- number: 8
  title: Resource System
  relationship: These types extend the built-in resource type registry
- number: 36
  title: Resource DAG Operational Semantics
  relationship: Container types create new sandbox domains subject to the sandbox boundary algebra
- number: 38
  title: Cross-Mechanism Sandbox Coordination
  relationship: Container/host equivalence is the primary use case for cross-mechanism coordination
- number: 40
  title: LSP Resource Types
  relationship: LSP servers may run inside containers, referencing container executables
- number: 42
  title: Resource Type Inheritance
  relationship: Provides the inheritance mechanism enabling subtypes of container-instance (e.g., devcontainer-instance)
- number: 43
  title: Devcontainer Integration and Container-Project Association
  relationship: Defines devcontainer-instance as a subtype of container-instance with auto-detection, container-project association patterns, and execution environment routing
acceptance:
  votes_for:
  - voter: Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com>
    comment: Engine-agnostic container types with executable discovery enable tool dependency resolution and cross-mechanism coordination
  votes_against: []
  abstentions: []
---
## Context

CleverAgents operates on diverse resources — git repositories, filesystems, databases, APIs — but the current 24 built-in resource types do not model containerized environments. Modern development workflows depend heavily on containers for reproducible builds, isolated tool execution, and consistent development environments. Tools, LSP servers, and other executables may be available only inside specific containers. The system needs resource types that represent container engines, images, instances, mounts, volumes, and execution environments so that the resource DAG can reason about tool availability, sandbox coordination across host and container boundaries, and equivalence between host files and container-mounted files.

These types must be **engine-agnostic** — representing containers abstractly regardless of whether the underlying engine is Docker, Podman, containerd, LXC/LXD, CRI-O, or another runtime.

## Decision Drivers

- Modern development workflows depend on containers for reproducible builds and isolated tool execution, but the current 24 built-in resource types cannot model containerized environments
- Tools, LSP servers, and executables may only be available inside specific containers; the system must reason about tool availability across host and container boundaries
- Container types must be engine-agnostic — Docker, Podman, containerd, LXC/LXD, and CRI-O must be supported without structural changes to the DAG
- Bind-mounted files create virtual equivalence between host and container filesystem paths, requiring cross-mechanism sandbox coordination (ADR-038)
- Executable discovery within containers is essential for answering "where can I run this tool?" in heterogeneous environments

## Decision

CleverAgents adds **seven new built-in physical resource types** for modeling containerized environments, plus one type for executable discovery. These types integrate into the existing resource DAG with containment edges, virtual equivalence links (for files accessible through both host and container), and reference edges (for tool/executable dependencies).

## Design

### New Resource Types

#### Container Layer (7 types)

##### `container-runtime`

The container engine installation — a running daemon or CLI tool that manages containers.

| Field | Value |
|-------|-------|
| **Kind** | physical |
| **Sandbox strategy** | `none` |
| **User-addable** | yes |
| **CLI arguments** | `--engine-type` (required: docker, podman, containerd, lxc, lxd, cri-o, other), `--socket-path` (optional), `--endpoint` (optional), `--version` (optional) |
| **Allowed parents** | (none — top-level resource) |
| **Allowed children** | `container-image` (0..\*), `container-instance` (0..\*), `container-volume` (0..\*), `container-network` (0..\*) |
| **Auto-discovery** | Discovers running instances, pulled images, created volumes, and configured networks from the engine |
| **Capabilities** | read: true, write: false, sandbox: false, checkpoint: false |

Properties: `engine_type` (docker \| podman \| containerd \| lxc \| lxd \| cri-o \| other), `socket_path` or `endpoint`, `version`, `api_version`.

##### `container-image`

An immutable container image (OCI image, LXC image, etc.).

| Field | Value |
|-------|-------|
| **Kind** | physical |
| **Sandbox strategy** | `none` (images are immutable) |
| **User-addable** | yes |
| **CLI arguments** | `--image` (required, e.g., `python:3.12-slim`), `--digest` (optional), `--engine-type` (optional, inherited from parent runtime) |
| **Allowed parents** | `container-runtime` (1) |
| **Allowed children** | `container-instance` (0..\*, instances created from this image) |
| **Auto-discovery** | Discovered from `container-runtime`. Does not auto-discover children (instances are discovered from the runtime). |
| **Capabilities** | read: true, write: false, sandbox: false, checkpoint: false |

Properties: `image_ref`, `digest`, `engine_type`, `architecture`, `os`, `size_bytes`, `created_at`, `labels` (key-value).

##### `container-instance`

A container — running, stopped, paused, or created.

| Field | Value |
|-------|-------|
| **Kind** | physical |
| **Sandbox strategy** | `snapshot` |
| **User-addable** | yes |
| **CLI arguments** | `--container-id` (optional; ID of an existing container to register), `--image` (required when `--container-id` is not provided; container image reference, e.g., `ubuntu:latest`), `--mount` (optional, repeatable; mount a resource or host path into the container, format: `RESOURCE_OR_PATH:CONTAINER_PATH[:MODE]`; see [ADR-043 §3.2](ADR-043-devcontainer-integration.md)), `--clone-into` (optional; clone a repository into the container on first start, format: `REPO_URL:CONTAINER_PATH`), `--engine-type` (optional, inherited from parent runtime) |
| **Allowed parents** | `container-runtime` (1), `container-image` (0..1, the image it was created from) |
| **Allowed children** | `container-mount` (0..\*), `container-exec-env` (1), `container-port` (0..\*) |
| **Auto-discovery** | Discovered from `container-runtime`. Auto-discovers mounts, execution environment, and port mappings. |
| **Capabilities** | read: true, write: true, sandbox: true, checkpoint: true |

Properties: `container_id`, `name`, `state` (running \| stopped \| paused \| created \| restarting \| removing \| dead), `image_ref`, `engine_type`, `pid`, `created_at`, `labels` (key-value), `hostname`, `working_dir`.

The `snapshot` sandbox strategy for container instances creates a container checkpoint or commit (engine-dependent), allowing rollback to a known state. For Docker/Podman this uses `docker commit` or `docker checkpoint`; for LXC/LXD this uses LXD snapshots.

##### `container-mount`

A mount point mapping external storage into a container.

| Field | Value |
|-------|-------|
| **Kind** | physical |
| **Sandbox strategy** | (inherits from `container-instance` parent) |
| **User-addable** | no (auto-discovered from container-instance) |
| **Allowed parents** | `container-instance` (1) |
| **Allowed children** | `fs-directory` (0..1, the mounted content root), `fs-file` (0..\*, if a single file is mounted) |
| **Auto-discovery** | Discovered from `container-instance` by inspecting container mount configuration. Auto-discovers the mounted filesystem contents as `fs-directory` / `fs-file` children. |
| **Capabilities** | read: true, write: (depends on mount read_only flag), sandbox: (inherits), checkpoint: (inherits) |

Properties: `source` (host path, volume name, or tmpfs), `target` (container-internal path), `mount_type` (bind \| volume \| tmpfs \| npipe), `read_only`, `propagation` (rprivate \| private \| rshared \| shared \| rslave \| slave).

**Virtual equivalence**: When `mount_type` is `bind`, the `fs-directory` or `fs-file` children inside the container are equivalent to the corresponding `fs-directory` or `fs-file` resources on the host. They share the same `directory` or `file` virtual parent. The coherence property on this equivalence link is `transparent` — changes on either side are immediately visible to the other because they share the same underlying storage.

When `mount_type` is `volume`, the filesystem contents are independent of the host filesystem (unless the volume is backed by a host directory, in which case it behaves like a bind mount). Coherence: `independent` by default, `transparent` if the volume driver provides pass-through semantics.

##### `container-volume`

A named volume managed by the container engine.

| Field | Value |
|-------|-------|
| **Kind** | physical |
| **Sandbox strategy** | `snapshot` |
| **User-addable** | yes |
| **CLI arguments** | `--volume-name` (required), `--driver` (optional, default: local), `--engine-type` (optional, inherited from parent runtime) |
| **Allowed parents** | `container-runtime` (1) |
| **Allowed children** | `fs-directory` (0..1, root of volume contents) |
| **Auto-discovery** | Discovered from `container-runtime`. Auto-discovers the root directory of the volume. |
| **Capabilities** | read: true, write: true, sandbox: true, checkpoint: true |

Properties: `volume_name`, `driver`, `mount_point` (host path where volume data is stored), `engine_type`, `labels` (key-value), `scope` (local \| global).

##### `container-exec-env`

The execution environment inside a container — what is available to run.

| Field | Value |
|-------|-------|
| **Kind** | physical |
| **Sandbox strategy** | (inherits from `container-instance` parent) |
| **User-addable** | no (auto-discovered from container-instance) |
| **Allowed parents** | `container-instance` (1) |
| **Allowed children** | `executable` (0..\*) |
| **Auto-discovery** | Discovered from `container-instance`. Auto-discovers executables by scanning the container's PATH directories. |
| **Capabilities** | read: true, write: false, sandbox: (inherits), checkpoint: (inherits) |

Properties: `path_dirs` (list of PATH entries), `env_vars` (key-value environment variables), `shell` (default shell path), `os` (linux \| windows \| macos), `architecture` (amd64 \| arm64 \| ...), `user` (default user), `working_dir`.

This resource type answers the question: "What can I run in this container?" It is the foundation for tool and LSP server dependency resolution — by discovering what executables are available, the system can determine whether a particular tool or LSP server can run inside a given container.

##### `container-network`

A container network configuration.

| Field | Value |
|-------|-------|
| **Kind** | physical |
| **Sandbox strategy** | `none` |
| **User-addable** | no (auto-discovered from container-runtime) |
| **Allowed parents** | `container-runtime` (1) |
| **Allowed children** | (none) |
| **Auto-discovery** | Discovered from `container-runtime`. |
| **Capabilities** | read: true, write: false, sandbox: false, checkpoint: false |

Properties: `network_name`, `driver` (bridge \| host \| overlay \| macvlan \| none), `subnet`, `gateway`, `scope` (local \| swarm \| global).

##### `container-port`

A port mapping exposing a container port to the host.

| Field | Value |
|-------|-------|
| **Kind** | physical |
| **Sandbox strategy** | (inherits from `container-instance` parent) |
| **User-addable** | no (auto-discovered from container-instance) |
| **Allowed parents** | `container-instance` (1) |
| **Allowed children** | (none) |
| **Auto-discovery** | Discovered from `container-instance`. |
| **Capabilities** | read: true, write: false, sandbox: (inherits), checkpoint: (inherits) |

Properties: `container_port`, `host_port`, `host_ip`, `protocol` (tcp \| udp \| sctp).

#### Execution Environment Layer (1 type)

##### `executable`

A binary, script, or interpreter available in some execution environment.

| Field | Value |
|-------|-------|
| **Kind** | physical |
| **Sandbox strategy** | `none` (executables themselves are not sandboxed; the environment they run in is) |
| **User-addable** | yes |
| **CLI arguments** | `--path` (required, path to the executable), `--name` (optional, defaults to filename), `--version` (optional) |
| **Allowed parents** | `container-exec-env` (0..1, if inside a container), `fs-directory` (0..1, if on host filesystem) |
| **Allowed children** | (none) |
| **Auto-discovery** | Discovered from `container-exec-env` (scanning container PATH) or from `fs-directory` (scanning host PATH). |
| **Capabilities** | read: true, write: false, sandbox: false, checkpoint: false |

Properties: `path` (absolute path within the execution environment), `name` (the executable name, e.g., `node`, `python3`, `pylsp`), `version` (if detectable), `interpreter` (for scripts: `python`, `bash`, `node`, etc.), `content_hash` (SHA-256 of the binary).

**Virtual equivalence**: The same executable installed in multiple locations (host, multiple containers) can share a `file` virtual parent when the content hash matches. This enables the query: "Where else is this executable available?"

**Tool dependency relationship**: Tools and LSP servers declare dependencies on executables via `references` edges. A tool that requires `node` v18+ has a `references` edge to the `executable` resource for `node`. The system can then query: "Which `container-exec-env` or host `fs-directory` contains a `node` executable matching the version constraint?"

### Containment Hierarchy Summary

```
container-runtime (docker/podman/containerd/lxc/lxd/cri-o)
  ├── container-image
  │     └── (instances created from this image link back)
  ├── container-instance
  │     ├── container-mount
  │     │     ├── fs-directory  ──[virtual equivalence]──▷ directory (virtual)
  │     │     └── fs-file       ──[virtual equivalence]──▷ file (virtual)
  │     ├── container-exec-env
  │     │     └── executable    ──[virtual equivalence]──▷ file (virtual)
  │     └── container-port
  ├── container-volume
  │     └── fs-directory
  └── container-network
```

### Sandbox Domain Impact

Container resource types create new sandbox domains in the DAG:

| Sandbox Boundary | Strategy | Domain Contents |
|------------------|----------|-----------------|
| `container-instance` | `snapshot` | All mounts, exec-env, ports, and their descendants within the container |
| `container-volume` | `snapshot` | The volume's filesystem contents |

These domains are **separate from** host filesystem sandbox domains. When a bind mount creates virtual equivalence between container files and host files, the files exist in two different sandbox domains simultaneously. This is the primary use case for the cross-mechanism coordination protocol defined in ADR-038.

### Engine-Specific Behavior

The `container-runtime` handler dispatches to engine-specific sub-handlers based on `engine_type`:

| Engine | Discovery | Instance Management | Snapshot Mechanism |
|--------|-----------|--------------------|--------------------|
| Docker | Docker Engine API / `docker` CLI | `docker inspect`, `docker ps` | `docker commit`, `docker checkpoint` (experimental) |
| Podman | Podman API / `podman` CLI | `podman inspect`, `podman ps` | `podman commit`, `podman container checkpoint` |
| containerd | containerd API / `ctr` CLI | `ctr containers list` | `ctr snapshots` |
| LXC/LXD | LXD REST API / `lxc` CLI | `lxc list`, `lxc info` | `lxc snapshot` |
| CRI-O | CRI gRPC API | `crictl ps`, `crictl inspect` | Engine-dependent |

The engine-specific details are encapsulated in the resource handler — the rest of the system interacts with the engine-agnostic resource types.

## Constraints

- All container resource types must work with any engine type. Engine-specific behavior is encapsulated in the handler, not exposed in the resource type or DAG structure.
- `container-exec-env` auto-discovery (scanning PATH for executables) may be expensive for containers with large PATH directories. Discovery should be lazy (triggered on demand or when a tool dependency needs resolution) rather than eager.
- `container-mount` equivalence with host files requires the system to resolve bind mount source paths to registered host resources. If the host directory is not registered as a resource, no equivalence link is created.
- `container-instance` snapshot sandbox strategy depends on engine support. Not all engines support checkpointing (e.g., Docker checkpoint is experimental). When checkpointing is unavailable, the system falls back to `none` strategy with a warning.

## Consequences

### Positive
- Container environments become first-class citizens in the resource DAG, enabling tool dependency resolution, sandbox coordination, and equivalence tracking across host and container boundaries.
- Engine-agnostic types ensure the system works with Docker, Podman, LXC/LXD, and any future container runtime without structural changes.
- The `executable` type enables answering "where can I run this tool?" — a critical question for heterogeneous environments.

### Negative
- Nine new resource types (including `container-port` and `container-network`) add complexity to the type registry and documentation.
- Container discovery requires engine-specific handler implementations for each supported engine.
- Executable discovery (scanning PATH) adds overhead and may produce large numbers of auto-discovered resources.

### Risks
- Container state is volatile — instances can be stopped/removed externally without the system knowing. Resource staleness detection and cleanup are critical.
- Bind mount detection requires inspecting container configuration, which may require elevated privileges or engine API access.
- Snapshot-based sandboxing for containers may be slow for large container filesystems.

## Alternatives Considered

**Docker-only resource types** — Simpler but locks the system to one engine. The container ecosystem has multiple viable engines, and users working with Podman, LXC/LXD, or containerd would be excluded.

**No container resource types (external tool management only)** — Treats containers as opaque external environments. Tools running in containers would be modeled as MCP servers with no visibility into the container's internals. This prevents tool dependency resolution and cross-mechanism equivalence tracking.

## Compliance

- **Type registration tests**: Each new resource type validates correctly (CLI args, parent/child constraints, capabilities).
- **Auto-discovery tests**: Container runtime discovery correctly identifies instances, images, volumes, networks for at least Docker and Podman.
- **Mount equivalence tests**: Bind mount files correctly link to host files through virtual parents with `transparent` coherence.
- **Executable discovery tests**: Container exec-env scanning correctly identifies executables and links them to `file` virtual parents when content hashes match host executables.
- **Engine abstraction tests**: The same test suite runs against Docker and Podman handlers to verify engine-agnostic behavior.
