Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions Docs/extensibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Extensibility

## Adding a New Engine

1. Create a new file in `abevalflow/engines/`:

```python
# abevalflow/engines/my_engine.py
from abevalflow.engines import register_engine
from abevalflow.engines.base import EvalEngine
from abevalflow.gates.base import GateResult, GateType

@register_engine("my-engine")
class MyEngine(EvalEngine):
name = "my-engine"

def read_result(self, reports_dir: Path) -> dict | None:
"""Read engine results from reports directory."""
result_path = reports_dir / "my-engine-report.json"
if not result_path.exists():
return None
return json.loads(result_path.read_text())

def to_gate_result(self, raw_result: dict, policy: GatePolicy) -> GateResult:
"""Convert engine result to standardized GateResult."""
score = raw_result.get("score", 0.0)
threshold = policy.get_gate_policy(self.name).threshold or 0.0

return GateResult(
gate_type=GateType.ENGINE,
gate_name="evaluation",
policy_key=self.name,
passed=score >= threshold,
score=score,
mode=policy.get_gate_policy(self.name).mode,
message=f"MyEngine: score={score:.2f}",
)
```

2. Import in `abevalflow/engines/__init__.py`:

```python
from abevalflow.engines.my_engine import MyEngine
```

## Adding a New Security Gate

1. Create a new file in `abevalflow/gates/security/`:

```python
# abevalflow/gates/security/snyk.py
from abevalflow.gates.security import register_security_gate
from abevalflow.gates.security.base import SecurityGate
from abevalflow.gates.base import GateResult, GateType

@register_security_gate("snyk")
class SnykGate(SecurityGate):
name = "snyk"

def evaluate(self, reports_dir: Path, policy: GatePolicy) -> GateResult:
"""Evaluate Snyk security scan results."""
# Read snyk-report.json and produce GateResult
...
```

2. Import in `abevalflow/gates/security/__init__.py`:

```python
from abevalflow.gates.security.snyk import SnykGate
```

## Adding a New Quality Gate

1. Create a new file in `abevalflow/gates/quality/`:

```python
# abevalflow/gates/quality/custom_review.py
from abevalflow.gates.quality import register_quality_gate
from abevalflow.gates.quality.base import QualityGate
from abevalflow.gates.base import GateResult, GateType

@register_quality_gate("custom-review")
class CustomReviewGate(QualityGate):
name = "custom-review"

def evaluate(self, workspace_root: Path, policy: GatePolicy) -> GateResult:
"""Evaluate custom quality review results."""
# Read review artifacts and produce GateResult
...
```

2. Import in `abevalflow/gates/quality/__init__.py`:

```python
from abevalflow.gates.quality.custom_review import CustomReviewGate
```

## Adding a New Gate Category

To add an entirely new gate category (e.g., "compliance", "performance"):

1. **Add the GateType enum** in `abevalflow/gates/base.py`:

```python
class GateType(str, Enum):
ENGINE = "engine"
SECURITY = "security"
QUALITY = "quality"
COMPLIANCE = "compliance" # New category
```

2. **Create the gate directory** at `abevalflow/gates/compliance/`:

```
abevalflow/gates/compliance/
├── __init__.py # Registry and exports
├── base.py # ComplianceGate base class
└── my_checker.py # First implementation
```

3. **Create the base class** in `abevalflow/gates/compliance/base.py`:

```python
from abc import abstractmethod
from abevalflow.gates.base import GateResult, GateType

class ComplianceGate:
name: str

@abstractmethod
def evaluate(self, reports_dir: Path, policy: GatePolicy) -> GateResult:
"""Evaluate compliance and return standardized GateResult."""
pass
```

4. **Update the scorecard aggregation** in `scripts/aggregate_scorecard.py`:

```python
from abevalflow.gates.compliance import get_all_compliance_gates

# In aggregate_scorecard():
for compliance_gate in get_all_compliance_gates():
if not policy.is_enabled(compliance_gate.name):
continue
gate_result = compliance_gate.evaluate(reports_dir, policy)
gates.append(gate_result)
```

5. **Add the category to policy schema** in `abevalflow/schemas.py` (documentation only, the schema is flexible)
164 changes: 164 additions & 0 deletions Docs/gates-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# Gates Architecture

Gates are evaluation checkpoints that produce standardized results. The unified scorecard aggregates all gate results to produce a final recommendation.

## Gate Types

