Files
freemo e28f9a8597 Docs: Add resource DAG operational semantics, tool reachability, cross-mechanism coordination, container, and LSP resource type ADRs (036-040)
- ADR-036: Resource DAG Operational Semantics (10 purposes, sandbox boundary algebra)
- ADR-037: Tool Reachability and Access Projection (transitive reach, read/write routing)
- ADR-038: Cross-Mechanism Sandbox Coordination (coherence, write-then-sync, lazy materialization)
- ADR-039: Container and Execution Environment Resource Types (9 engine-agnostic types)
- ADR-040: Language Server Protocol Resource Types (lsp-server, lsp-workspace, lsp-document)
- Updated specification.md with 7 new subsections for operational semantics
- Updated resource_dag.md reference doc with operational semantics section
- Updated ADR-008, ADR-011, ADR-015, ADR-027 with cross-references
- Resolved merge conflicts from cherry-pick renumbering (033-037 -> 036-040)
- Fixed all stale display-text references across all files
2026-02-21 19:51:28 -05:00

206 lines
6.9 KiB
Markdown

# Resource DAG
Resources in CleverAgents form a **directed acyclic graph** (DAG) where
parent resources contain or reference child resources. This document
describes the linking rules, cycle detection, type compatibility
enforcement, and auto-discovery behaviour.
## DAG Rules
| Rule | Description |
|------|-------------|
| **No self-loops** | A resource cannot be its own child. |
| **No cycles** | If resource A is an ancestor of B, then B cannot become a parent of A. |
| **Type compatibility** | The child's resource type must appear in the parent type's `child_types` list. |
| **Unique links** | A given (parent, child) pair can only be linked once. |
| **Both must exist** | Both the parent and the child resource must be registered before linking. |
## API
### `link_child(parent_id, child_id)`
Links a child resource to a parent in the DAG.
1. Validates that both resources exist in the registry.
2. Checks **type compatibility** — the child resource's type must be
listed in the parent resource type's `child_types` field.
3. Performs **cycle detection** — walks the ancestor chain of the
parent to ensure the child is not already an ancestor.
4. Persists the link in the `resource_links` table.
**Errors:**
- `ResourceNotFoundRepoError` — parent or child does not exist.
- `TypeIncompatibleError` — child type not in parent's `child_types`.
- `CycleDetectedError` — linking would create a cycle.
- `DuplicateResourceLinkError` — link already exists.
### `unlink_child(parent_id, child_id)`
Removes a parent-child link from the DAG.
1. Validates that both resources exist.
2. Validates the link exists.
3. Deletes the link from `resource_links`.
**Errors:**
- `ResourceNotFoundRepoError` — parent or child does not exist.
- `LinkNotFoundError` — the link does not exist.
### `get_children(resource_id)`
Returns all direct children of a resource (via `resource_links`).
### `get_parents(resource_id)`
Returns all direct parents of a resource (via `resource_links`).
## Cycle Detection
Cycle detection uses a **breadth-first search** upward through the
`resource_links` table starting from the proposed parent. If the
proposed child is found among the ancestors, the link is rejected
with a `CycleDetectedError` that includes the cycle path.
### Example
```
A -> B -> C
```
Attempting to link `C -> A` would be rejected because `A` is an
ancestor of `C`. The error message includes the path:
`A -> B -> C -> A`.
## Type Compatibility
Each resource type defines a `child_types` list of allowed child
type names. When linking, the system verifies:
```
child.resource_type_name in parent_type.child_types
```
If the parent type's `child_types` list is empty, **any** child type
is allowed (no restriction).
### Example
```yaml
# git-checkout type
child_types: ["fs-directory", "git"]
```
Only resources of type `fs-directory` or `git` can be linked as
children of a `git-checkout` resource.
## Auto-Discovery
### `auto_discover_children(resource_id)`
Materializes child resources based on the parent's type auto-discovery
configuration.
1. Looks up the resource and its type.
2. Reads the `auto_discovery` configuration from the type.
3. For each discovery rule where `enabled` is `true`:
- Checks the child type exists in the database.
- Checks type compatibility with the parent.
- Creates a new child resource with `auto_discovered = true`.
- Links the child to the parent via `resource_links`.
4. Returns the list of newly created child resources.
### Auto-Discovery Configuration
Auto-discovery is configured per resource type in YAML:
```yaml
auto_discovery:
enabled: true
rules:
- type: fs-directory
pattern: "*/"
- type: fs-file
pattern: "*"
```
Each rule specifies:
- `type` — the child resource type name to create.
- `pattern` — a glob pattern (used by handlers for actual file
discovery; the repository layer creates placeholder entries).
### When Does Auto-Discovery Run?
Auto-discovery is triggered by calling
`auto_discover_children(resource_id)`. This is typically invoked:
- When a resource is first registered.
- When a resource's contents change (e.g., new files appear).
- On demand via CLI commands.
## Database Schema
### `resource_links` Table
| Column | Type | Description |
|--------|------|-------------|
| `parent_id` | `String(26)` | FK to `resources.resource_id` |
| `child_id` | `String(26)` | FK to `resources.resource_id` |
| `created_at` | `String(30)` | ISO-8601 timestamp |
Primary key: `(parent_id, child_id)`
Constraints:
- `parent_id != child_id` (no self-loops)
- Foreign keys cascade on delete
### `resource_edges` Table (Extended)
| Column | Type | Description |
|--------|------|-------------|
| `parent_id` | `String(26)` | FK to `resources.resource_id` |
| `child_id` | `String(26)` | FK to `resources.resource_id` |
| `link_type` | `String(20)` | `contains`, `references`, or physical-to-virtual |
| `auto_discovered` | `Boolean` | Whether the link was auto-discovered |
| `coherence` | `String(20)` | `transparent`, `cached`, `independent`, or `NULL` (only set on physical-to-virtual edges) |
| `created_at` | `String(30)` | ISO-8601 timestamp |
## Operational Semantics
Beyond structural rules, the DAG supports runtime operations.
For full details, see [ADR-036](../adr/ADR-036-resource-dag-operational-semantics.md),
[ADR-037](../adr/ADR-037-tool-reachability-and-access-projection.md), and
[ADR-038](../adr/ADR-038-cross-mechanism-sandbox-coordination.md).
### Sandbox Boundary Algebra
- **`sandbox_boundary(r)`**: Nearest sandboxable ancestor of `r` along `contains` edges.
- **Sandbox domain**: All resources sharing the same sandbox boundary share one sandbox instance.
- Resources below a boundary (e.g., files within a git-checkout) cannot be sandboxed independently.
### Tool Reachability
- **Forward**: Tool bound to `R` can reach `R` and all `contains` descendants.
- **Inverse**: Resource `r` is reachable by tools bound to any of `r`'s containment ancestors.
- **Cross-equivalence**: Tools reaching one physical manifestation of a virtual resource also logically reach all equivalent manifestations.
### Access Projection
Each resource type handler implements `project_access()` to compute how
a tool bound to an ancestor reaches a descendant resource (access path,
protocol, sandbox-crossing awareness, read richness score).
### Read/Write Routing
Virtual resources with multiple physical manifestations route:
- **Reads** through the richest available source (LSP > filesystem > raw).
- **Writes** through the canonical write target (strongest sandbox domain).
### Cross-Mechanism Coordination
- **Coherence**: `transparent` (shared storage), `cached` (needs refresh), `independent` (needs sync).
- **Write-then-sync**: Changes propagate at commit time, not during execution.
- **Conflict detection**: Multiple dirty manifestations of the same virtual resource at commit time trigger resolution (canonical-wins, merge, fail, or last-writer-wins).