Files
cleveragents-core/docs/adr/ADR-036-resource-dag-operational-semantics.md
freemo c2db74ba81
CI / lint (push) Successful in 15s
CI / build (push) Successful in 16s
CI / quality (push) Successful in 20s
CI / security (push) Successful in 35s
CI / typecheck (push) Successful in 42s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Successful in 2m38s
CI / integration_tests (push) Successful in 3m11s
CI / docker (push) Successful in 39s
CI / coverage (push) Successful in 4m58s
CI / benchmark-publish (push) Successful in 17m32s
Docs: Restyled ADR pages
2026-03-10 12:38:35 -04:00

18 KiB
Raw Permalink Blame History

adr_number, title, status_history, tier, authors, superseded_by, related_adrs, acceptance
adr_number title status_history tier authors superseded_by related_adrs acceptance
36 Resource DAG Operational Semantics
2026-02-21
Proposed
Jeffrey Phillips Freeman
2026-02-21
Accepted
Jeffrey Phillips Freeman
3
Jeffrey Phillips Freeman
null
number title relationship
8 Resource System Defines the DAG's structural rules; this ADR adds operational semantics on top
number title relationship
11 Tool System Tools bind to resources; this ADR defines how reachability extends through the DAG
number title relationship
15 Sandbox and Checkpoint Defines sandbox strategies; this ADR formalizes sandbox boundaries as a DAG property
number title relationship
37 Tool Reachability and Access Projection Extends purposes 13 with the full reachability and routing model
number title relationship
38 Cross-Mechanism Sandbox Coordination Extends purpose 5 with coherence, write-then-sync, and lazy virtual materialization
number title relationship
39 Container and Execution Environment Resource Types Defines new resource types that create additional sandbox domains and containment hierarchies
number title relationship
40 Language Server Protocol Resource Types Defines LSP resource types that create cross-mechanism equivalence with filesystem resources
votes_for votes_against abstentions
voter comment
Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com> The ten purposes and sandbox boundary algebra provide the missing operational theory for the resource DAG

Context

ADR-008 defines the resource DAG's structural rules (acyclicity, type compatibility, unique links, existence constraints) and its role in resource discovery and sandbox ordering. The specification (lines 2266022911) elaborates the 24 built-in types, virtual/physical equivalence linking, and divergence detection. However, neither document articulates a unified theory of what the DAG is for at runtime — what operational questions it answers, what algorithms it supports, and how its structure drives sandbox creation, tool routing, access control propagation, and change coordination across heterogeneous mechanisms.

This ADR defines the operational semantics of the resource DAG: the purpose-driven theory that connects DAG structure to runtime behavior.

Decision Drivers

  • The DAG's structural rules (ADR-008) exist but no unified theory connects DAG structure to runtime behavior across subsystems (sandbox, tool routing, access control, auto-discovery)
  • Sandbox boundaries must be explicit and queryable — a file cannot be sandboxed in isolation; its containing repository or directory must define the boundary
  • Tool reachability, read/write routing, and access control propagation all traverse the DAG differently and need formalized semantics
  • Lifecycle operations (creation, commit, rollback, cleanup) require correct topological orderings with direction varying per operation type
  • Change propagation through virtual resource equivalence classes must terminate deterministically and not cascade unboundedly

Decision

The resource DAG serves as the topology of the system's operational world. Its structure supports ten distinct operational purposes, organized into three categories: reachability (how tools connect to resources), isolation (how sandboxing and checkpointing map to DAG positions), and coordination (how changes propagate and how equivalent resources stay synchronized). This ADR defines the sandbox boundary algebra and dependency ordering protocols that emerge from these purposes.

Design

The Ten Purposes of the Resource DAG

The DAG answers five fundamental questions, each served by specific operational purposes:

