test(features): fix BDD scenarios for progressive disclosure and resource tests

Add 'When I load the agent skills folder' step to scenarios that use
load-based testing without explicit loading. The Given step creates
the temp folder but sets loaded_skill=None; the loader must be created
explicitly before calling discover/activate/deactivate/list_resources.

Also restored scripts/check-adr-compliance.py which was removed by
git filter-repo --invert-paths during security token purge.

ISSUES CLOSED: #9454
This commit is contained in:
2026-05-07 18:05:03 +00:00
parent 18d00c04c4
commit d5bf3e66cb
2 changed files with 145 additions and 0 deletions
+5
View File
@@ -295,6 +295,7 @@ Feature: Agent Skills Loader
Step 1: do this.
Step 2: do that.
"""
When I load the agent skills folder
When I call discover on the agent skill
Then the discover result should have name "local/disclosure-skill"
And the discover result should have description "Progressive disclosure test skill."
@@ -311,6 +312,7 @@ Feature: Agent Skills Loader
Step 1: do this.
Step 2: do that.
"""
When I load the agent skills folder
When I call discover on the agent skill
And I call activate on the agent skill
Then the activate result body should contain "Step 1: do this."
@@ -327,6 +329,7 @@ Feature: Agent Skills Loader
Use the scripts.
"""
And the folder contains a script file "helper.py" with content "# helper"
When I load the agent skills folder
When I call discover on the agent skill
And I list available resources
Then the resources list should include "helper.py"
@@ -341,6 +344,7 @@ Feature: Agent Skills Loader
---
Step 1: do this.
"""
When I load the agent skills folder
When I call discover on the agent skill
And I call activate on the agent skill
And I call deactivate on the agent skill
@@ -690,6 +694,7 @@ Feature: Agent Skills Loader
Instructions.
"""
And the folder contains an asset file "config.json" with content "{}"
When I load the agent skills folder
When I call discover on the agent skill
Then the discover result should have 1 resource slot(s)
And the discover result resource slot "assets" should have access "read_only"
+140
View File
@@ -0,0 +1,140 @@
#!/usr/bin/env python3
"""ADR compliance checker for CleverAgents.
Verifies that code follows the architectural decisions recorded in ADRs.
ADR-002: Asyncio Concurrency Model
- Async functions should use 'async def', not threading
- No direct thread usage in application layer
ADR-003: Dependency Injection Framework
- Services should receive dependencies via __init__
- No direct instantiation of infrastructure in application layer
ADR-007: Repository Pattern for Persistence
- Database access only through repository classes
- No direct SQLAlchemy session usage in services
Usage:
python scripts/check-adr-compliance.py
"""
import ast
import sys
from pathlib import Path
def check_adr002_async_model(src_dir: Path) -> list[str]:
"""Check ADR-002: Asyncio Concurrency Model.
Verifies no direct threading usage in application code.
"""
violations: list[str] = []
app_dir = src_dir / "application"
if not app_dir.exists():
return violations
for py_file in app_dir.rglob("*.py"):
content = py_file.read_text()
if "import threading" in content or "from threading import" in content:
violations.append(
f"ADR-002 violation: {py_file.relative_to(src_dir)} "
"imports threading directly. Use asyncio instead."
)
return violations
def check_adr003_dependency_injection(src_dir: Path) -> list[str]:
"""Check ADR-003: Dependency Injection Framework.
Verifies services use constructor injection.
"""
violations: list[str] = []
services_dir = src_dir / "application" / "services"
if not services_dir.exists():
return violations
for py_file in services_dir.rglob("*.py"):
if py_file.name.startswith("__"):
continue
try:
tree = ast.parse(py_file.read_text())
except SyntaxError:
continue
for node in ast.walk(tree):
if isinstance(node, ast.ClassDef):
# Check if class has __init__ with parameters (DI)
has_init = False
for item in node.body:
if isinstance(item, ast.FunctionDef) and item.name == "__init__":
has_init = True
# Check for at least one parameter beyond self
if len(item.args.args) < 2:
violations.append(
f"ADR-003 note: {py_file.relative_to(src_dir)}:"
f"{node.lineno} class {node.name} has __init__ "
"with no injected dependencies"
)
if not has_init and node.body:
# Services without __init__ may be okay if they're utility classes
pass
return violations
def check_adr007_repository_pattern(src_dir: Path) -> list[str]:
"""Check ADR-007: Repository Pattern for Persistence.
Verifies no direct SQLAlchemy session usage in service layer.
"""
violations: list[str] = []
services_dir = src_dir / "application" / "services"
if not services_dir.exists():
return violations
for py_file in services_dir.rglob("*.py"):
content = py_file.read_text()
# Check for direct session imports
if "from sqlalchemy" in content:
violations.append(
f"ADR-007 violation: {py_file.relative_to(src_dir)} "
"imports SQLAlchemy directly. Use repository pattern instead."
)
if "session.query" in content or "session.execute" in content:
violations.append(
f"ADR-007 violation: {py_file.relative_to(src_dir)} "
"uses session directly. Access data through repositories."
)
return violations
def main() -> int:
"""Run all ADR compliance checks."""
src_dir = Path("src/cleveragents")
if not src_dir.exists():
print("Error: src/cleveragents directory not found")
return 1
all_violations: list[str] = []
print("Checking ADR-002: Asyncio Concurrency Model...")
all_violations.extend(check_adr002_async_model(src_dir))
print("Checking ADR-003: Dependency Injection Framework...")
all_violations.extend(check_adr003_dependency_injection(src_dir))
print("Checking ADR-007: Repository Pattern for Persistence...")
all_violations.extend(check_adr007_repository_pattern(src_dir))
if all_violations:
print(f"\nFound {len(all_violations)} ADR compliance issue(s):")
for violation in all_violations:
print(f" - {violation}")
return 1
print("\nAll ADR compliance checks passed.")
return 0
if __name__ == "__main__":
sys.exit(main())