Files
cleveragents-core/robot/helper_scoped_view.py
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

274 lines
9.6 KiB
Python

"""Robot Framework helper for scoped backend view smoke tests.
Provides a CLI-style interface for Robot to invoke scoped view creation,
resource scope resolution, backend proxying, and scope validation.
Exit code 0 = success, 1 = failure.
Usage:
python robot/helper_scoped_view.py <command>
"""
from __future__ import annotations
import sys
from pathlib import Path
# Ensure the src directory is on the import path.
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
from cleveragents.domain.models.acms.scope_resolution import ( # noqa: E402
ResourceAliasResolver,
resolve_resource_scope,
validate_project_scope,
validate_resource_scope,
)
from cleveragents.domain.models.acms.scoped_view import ( # noqa: E402
ResourceScope,
ScopedBackendView,
ScopeViolationError,
create_scoped_backend_set,
)
from cleveragents.domain.models.acms.stubs import ( # noqa: E402
InMemoryGraphBackend,
InMemoryTextBackend,
InMemoryVectorBackend,
)
from cleveragents.domain.models.acms.tiers import ( # noqa: E402
ContextTier,
TieredFragment,
)
from cleveragents.domain.models.core.project import ( # noqa: E402
LinkedResource,
NamespacedProject,
)
def main() -> int:
"""Entry point called by Robot Framework ``Run Process``."""
if len(sys.argv) < 2:
print("Usage: helper_scoped_view.py <command>")
return 1
command: str = sys.argv[1]
if command == "resource-scope":
try:
scope = ResourceScope(
resource_ids=frozenset({"R1", "R2"}),
project_names=frozenset({"local/api"}),
)
assert scope.contains_resource("R1")
assert scope.contains_resource("R2")
assert not scope.contains_resource("R99")
assert scope.contains_project("local/api")
assert not scope.contains_project("local/frontend")
assert scope.temporal_scope == "current"
print("scoped-view-resource-scope-ok")
return 0
except Exception as exc:
print(f"scoped-view-resource-scope-fail: {exc}")
return 1
if command == "path-filtering":
try:
scope = ResourceScope(
resource_ids=frozenset({"R1"}),
project_names=frozenset({"p1"}),
include_paths=("src/**/*.py",),
exclude_paths=("**/test_*",),
)
assert scope.matches_path("src/main.py")
assert not scope.matches_path("src/test_main.py")
assert not scope.matches_path("docs/readme.md")
print("scoped-view-path-filtering-ok")
return 0
except Exception as exc:
print(f"scoped-view-path-filtering-fail: {exc}")
return 1
if command == "fragment-filtering":
try:
view = ScopedBackendView(
allowed_projects=frozenset({"proj-a"}),
resource_ids=frozenset({"R1"}),
)
frag_ok = TieredFragment(
fragment_id="f1",
content="c",
tier=ContextTier("hot"),
project_name="proj-a",
resource_id="R1",
)
frag_bad_proj = TieredFragment(
fragment_id="f2",
content="c",
tier=ContextTier("hot"),
project_name="proj-b",
resource_id="R1",
)
frag_bad_res = TieredFragment(
fragment_id="f3",
content="c",
tier=ContextTier("hot"),
project_name="proj-a",
resource_id="R99",
)
assert view.is_visible(frag_ok)
assert not view.is_visible(frag_bad_proj)
assert not view.is_visible(frag_bad_res)
print("scoped-view-fragment-filtering-ok")
return 0
except Exception as exc:
print(f"scoped-view-fragment-filtering-fail: {exc}")
return 1
if command == "backend-proxying":
try:
scope = ResourceScope(
resource_ids=frozenset({"R1"}),
project_names=frozenset({"local/api"}),
)
view = ScopedBackendView.from_resource_scope(scope)
text_results = view.search_text(InMemoryTextBackend(), "test")
assert text_results == []
vec_results = view.search_vector(InMemoryVectorBackend(), [0.1, 0.2, 0.3])
assert vec_results == []
graph_result = view.search_graph(
InMemoryGraphBackend(), "SELECT ?s WHERE { ?s a uko:X }"
)
assert graph_result.triples == []
print("scoped-view-backend-proxying-ok")
return 0
except Exception as exc:
print(f"scoped-view-backend-proxying-fail: {exc}")
return 1
if command == "backend-set":
try:
scope = ResourceScope(
resource_ids=frozenset({"R1"}),
project_names=frozenset({"local/api"}),
)
bset = create_scoped_backend_set(
scope,
text_backend=InMemoryTextBackend(),
vector_backend=InMemoryVectorBackend(),
graph_backend=InMemoryGraphBackend(),
)
assert bset.search_text("q") == []
assert bset.search_vector([0.1]) == []
assert bset.search_graph("SELECT ?s WHERE { ?s a uko:X }").triples == []
# None backend returns empty
bset_no_text = create_scoped_backend_set(scope)
assert bset_no_text.search_text("q") == []
print("scoped-view-backend-set-ok")
return 0
except Exception as exc:
print(f"scoped-view-backend-set-fail: {exc}")
return 1
if command == "scope-validation":
try:
# validate_project_scope: valid
validate_project_scope(
frozenset({"proj-a"}), frozenset({"proj-a", "proj-b"})
)
# validate_project_scope: invalid
raised = False
try:
validate_project_scope(
frozenset({"proj-c"}), frozenset({"proj-a", "proj-b"})
)
except ScopeViolationError:
raised = True
assert raised, "Expected ScopeViolationError for out-of-scope project"
# validate_resource_scope: valid
scope = ResourceScope(
resource_ids=frozenset({"R1", "R2"}),
project_names=frozenset({"p1"}),
)
validate_resource_scope(frozenset({"R1"}), scope)
# validate_resource_scope: invalid
raised = False
try:
validate_resource_scope(frozenset({"R99"}), scope)
except ScopeViolationError:
raised = True
assert raised, "Expected ScopeViolationError for out-of-scope resource"
print("scoped-view-scope-validation-ok")
return 0
except Exception as exc:
print(f"scoped-view-scope-validation-fail: {exc}")
return 1
if command == "alias-resolver":
try:
ulid1 = "01HQ8ZDRX50000000000000001"
ulid2 = "01HQ8ZDRX50000000000000002"
proj = NamespacedProject(
name="myproj",
namespace="local",
linked_resources=[
LinkedResource(resource_id=ulid1, alias="repo"),
LinkedResource(resource_id=ulid2, alias="docs"),
],
)
resolver = ResourceAliasResolver()
# Resolve by alias
assert resolver.resolve(proj, "repo") == ulid1
# Resolve by ID
assert resolver.resolve(proj, ulid1) == ulid1
# Unknown returns None
assert resolver.resolve(proj, "nonexistent") is None
# resolve_many
resolved = resolver.resolve_many(proj, ("repo", "docs"))
assert ulid1 in resolved
assert ulid2 in resolved
# validate_uniqueness: no dupes
assert resolver.validate_uniqueness(proj) == []
print("scoped-view-alias-resolver-ok")
return 0
except Exception as exc:
print(f"scoped-view-alias-resolver-fail: {exc}")
return 1
if command == "resolve-scope":
try:
ulid1 = "01HQ8ZDRX50000000000000001"
ulid2 = "01HQ8ZDRX50000000000000002"
proj = NamespacedProject(
name="api",
namespace="local",
linked_resources=[
LinkedResource(resource_id=ulid1, alias="repo"),
LinkedResource(resource_id=ulid2, alias="docs"),
],
)
# Full scope
scope = resolve_resource_scope([proj])
assert len(scope.resource_ids) == 2
# Include filter
scope_inc = resolve_resource_scope([proj], include_resources=("repo",))
assert len(scope_inc.resource_ids) == 1
assert ulid1 in scope_inc.resource_ids
# Exclude filter
scope_exc = resolve_resource_scope([proj], exclude_resources=("docs",))
assert len(scope_exc.resource_ids) == 1
assert ulid1 in scope_exc.resource_ids
print("scoped-view-resolve-scope-ok")
return 0
except Exception as exc:
print(f"scoped-view-resolve-scope-fail: {exc}")
return 1
print(f"Unknown command: {command}")
return 1
if __name__ == "__main__":
sys.exit(main())