Question DAG Feature Purposes
What is this? Virtual resource identity (hub node) Equivalence classes
Where does it live? Physical manifestations and containment hierarchy Reachability, auto-discovery
How do I reach it? Tool binding → containment → access projection Tool reachability, access projection, read/write routing
How do I safely change it? Sandbox boundaries and coordination policies Sandbox boundary algebra, cross-mechanism coordination
What else is affected? Descendant invalidation, ancestor propagation, sibling sync Change propagation, access control propagation, dependency ordering

Purpose 1: Tool Reachability

Knowing which tools can reach a particular resource for reading or writing it. A tool declares resource slots specifying what types it binds to. Through the DAG's containment hierarchy, a tool bound to a git-checkout can transitively reach every fs-directory and fs-file descendant within that checkout. The DAG supports both forward reachability (tool → bound resource → descendants) and inverse reachability (resource → ancestors → tools with compatible bindings). See ADR-037 for the full reachability model.

Purpose 2: Equivalence and Alternative Paths

Knowing which physical resources are equivalent so the system can find alternative tools and paths for reaching the same logical resource. Virtual resources are identity hubs that link physical resources sharing the same content or structural identity. When one path to a resource is unavailable (e.g., an MCP server is down), the system uses the DAG to find alternative physical manifestations of the same virtual resource, each potentially reachable through different tools. See the specification's Physical vs Virtual Resources section for the 9 equivalence types.

Purpose 3: Read/Write Routing

Routing reads and writes through different physical manifestations of the same virtual resource. An LSP server provides semantically rich reads (type info, references, diagnostics) while a git-checkout provides sandbox-tracked writes. The DAG, combined with equivalence classes, enables the system to route reads through the richest available path and writes through the canonical sandbox-tracked path. See ADR-037 for read/write routing.

Purpose 4: Sandbox Boundary Algebra

Understanding at what level in the DAG sandboxing and checkpointing take place. Not every resource in the DAG is independently sandboxable. A file cannot be sandboxed in isolation — its containing directory (copy_on_write) or git repository (git_worktree) must be sandboxed, and the file inherits that sandbox. The DAG defines sandbox boundaries: specific nodes at which sandboxing is physically implementable. All descendants of a sandbox boundary share that boundary's sandbox instance. See the Sandbox Boundary Algebra section below.

Purpose 5: Cross-Mechanism Sandbox Coordination

Coordinating sandbox and checkpoint operations across different physical resources that share a virtual identity. When the same logical file exists in a git-checkout sandbox and a container bind-mount, changes through one path must be accounted for in the other. The DAG's equivalence structure, combined with coherence properties on the physical-to-virtual edges, provides the information needed to synchronize or coordinate across different sandbox mechanisms. See ADR-038 for the coordination protocol.

Purpose 6: Dependency Ordering

Determining the correct order for lifecycle operations. The DAG provides topological orderings for sandbox lifecycle operations where the traversal direction differs per operation:

Operation Traversal Rationale
Sandbox creation Top-down (parent before child) A parent resource must be sandboxed before its children can be accessed within the sandbox
Sandbox commit/apply Bottom-up (child before parent) Child changes should be finalized before the parent incorporates them (e.g., file changes committed before directory snapshot)
Sandbox rollback Top-down (parent before child) Rolling back a parent implicitly rolls back children within its sandbox domain
Sandbox cleanup Bottom-up (child before parent) Clean up child sandboxes before removing the parent, to avoid dangling references
Auto-discovery Top-down (parent before child) Discover containers before their contents, directories before their files
Auto-discovery refresh Bottom-up then top-down Re-evaluate changed children, then cascade to parent identity re-evaluation

Purpose 7: Change Propagation and Invalidation

Determining what else is affected when a resource changes. When a tool writes to a file:

  • Descendant impact: Rarely applicable for files, but a directory change may affect files (e.g., permission changes).
  • Ancestor impact: The file's parent directory has changed contents. The git-checkout has uncommitted changes. These ancestors need status updates.
  • Equivalence sibling impact: If the file has a virtual parent, other physical siblings may need cache invalidation (LSP server), content refresh, or conflict detection.
  • Cascading identity re-evaluation: A file content change may break its link to its file virtual parent. A directory containing that file may break its directory virtual link. The DAG provides the traversal path for this cascade (see specification: Divergence Detection / Cascading Divergence).

