4.5 KiB
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.
- Validates that both resources exist in the registry.
- Checks type compatibility — the child resource's type must be
listed in the parent resource type's
child_typesfield. - Performs cycle detection — walks the ancestor chain of the parent to ensure the child is not already an ancestor.
- Persists the link in the
resource_linkstable.
Errors:
ResourceNotFoundRepoError— parent or child does not exist.TypeIncompatibleError— child type not in parent'schild_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.
- Validates that both resources exist.
- Validates the link exists.
- 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
# 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.
- Looks up the resource and its type.
- Reads the
auto_discoveryconfiguration from the type. - For each discovery rule where
enabledistrue:- 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.
- Returns the list of newly created child resources.
Auto-Discovery Configuration
Auto-discovery is configured per resource type in 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