Files
cleveragents-core/docs/reference/scoped_backend_view.md
T
hamza.khyari b028c80cab
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 14s
CI / build (pull_request) Successful in 15s
CI / quality (pull_request) Successful in 18s
CI / security (pull_request) Successful in 33s
CI / typecheck (pull_request) Successful in 41s
CI / unit_tests (pull_request) Successful in 2m29s
CI / integration_tests (pull_request) Successful in 3m2s
CI / docker (pull_request) Successful in 39s
CI / coverage (pull_request) Successful in 4m28s
CI / benchmark-regression (pull_request) Successful in 29m5s
feat(acms): add scoped backend view filtering
Add project-resource isolation to the ACMS context tier system via
ScopedBackendView, ResourceScope, alias resolution, DAG expansion,
and enforcement hooks.

Core types (scoped_view.py): ResourceScope, ScopeViolationError,
ScopedBackendView, ScopedBackendSet, create_scoped_backend_set.

Scope resolution (scope_resolution.py): ResourceAliasResolver,
validate_project_scope, validate_resource_scope, resolve_resource_scope
with registry_lookup callback for DAG expansion.

Tier integration (scoped_tiers.py): ScopedTierMixin with get_scoped,
get_scoped_by_resource, validate_fragment_scope, store_with_scope_check,
get_scoped_metrics mixed into ContextTierService.

Backward compat: tiers.py re-exports ScopedBackendView.

Tests: 74 Behave scenarios (235 steps), 8 Robot Framework cases,
7 ASV benchmark suites. Lint and typecheck clean.

ISSUES CLOSED: #193
2026-03-06 00:49:33 +00:00

10 KiB

Scoped Backend View

The Scoped Backend View layer provides project-resource isolation for the Advanced Context Management System (ACMS). It ensures that actors and strategies never see resources outside their plan's scope, enforcing cross-project isolation at both the backend query and fragment visibility levels.

Architecture

Plan ──► ResourceScope ──► ScopedBackendView ──► Backend Protocol ──► Physical Store
              │                    │
              │                    ├── search_text(backend, query)
              │                    ├── search_vector(backend, embedding)
              │                    ├── search_graph(backend, query)
              │                    └── is_visible(fragment) → bool
              │
              ├── resource_ids: frozenset[str]
              ├── project_names: frozenset[str]
              ├── include_paths / exclude_paths
              └── include_resources / exclude_resources

Each plan is assigned a ResourceScope at creation time. The scope is immutable (frozen) — once created, a plan's visible resources never change. ScopedBackendView wraps backend queries to automatically inject the scope parameter, and ScopedBackendSet bundles scoped views for all three backend types (text, vector, graph).

ResourceScope

Resolved set of resources and projects visible to a plan. Created by resolve_resource_scope() during plan creation.

Based on docs/specification.md lines 42510--42525.

Field Type Description
resource_ids frozenset[str] Resource ULIDs in scope (empty = no resources after filtering)
project_names frozenset[str] Namespaced project names in scope (min 1)
temporal_scope str One of "current", "recent", "all" (default: "current")
include_resources tuple[str, ...] Resource allowlist (names/aliases). Empty = all
exclude_resources tuple[str, ...] Resource denylist. Applied after allowlist
include_paths tuple[str, ...] Path glob allowlist. Empty = all
exclude_paths tuple[str, ...] Path glob denylist. Applied after allowlist

Methods

Method Signature Returns
contains_resource (resource_id: str) bool
contains_project (project_name: str) bool
matches_path (path: str) bool — applies include/exclude glob filters

Path matching uses pathlib.PurePath.full_match() which supports recursive ** glob patterns (e.g., src/**/*.py).

ScopedBackendView

Wraps a backend to restrict queries to a resource scope. Operates at two levels: backend-level (injects scope into queries) and fragment-level (filters TieredFragment by project name and resource ID).

Based on docs/specification.md lines 42548--42566.

Field Type Description
allowed_projects frozenset[str] Project names the actor can access (min 1)
resource_ids frozenset[str] Resource ULIDs in scope (empty = project-only filtering)
denied_resource_ids frozenset[str] Resource ULIDs explicitly denied (denylist)

Methods

Method Signature Returns
is_resource_in_scope (resource_id: str) bool
effective_scope () frozenset[str]resource_ids minus denied_resource_ids
is_visible (fragment: object) bool — fragment-level project+resource check
search_text (backend: TextBackend, query: str, *, max_results: int = 20) list[TextResult]
search_vector (backend: VectorBackend, embedding: list[float], *, top_k: int = 20) list[VectorResult]
search_graph (backend: GraphBackend, query: str) GraphResult
from_resource_scope (scope: ResourceScope) (classmethod) ScopedBackendView

Preconditions:

  • allowed_projects must be non-empty.
  • When resource_ids is populated, search methods raise ScopeViolationError if effective_scope() is empty (all resources denied).

Postconditions:

  • Search results are restricted to in-scope resources via the scope parameter.
  • Fragments with empty project_name are always excluded.

ScopedBackendSet

