Files
cleveragents-core/docs/modules/devcontainer-discovery.md
HAL9000 988a169831
CI / benchmark-regression (push) Has been skipped
CI / benchmark-publish (push) Failing after 41s
CI / quality (push) Failing after 1m4s
CI / security (push) Failing after 1m4s
CI / unit_tests (push) Failing after 1m3s
CI / build (push) Failing after 45s
CI / push-validation (push) Successful in 47s
CI / helm (push) Successful in 42s
CI / lint (push) Successful in 1m33s
CI / e2e_tests (push) Failing after 45s
CI / integration_tests (push) Failing after 47s
CI / typecheck (push) Successful in 1m43s
CI / docker (push) Has been skipped
CI / coverage (push) Has been skipped
CI / status-check (push) Failing after 6s
CI / benchmark-publish (pull_request) Has been skipped
CI / helm (pull_request) Successful in 50s
CI / build (pull_request) Successful in 57s
CI / lint (pull_request) Successful in 1m9s
CI / quality (pull_request) Successful in 1m18s
CI / benchmark-regression (pull_request) Failing after 1m16s
CI / typecheck (pull_request) Successful in 1m30s
CI / security (pull_request) Successful in 1m36s
CI / e2e_tests (pull_request) Successful in 3m52s
CI / integration_tests (pull_request) Successful in 4m30s
CI / push-validation (pull_request) Successful in 20s
CI / unit_tests (pull_request) Successful in 6m31s
CI / docker (pull_request) Successful in 1m36s
CI / coverage (pull_request) Successful in 12m19s
CI / status-check (pull_request) Successful in 4s
docs: add InvariantReconciliationActor API docs, devcontainer discovery module guide, and mkdocs nav
- docs/api/actor.md: Add InvariantReconciliationActor section documenting
  the built-in reconciliation actor introduced in v3.8.0, including the
  reconciliation algorithm, ReconciliationResult/ConflictRecord/ScopeInvariants
  data classes, standalone reconcile_invariants() function, DI registration,
  and failure behaviour.

- docs/modules/devcontainer-discovery.md: New module guide for the
  devcontainer auto-discovery system (v3.8.0 fix #2615), covering
  DevcontainerDiscoveryResult, discover_devcontainers(), is_trigger_type(),
  monorepo named-config support, and gotchas.

- mkdocs.yml: Add 'Devcontainer Auto-Discovery' to the Modules nav section,
  alongside shell-safety, uko-provenance, invariant-reconciliation,
  context-hydration, and git-worktree-sandbox module docs.

- robot/coverage_threshold.robot: Add tdd_issue and tdd_issue_4305 tags
  to the Noxfile Contains Coverage Threshold Constant test case.

ISSUES CLOSED: #4485
2026-05-05 06:15:57 +00:00

5.4 KiB

Devcontainer Auto-Discovery

Package: cleveragents.resource.handlers.discovery Introduced: v3.8.0 (issue #2615)

This module guide covers the devcontainer auto-discovery system, which automatically detects devcontainer.json configurations when a git-checkout or fs-directory resource is linked to a project. It supports both root-level configurations and named configurations for monorepo projects.


Purpose

When a git-checkout or fs-directory resource is registered, the discover_devcontainers() function scans the resource location for devcontainer.json files. Each valid configuration is returned as a DevcontainerDiscoveryResult, which the resource registry uses to create child devcontainer-instance resources automatically.


Scan Paths

The scanner checks three categories of paths:

Category Path config_name
Root-level (default) .devcontainer/devcontainer.json None
Root-level (flat) .devcontainer.json None
Named configuration .devcontainer/<name>/devcontainer.json "<name>"

Named configurations are discovered by iterating one subdirectory level inside .devcontainer/. Each subdirectory that contains a devcontainer.json produces a separate result with config_name set to the subdirectory name (e.g. "api", "frontend").


Key Classes and Functions

DevcontainerDiscoveryResult

from cleveragents.resource.handlers.discovery import DevcontainerDiscoveryResult

result = DevcontainerDiscoveryResult(
    config_path=Path("/workspace/.devcontainer/api/devcontainer.json"),
    config_data={"name": "API Dev Container", "image": "mcr.microsoft.com/devcontainers/python:3.12"},
    parent_location="/workspace",
    config_name="api",
)
Attribute Type Description
config_path Path Absolute path to the devcontainer.json file
config_data dict[str, object] Parsed JSON content of the devcontainer config
parent_location str Filesystem path of the parent resource
config_name str | None Named configuration identifier, or None for root-level configs

discover_devcontainers

from cleveragents.resource.handlers.discovery import discover_devcontainers

results = discover_devcontainers(
    resource_location="/workspace",
    resource_type="git-checkout",
)
for result in results:
    print(result.config_name, result.config_path)

Scans a resource location for all valid devcontainer configurations. Invalid JSON files are skipped with a warning log rather than raising.

Parameters:

Parameter Type Description
resource_location str Filesystem path of the parent resource
resource_type str Type name of the parent resource

Returns: list[DevcontainerDiscoveryResult] — one entry per valid config.

Raises:

  • ValueError — if resource_location is empty or resource_type is empty/blank.

is_trigger_type

from cleveragents.resource.handlers.discovery import is_trigger_type

is_trigger_type("git-checkout")   # True
is_trigger_type("sqlite")         # False

Returns True if the given resource type triggers devcontainer auto-discovery. Currently only "git-checkout" and "fs-directory" are trigger types.


Monorepo Example

Given a monorepo with the following layout:

/workspace/
├── .devcontainer/
│   ├── api/
│   │   └── devcontainer.json    → config_name="api"
│   └── frontend/
│       └── devcontainer.json    → config_name="frontend"
└── src/

discover_devcontainers("/workspace", "git-checkout") returns two results:

results = discover_devcontainers("/workspace", "git-checkout")
# results[0].config_name == "api"
# results[1].config_name == "frontend"

Each result produces a distinct devcontainer-instance resource in the registry, allowing plans to target individual containers by name.


Root-Level Example

For a single-container project:

/workspace/
├── .devcontainer/
│   └── devcontainer.json    → config_name=None
└── src/
results = discover_devcontainers("/workspace", "git-checkout")
# results[0].config_name is None

Root-level configs retain config_name=None for full backward compatibility.


Gotchas

  • Non-trigger types return an empty list. Calling discover_devcontainers() with a resource type that is not "git-checkout" or "fs-directory" returns [] immediately without scanning the filesystem.
  • Invalid JSON is silently skipped. Files that cannot be parsed as JSON objects are logged at WARNING level and excluded from results.
  • Only one subdirectory level is scanned. Named configurations must be directly inside .devcontainer/<name>/devcontainer.json. Deeper nesting is not supported.
  • Sorted order. Named configurations are returned in lexicographic order of their subdirectory names (via sorted(dc_dir.iterdir())).