- 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
6.9 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
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-037, and ADR-038.
Sandbox Boundary Algebra
sandbox_boundary(r): Nearest sandboxable ancestor ofralongcontainsedges.- 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
Rcan reachRand allcontainsdescendants. - Inverse: Resource
ris reachable by tools bound to any ofr'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).