| Category | Policy Key | Purpose | Implementation |
|----------|------------|---------|----------------|
| **evaluation** | `evaluation` | Results from the selected eval engine | Harbor, ASE, A2A, MCPChecker, or AEH |
| **security** | `security` | Security scanning results | Cisco AI Defense scanner, [harness-eval](https://github.com/redhat-community-ai-tools/harness-eval) deterministic scanner |
| **quality** | `quality` | Quality review results | LLM-powered review, [harness-eval](https://github.com/redhat-community-ai-tools/harness-eval) deterministic quality checks |

## Gate Modes

Each gate operates in one of three modes:

| Mode | Behavior |
|------|----------|
| `disabled` | Gate is skipped entirely |
| `warn` | Gate runs; failures produce warnings but don't block |
| `block` | Gate runs; failures cause the scorecard to fail |

## GateResult Schema

All gates produce a standardized `GateResult`:

```python
class GateResult:
gate_type: GateType # engine, security, or quality
gate_name: str # Category name: "evaluation", "security", or "quality"
policy_key: str # Implementation: "harbor", "cisco", "llm-review", etc.
passed: bool # Whether the gate passed
score: float # Normalized score (0.0 to 1.0)
mode: GateMode # Mode that was applied (disabled/warn/block)
threshold: float | None # Threshold used for pass/fail
findings: list[Finding] # Issues discovered (security/quality gates)
details: dict # Implementation-specific data (e.g., {"engine": "harbor"})
message: str # Human-readable summary
```

The `gate_name` is the category used in policy configuration, while `policy_key` identifies the specific implementation.

## Existing Gates

### Evaluation Gate (`evaluation`)

The primary gate that wraps the selected evaluation engine's results.

- **Location:** `abevalflow/engines/*.py` (each engine produces evaluation gate results)
- **Input:** Engine-specific report from `reports/{submission}/`
- **Engines:** Harbor, ASE, A2A, MCPChecker (selected via `eval_engine` in metadata.yaml)
- **Pass criteria:**
- Harbor/ASE/A2A: `treatment_score - control_score >= threshold` (default threshold: 0.0)
- MCPChecker: All tasks pass verification
- **Score:** Mean reward or pass rate depending on engine

### Security Gate (`security`)

Two scanners feed into the security gate:

**Cisco AI Defense** (`CiscoGate`):
- **Location:** `abevalflow/gates/security/cisco.py`
- **Input:** `reports/{submission}/security-scan.json`
- **Scanner:** Cisco AI Defense model-based detection

**harness-eval** (`SkillMdScannerGate`):
- **Location:** `abevalflow/gates/security/skillmd_scanner.py`
- **Input:** `reports/{submission}/skillmd-security-scan.json`
- **Scanner:** [harness-eval](https://github.com/redhat-community-ai-tools/harness-eval) `skill-submission-scan` CLI (27 rule categories, 97 deterministic rules)
- **Includes:** Optional LLM semantic security review (anti-jailbreak, semantic attacks, description-behavior mismatch)

Both gates use the same pass criteria:
- `warn` mode: Always passes (findings are advisory)
- `block` mode: Fails if any HIGH or CRITICAL findings exist
- **Score:** Weighted average based on finding severities

### Quality Gate (`quality`)

Two sources feed into quality gates:

**LLM Quality Review** (`LLMReviewGate`):
- **Location:** `abevalflow/gates/quality/llm_review.py`
- **Input:** `{workspace}/_ai_review.json`
- **Dimensions evaluated:** coherence, coverage, clarity, feasibility, robustness
- **Default threshold:** 0.6

**harness-eval Quality** (`SkillMdQualityGate`):
- **Location:** `abevalflow/gates/quality/skillmd_quality.py`
- **Input:** `{workspace}/skillmd-quality-scan.json`
- **Checks:** description quality, broken references, imprecise instructions, unfinished content, stale references, scope overreach, token budget, and more

## Scorecard

The scorecard is the single source of truth for submission evaluation, aggregating all gate results with configurable policy.

### Scorecard Schema

```python
class Scorecard:
submission_name: str # Name of the evaluated submission
pipeline_run_id: str # Tekton PipelineRun ID
eval_engine: str # Primary evaluation engine used
gates: list[GateResult] # All gate results
policy: GatePolicy # Policy that was applied
recommendation: Recommendation # pass, warn, or fail
recommendation_reason: str # Human-readable explanation
gates_passed: int # Count of passed gates
gates_failed: int # Count of failed gates
blocking_gates_passed: int # Count of passed blocking gates
blocking_gates_failed: int # Count of failed blocking gates
```

### Combination Modes

| Mode | Logic |
|------|-------|
| `all_pass` | All blocking gates must pass; failing warn gates produce warnings |
| `any_pass` | At least one blocking gate must pass |
| `weighted` | Weighted average of gate scores determines outcome |

### Output

The scorecard is written to `reports/{submission}/scorecard.json` and includes:
- All gate results with scores and findings
- Final recommendation with reasoning
- Provenance metadata (commit SHA, branch, pipeline run ID)

## Gate Policy Configuration

Gate policies are configured in `metadata.yaml` under the `gate_policy` key:

```yaml
# metadata.yaml
name: my-skill
eval_engine: harbor

gate_policy:
default_mode: warn # Default mode for all gates
combination: all_pass # How to combine gate results

gates:
# Security gate configuration
security:
mode: block # Fail the scorecard on security issues
threshold: 0.8 # Minimum score to pass

# Quality gate configuration
quality:
mode: warn # Advisory only
threshold: 0.6 # Threshold for pass/fail

# Engine gate configuration (uses eval_engine automatically)
evaluation:
mode: block
threshold: 0.0 # Any positive uplift passes
```

### GatePolicyItem Options

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `mode` | `disabled`/`warn`/`block` | `warn` | Enforcement mode |
| `threshold` | `float` | Gate-specific | Score threshold for pass/fail |
| `weight` | `float` | `1.0` | Weight for weighted combination mode |
36 changes: 36 additions & 0 deletions Docs/persistence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Persistence

## MinIO (Object Storage)

Reports and artifacts are uploaded to MinIO under a timestamped prefix:

```
s3://ab-eval-reports/YYYYMMDD_hhmmss_{submission}_{run-id}/
├── report.json # Main evaluation report
├── report.md # Human-readable report
├── scorecard.json # Unified scorecard
├── security_scans/ # Security scan results
│ └── security-scan.json
├── generated/ # AI-generated artifacts
│ ├── instruction.md
│ └── test_outputs.py
├── scaffolded/ # Scaffolded configs and review
│ └── _ai_review.json
└── trials/ # Per-trial artifacts (Harbor)
├── trial_001/
│ ├── agent/
│ └── verifier/
└── ...
```

## PostgreSQL (Results Database)

Evaluation results are persisted for historical analysis and monitoring:

- **Script:** `scripts/store_results.py`
- **Data stored:**
- Submission metadata
- Per-trial results (Harbor/ASE)
- Security scan findings
- Aggregate statistics
- Scorecard recommendation
Loading
Loading