Purpose 8: Access Control Propagation

Propagating read-only and capability constraints through the containment hierarchy. A resource marked read_only at the project level propagates that constraint to all descendants. A git-checkout marked read-only means all files within it are read-only, regardless of their individual capabilities. The query "is this resource writable, considering all its ancestors' constraints?" requires walking up the containment edges.

More precisely, the effective writability of a resource is:

effective_writable(r) = r.capabilities.writable
                        AND all(p.capabilities.writable for p in containment_ancestors(r))
                        AND NOT project_read_only(r)

Purpose 9: Auto-Discovery Scope

Determining the scope and cascade of resource auto-discovery. The DAG defines which resources are parents of which, and auto-discovery rules are configured per resource type. When a git-checkout is registered, auto-discovery cascades down: discover the git child and fs-directory child, then the git handler discovers branches/tags/commits/trees, and the fs-directory handler discovers subdirectories and files. The DAG is the traversal structure for both initial discovery and incremental refresh.

Purpose 10: Tool Capability Inference

Inferring tool capabilities from DAG position. A tool bound at the git-checkout level inherits the ability to transitively write to any file within the checkout. But a tool bound only to a specific fs-file cannot create directories or modify git metadata. The DAG position constrains and infers tool capabilities beyond what the tool explicitly declares — the effective write scope of a tool is bounded by the sandbox domain of its bound resource.

Sandbox Boundary Algebra

Definitions

Sandbox boundary: A node in the DAG at which sandboxing is physically implementable. Formally: resource b is a sandbox boundary if b.capabilities.sandboxable == true and b.sandbox_strategy != none.

Sandbox domain: The set of all descendants of a sandbox boundary (along contains edges) that do not have their own intervening sandbox boundary. If a descendant d of boundary b is itself a sandbox boundary, then d and its descendants form a separate sandbox domain, not part of b's domain.

sandbox_boundary(r): A function that returns the nearest ancestor sandbox boundary of resource r (inclusive — if r is itself a boundary, returns r). If no sandbox boundary exists in r's ancestor chain, r is unsandboxable and sandbox_boundary(r) = None.

sandbox_domain(b): A function that returns the set {r : sandbox_boundary(r) == b} — all resources whose nearest sandbox boundary is b.

Properties

  1. Partition: Every sandboxable resource belongs to exactly one sandbox domain. The sandbox domains partition the set of all sandboxable resources into disjoint groups.

  2. Shared sandbox instance: All resources within the same sandbox domain share a single sandbox instance during plan execution. The SandboxManager keys sandboxes by (plan_id, sandbox_boundary_id), not by individual resource ID.

  3. Inheritance: Child resources inherit their parent's sandbox unless they define their own boundary. This is already described in the specification ("The sandbox strategy is inherited by child resources from their parent unless the child type defines its own"), but the sandbox boundary algebra makes the inheritance structure queryable and explicit.

  4. Cross-equivalence boundaries: A virtual resource's physical manifestations may reside in different sandbox domains. For example, a file's fs-file manifestation is in the git-checkout's sandbox domain (git_worktree), while its container-mount-file manifestation is in the container-instance's sandbox domain (snapshot), and its lsp-document manifestation has no sandbox at all (none). The set of sandbox boundaries for a virtual resource is: {sandbox_boundary(p) for p in physical_manifestations(v)}.

Checkpoint Boundaries

Checkpoint boundary: A node in the DAG at which state capture (checkpointing) is physically implementable. Checkpoint boundaries may coincide with sandbox boundaries or be finer-grained.

For most resource types, the checkpoint boundary is the same as the sandbox boundary (e.g., a git commit captures the entire worktree). For databases, checkpoint boundaries can be finer — a savepoint within a transaction captures a subset of changes within the transaction sandbox.

