158 lines
4.5 KiB
Markdown
158 lines
4.5 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
|