# Compliance & Tenant Isolation

The `llm_toolkit_schema.compliance` package provides programmatic compliance tests
that enterprise teams and third-party tool authors can run in CI pipelines, at
deployment time, or as part of security audits — without requiring pytest.

## Compatibility checklist (`test_compatibility`)

The five-point compatibility checklist verifies that a batch of events meets
the llm-toolkit-schema v1.0 adoption requirements:

```python
from llm_toolkit_schema.compliance import test_compatibility

result = test_compatibility(events)

if result.passed:
    print(f"All {result.events_checked} events are compatible.")
else:
    for v in result.violations:
        print(f"[{v.check_id}] {v.event_id}: {v.rule} — {v.detail}")
```

The five checks:

| Check | Rule | Description |
|-------|------|-------------|
| CHK-1 | Required fields present | `schema_version`, `source`, and `payload` must be non-empty. |
| CHK-2 | Event type registered or valid custom | Must be a first-party `EventType` value **or** pass `validate_custom`. |
| CHK-3 | Source identifier format | Must match `^[a-z][a-z0-9-]*@\d+\.\d+(\.\d+)?([.-][a-z0-9]+)*$`. |
| CHK-5 | Event ID is a valid ULID | `event_id` must be a well-formed 26-character ULID string. |

## Audit chain integrity (`verify_chain_integrity`)

Wraps `verify_chain()` with higher-level diagnostics:

```python
from llm_toolkit_schema.compliance import verify_chain_integrity

result = verify_chain_integrity(
    events,
    org_secret="my-org-secret",
    check_monotonic_timestamps=True,   # default
)

print(f"Events verified: {result.events_verified}")
print(f"Gaps detected:   {result.gaps_detected}")

for v in result.violations:
    # v.violation_type: "tampered" | "gap" | "non_monotonic_timestamp"
    print(f"[{v.violation_type}] {v.event_id}: {v.detail}")
```

## Multi-tenant isolation (`verify_tenant_isolation`)

Verify that events from two tenants share no `org_id` values and that each
tenant's events are internally consistent:

```python
from llm_toolkit_schema.compliance import verify_tenant_isolation

result = verify_tenant_isolation(
    tenant_a_events,
    tenant_b_events,
    strict=True,    # flag events missing org_id (default)
)

if not result:
    for v in result.violations:
        # v.violation_type: "missing_org_id" | "mixed_org_ids" | "shared_org_id"
        print(f"  {v.violation_type}: {v.detail}")
```

## Scope verification (`verify_events_scoped`)

Assert that all events in a batch belong to an expected org/team:

```python
from llm_toolkit_schema.compliance import verify_events_scoped

result = verify_events_scoped(
    events,
    expected_org_id="org_01HX",
    expected_team_id="team_engineering",
)

if not result:
    for v in result.violations:
        # v.violation_type: "wrong_org_id" | "wrong_team_id"
        print(f"  {v.event_id}: {v.detail}")
```

## Using compliance results

All result objects are truthy on success and falsy on failure:

```python
result = verify_tenant_isolation(a, b)
assert result, f"Isolation failed: {result.violations}"

# Or use in conditional logic:
if not result:
    notify_security_team(result.violations)
```

## CI integration

```python
# conftest.py or test_compliance.py
import pytest
from llm_toolkit_schema.compliance import test_compatibility

def test_all_events_compatible(captured_events):
    result = test_compatibility(captured_events)
    assert result, "\n".join(
        f"  [{v.check_id}] {v.event_id}: {v.detail}"
        for v in result.violations
    )
```