The tool's declared checkpoint_scope must be compatible with the checkpoint boundary's granularity:

Tool checkpoint_scope Compatible checkpoint boundaries
file git-checkout (via git commit), fs-directory (via filesystem snapshot)
transaction Database resource (via savepoint)
commit git-checkout (via git commit)
snapshot Any resource with snapshot capability (containers, filesystems, databases)

DAG Edge Types

The resource DAG uses three types of directed edges:

Edge Type Semantics Traversal in queries
contains Parent physically contains or structurally owns the child. The primary hierarchy. Sandbox domain computation, reachability, auto-discovery, access projection, dependency ordering
references Soft dependency — the parent depends on or uses the child, but does not contain it. Ordering constraints, capability discovery (e.g., lsp-server references executable)
Physical-to-virtual (child of virtual parent) Physical resource is a manifestation of the virtual identity. Equivalence class queries, read/write routing, cross-mechanism coordination, divergence detection

Note: The physical-to-virtual relationship uses the standard parent-child edge with the virtual resource as parent. The edge direction means "this virtual identity has these physical manifestations as children." This is consistent with the existing DAG structure — contains edges from virtual parents to physical children carry the semantics "this identity is realized by these physical resources."

Constraints

  • Sandbox boundary computation must be O(depth) where depth is the longest path from the resource to the DAG root. In practice, DAGs are shallow (typically 36 levels), so this is effectively O(1).
  • Change propagation must not trigger unbounded cascades. Divergence re-evaluation stops when a virtual resource's identity is stable (no further changes detected in its physical children).
  • Access control propagation is read-only and stateless — effective_writable(r) is computed fresh each time, not cached, to avoid stale permission data.
  • Dependency ordering for lifecycle operations must produce a valid topological sort. If the DAG has multiple connected components, each component is ordered independently.

Consequences

Positive

  • A unified theory connecting DAG structure to runtime behavior, replacing implicit knowledge scattered across multiple subsystems.
  • Sandbox boundary algebra makes sandbox sharing explicit and queryable, instead of implicit in the SandboxManager implementation.
  • Dependency ordering protocols prevent incorrect lifecycle sequencing (e.g., cleaning up a parent before its children).
  • The ten purposes provide a clear framework for evaluating whether a proposed DAG change (new edge type, new resource type) is well-motivated.

Negative

  • The operational semantics add conceptual complexity beyond the structural rules in ADR-008. Implementers must understand both the structural and operational layers.
  • Sandbox boundary computation introduces a new query pattern that the current repository layer does not directly support (ancestor walks along contains edges).

Risks

  • The sandbox boundary algebra assumes DAGs are shallow. Deeply nested resource hierarchies (unlikely in practice but possible with custom types) could make boundary computation expensive.
  • Change propagation cascades through virtual resource re-evaluation could become expensive if a single virtual resource has many physical manifestations across many repositories.

Alternatives Considered

Implicit operational semantics (status quo) — Each subsystem (SandboxManager, BindingResolutionService, auto-discovery) implements its own understanding of the DAG without a shared theory. This works for simple cases but leads to inconsistent behavior as the system grows (e.g., SandboxManager and tool routing may disagree on what "reachable" means).

Flat resource model without DAG — Resources exist independently without hierarchy. Simpler but cannot support transitive reachability, sandbox domain sharing, or automatic change propagation.

Compliance

  • Sandbox boundary tests: Unit tests verify sandbox_boundary(r) and sandbox_domain(b) for all built-in resource type hierarchies (git, filesystem, container, LSP).
  • Dependency ordering tests: Tests verify correct topological ordering for creation, commit, rollback, and cleanup operations.
  • Change propagation tests: Tests verify that changes to a file trigger ancestor status updates and virtual resource re-evaluation, with cascade termination.
  • Access control propagation tests: Tests verify that effective_writable(r) correctly aggregates constraints from all ancestors and project-level read-only flags.