Files
cleveragents-core/docs/reference/semantic_validation.md
CoreRasurae a2be3e67b0
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 13s
CI / build (pull_request) Successful in 17s
CI / quality (pull_request) Successful in 18s
CI / security (pull_request) Successful in 30s
CI / typecheck (pull_request) Successful in 59s
CI / integration_tests (pull_request) Successful in 4m40s
CI / unit_tests (pull_request) Successful in 11m32s
CI / docker (pull_request) Successful in 1m1s
CI / benchmark-regression (pull_request) Successful in 28m54s
CI / coverage (pull_request) Successful in 44m11s
CI / lint (push) Successful in 12s
CI / build (push) Successful in 14s
CI / quality (push) Successful in 16s
CI / typecheck (push) Successful in 31s
CI / benchmark-regression (push) Has been skipped
CI / security (push) Successful in 42s
CI / integration_tests (push) Successful in 2m53s
CI / unit_tests (push) Successful in 11m20s
CI / docker (push) Successful in 9s
CI / benchmark-publish (push) Successful in 12m18s
CI / coverage (push) Successful in 43m33s
feat(validation): add semantic validation service
Added SemanticValidationService with built-in Python checks for syntax
errors, missing imports, broken references, dependency cycles, API misuse,
and missing symbols. Implemented SemanticRuleRegistry for extensible
validator registration. Integrated with ValidationPipeline as informational
by default. Added per-project/per-plan config keys, severity mapping
(info/warn/error), output schema normalization, and file-hash-based caching.

Includes Behave BDD scenarios, Robot integration tests, ASV benchmarks,
and reference documentation.

ISSUES CLOSED: #207
2026-02-28 17:46:54 +00:00

266 lines
8.6 KiB
Markdown