Bundle of scoped backend views for text, vector, and graph backends. Created by create_scoped_backend_set() during initial context assembly.

Field Type Description
view ScopedBackendView The scoped view enforcing isolation
text_backend TextBackend | None Text search backend (None if unavailable)
vector_backend VectorBackend | None Vector search backend (None if unavailable)
graph_backend GraphBackend | None Graph query backend (None if unavailable)

Methods

Method Signature Returns
search_text (query: str, *, max_results: int = 20) list[TextResult] — empty if no backend
search_vector (embedding: list[float], *, top_k: int = 20) list[VectorResult] — empty if no backend
search_graph (query: str) GraphResult — empty if no backend

ScopeViolationError

Exception raised when a context request references resources outside scope.

Attribute Type Description
resource_ids tuple[str, ...] Out-of-scope resource identifiers
scope_project_names tuple[str, ...] Project names that define the scope

ResourceAliasResolver

Stateless helper that resolves resource aliases to canonical resource ULIDs. Used during scope resolution to translate user-facing resource names.

Method Signature Returns
resolve (project: NamespacedProject, alias_or_id: str) str | None
resolve_many (project: NamespacedProject, names: tuple[str, ...]) frozenset[str]
validate_uniqueness (project: NamespacedProject) list[str] — error messages for duplicates

Factory and Validation Functions

create_scoped_backend_set()

def create_scoped_backend_set(
    scope: ResourceScope,
    *,
    text_backend: TextBackend | None = None,
    vector_backend: VectorBackend | None = None,
    graph_backend: GraphBackend | None = None,
) -> ScopedBackendSet: ...

resolve_resource_scope()

def resolve_resource_scope(
    projects: list[NamespacedProject],
    *,
    include_resources: tuple[str, ...] = (),
    exclude_resources: tuple[str, ...] = (),
    include_paths: tuple[str, ...] = (),
    exclude_paths: tuple[str, ...] = (),
    temporal_scope: str = "current",
    registry_lookup: Callable[[str], frozenset[str]] | None = None,
) -> ResourceScope: ...

Collects all resource IDs from linked resources across projects, then applies allowlist/denylist filtering using alias resolution. When registry_lookup is provided, each resource ID is expanded to include its DAG descendants (per spec §42517--42521).

validate_project_scope()

def validate_project_scope(
    requested_projects: frozenset[str],
    available_projects: frozenset[str],
) -> None: ...  # raises ScopeViolationError

validate_resource_scope()

def validate_resource_scope(
    requested_resource_ids: frozenset[str],
    scope: ResourceScope,
) -> None: ...  # raises ScopeViolationError

Usage Examples

Basic Scope Resolution

from cleveragents.domain.models.acms.scope_resolution import resolve_resource_scope

# Resolve scope from a project — includes all linked resources
scope = resolve_resource_scope([project])

# Allowlist: only include the "repo" resource (by alias)
scope = resolve_resource_scope([project], include_resources=("repo",))

# Denylist: exclude "docs" resource, keep everything else
scope = resolve_resource_scope([project], exclude_resources=("docs",))

# Combined: include only Python files, exclude test files
scope = resolve_resource_scope(
    [project],
    include_paths=("src/**/*.py",),
    exclude_paths=("**/test_*", "**/*_test.py"),
)

Scoped Backend Queries

from cleveragents.domain.models.acms.scoped_view import (
    ScopedBackendView,
    create_scoped_backend_set,
)

# Create a scoped view from a resolved scope
view = ScopedBackendView.from_resource_scope(scope)

# Search with automatic scope injection
results = view.search_text(text_backend, "authentication flow")
vectors = view.search_vector(vector_backend, embedding)
graph = view.search_graph(graph_backend, "SELECT ?s WHERE { ?s a uko:Class }")

# Or use ScopedBackendSet for convenience
backend_set = create_scoped_backend_set(
    scope,
    text_backend=text_backend,
    vector_backend=vector_backend,
    graph_backend=graph_backend,
)
results = backend_set.search_text("authentication flow")

Scope Validation Guards

from cleveragents.domain.models.acms.scope_resolution import (
    validate_project_scope,
    validate_resource_scope,
)
from cleveragents.domain.models.acms.scoped_view import ScopeViolationError

try:
    validate_project_scope(
        requested_projects=frozenset({"local/api", "local/frontend"}),
        available_projects=frozenset({"local/api"}),
    )
except ScopeViolationError as e:
    # "Projects not in scope: local/frontend. Available: local/api"
    print(e)

Module Locations

Module Purpose
cleveragents.domain.models.acms.scoped_view Core types: ResourceScope, ScopedBackendView, ScopedBackendSet
cleveragents.domain.models.acms.scope_resolution Resolution: ResourceAliasResolver, resolve_resource_scope, validation guards
cleveragents.domain.models.acms.tiers Re-exports ScopedBackendView for backward compat
cleveragents.domain.models.acms Package-level re-exports of all public symbols
cleveragents.application.services.context_tiers Enforcement hooks (get_scoped, get_scoped_by_resource, etc.)