# 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`

```python
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`

```python
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`

```python
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:

```python
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/
```

```python
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())`).

---

## Related Documentation

- [Resource System API](../api/resource.md) — `ResourceHandler` protocol and built-in handlers
- [ADR-043 Devcontainer Integration](../adr/ADR-043-devcontainer-integration.md) — design rationale
- [Resource System](../api/resource.md#devcontainerhandler) — `DevcontainerHandler` CRUD operations