# Semantic Validation
The **Semantic Validation Service** provides lightweight, AST-based semantic
analysis for Python projects during the strategize/execute phases. It detects
syntax errors, missing imports, broken references, duplicate imports, API
misuse, and missing symbols before code is applied. Non-Python files are
automatically skipped.
## Overview
Semantic checks are exposed as Validation tools attachable per resource via the
Tool Registry. Results integrate into the `ValidationPipeline` as informational
by default, meaning failures are reported but do not block the plan lifecycle
unless configured otherwise.
```
Source Code ──► SemanticValidationService.check_file() ──► [SemanticCheckResult, ...]
├── SyntaxCheckRule
├── MissingImportRule
├── BrokenReferenceRule
├── DuplicateImportRule
├── APIMisuseRule
└── MissingSymbolRule
```
> **Note:** `check_file()` returns `[]` immediately for non-Python files
> (files without `.py` or `.pyi` extension).
## Built-in Rules
| Rule | Description | Default Severity |
|--------------------|-------------------------------------------------------|-----------------|
| `syntax_error` | Parses source via `ast.parse` | `error` |
| `missing_import` | Flags imports not in `sys.stdlib_module_names` | `warn` |
| `broken_reference` | Detects names not defined in any scope (scope-aware) | `warn` |
| `duplicate_import` | Finds duplicate relative imports | `error` |
| `api_misuse` | AST-based detection of dangerous calls (see below) | `warn` |
| `missing_symbol` | Finds undefined names in functions and class methods | `info` |
> **Rename:** The rule was previously registered as `dependency_cycle` and is
> now registered as `duplicate_import` to better reflect its purpose.
> The class alias `DependencyCycleRule` remains available for backward
> compatibility.
### APIMisuseRule Dangerous Calls
The `api_misuse` rule uses AST analysis (not regex) to detect calls to:
- `eval()`, `exec()`, `compile()`
- `__import__()`
- `os.system()`, `os.popen()`
- `subprocess.call()`, `subprocess.run()`, `subprocess.Popen()`
- `pickle.load()`, `pickle.loads()`
- `marshal.loads()`
Because the rule inspects `ast.Call` nodes, it does **not** false-positive on
string literals that happen to contain these function names.
## Severity Mapping
Each rule produces findings with a severity level. The severity determines
how the finding integrates with the `ValidationPipeline`:
| Severity | Pipeline Mode | Behaviour |
|----------|------------------|------------------------------------|
| `error` | `required` | Failure **blocks** the operation |
| `warn` | `informational` | Failure is reported, does not block |
| `info` | `informational` | Informational only |
The mapping is configurable via `validation.semantic.severity_mapping`.
## Required vs Informational Attachment Modes
Semantic validations attach to the `ValidationPipeline` in one of two modes:
- **Required** (`error` severity): If a required semantic validation fails,
`ValidationSummary.all_required_passed` becomes `False`, blocking the plan
from proceeding to the Apply phase.
- **Informational** (`warn`/`info` severity): If an informational validation
fails, the finding is logged at INFO level for review but does **not**
affect the `all_required_passed` property.
This allows teams to start with informational semantic checks and
progressively promote them to required as confidence grows.
## Configuration
Three configuration keys control semantic validation:
| Key | Type | Default | Description |
|------------------------------------------|--------|---------|----------------------------------------|
| `validation.semantic.enabled` | `bool` | `true` | Global on/off for semantic validation |
| `validation.semantic.python.enabled` | `bool` | `true` | Python-specific checks on/off |
| `validation.semantic.severity_mapping` | `dict` | (below) | Per-rule severity overrides |
Default severity mapping:
```yaml
validation.semantic.severity_mapping:
syntax_error: error
missing_import: warn
broken_reference: warn
duplicate_import: error
api_misuse: warn
missing_symbol: info
```
### Per-project Configuration
Set `validation.semantic.enabled: false` in the project configuration to
disable semantic validation for a specific project:
```yaml
# .cleveragents/config.yaml
validation:
semantic:
enabled: false
```
### Per-plan Configuration
Override in plan metadata to enable/disable per plan execution:
```yaml
validation:
semantic:
enabled: true
python:
enabled: true
```
## Rule Registry
The `SemanticRuleRegistry` manages pluggable validation rules:
```python
from cleveragents.application.services import (
SemanticRuleRegistry,
create_default_registry,
)
# Create with built-in rules
registry = create_default_registry()
# List registered rules
print(registry.list_rules())
# ['api_misuse', 'broken_reference', 'duplicate_import',
# 'missing_import', 'missing_symbol', 'syntax_error']
#
# Note: 'duplicate_import' is provided by DuplicateImportRule
# Look up a rule
rule = registry.get("syntax_error")
# Register a custom rule
registry.register(my_custom_rule)
```
### Custom Rule Protocol
Any object implementing the `SemanticValidationRule` protocol can be
registered:
```python
class MyCustomRule:
@property
def name(self) -> str:
return "my_custom_check"
def check(self, source: str, filename: str) -> SemanticCheckResult:
# ... run analysis ...
return SemanticCheckResult(
passed=True,
message="All good",
)
```
## Caching
The `SemanticValidationCache` avoids re-running checks on unchanged files
by caching results keyed by `(rule_name, file_sha256)`. The cache uses an
**LRU (Least Recently Used)** eviction policy with a configurable
`max_size` (default: 4096 entries). All operations are **thread-safe**.
```python
from cleveragents.application.services import SemanticValidationCache
cache = SemanticValidationCache(max_size=256)
file_hash = cache.compute_hash(source_code)
# Check cache before running expensive rule
cached = cache.get("syntax_error", file_hash)
if cached is None:
result = rule.check(source_code, filename)
cache.put("syntax_error", file_hash, result)
```
The service automatically uses caching when `check_file()` is called.
Calling `check_file()` with the same source content will return cached
results without re-executing rules.
When the cache reaches `max_size`, the least-recently-used entry is
automatically evicted to make room for the new one.
## Output Schema Normalisation
All semantic check results follow a consistent normalised schema:
```json
{
"passed": true,
"message": "No syntax errors in test.py",
"data": null
}
```
The `data` field contains structured finding details when present:
```json
{
"passed": false,
"message": "Syntax error at test.py:3: invalid syntax",
"data": {
"filename": "test.py",
"line": 3,
"detail": "invalid syntax"
}
}
```
The `normalise_output()` method on `SemanticValidationService` produces
this schema from any `SemanticCheckResult`.
## Pipeline Integration
Use `as_pipeline_results()` to get results compatible with the
`ValidationPipeline`:
```python
service = SemanticValidationService()
results = service.as_pipeline_results(source, "module.py")
# Each result dict has:
# - passed: bool
# - message: str
# - data: dict | None
# - severity: str ("info" | "warn" | "error")
# - mode: str ("required" | "informational")
```
## API Reference
### `SemanticValidationService`
| Method | Description |
|-----------------------|------------------------------------------------|
| `check_file()` | Run all (or selected) rules on source code |
| `as_pipeline_results()` | Same as `check_file` but returns pipeline dicts |
| `normalise_output()` | Normalise a single result to passed/message/data |
| `enabled` | Whether semantic validation is globally enabled |
| `python_enabled` | Whether Python-specific checks are enabled |
| `registry` | The rule registry instance |
| `cache` | The result cache instance |