# CLAUDE.md

## What is Edictum

Runtime contract enforcement for AI agent tool calls. Deterministic pipeline: preconditions, postconditions, session contracts, principal-aware enforcement. Six framework adapters (LangChain, CrewAI, Agno, Semantic Kernel, OpenAI Agents SDK, Claude Agent SDK). Zero runtime deps in core.

Current version: 0.8.1 (PyPI: `edictum`)

## Architecture: Open-Core with ee/ Directory

Single repo, two license zones. PostHog/GitLab monorepo pattern.

```
edictum/
├── src/edictum/          <- MIT license (open source core)
│   ├── core/             pipeline, envelope, session (MemoryBackend)
│   ├── contracts/        YAML parser, templates, composition
│   ├── adapters/         6 framework adapters
│   ├── audit.py          AuditEvent, StdoutAuditSink, FileAuditSink, RedactionPolicy
│   ├── telemetry.py      OTel spans, GovernanceTelemetry
│   ├── cli/              check, test, validate, diff, replay
│   └── pii.py            PIIDetector protocol + PIIMatch (interface only)
├── ee/                   <- Proprietary license (not yet created)
│   ├── pii/              RegexPIIDetector, PresidioPIIDetector, CompositePIIDetector
│   ├── sinks/            Webhook, Splunk HEC, Datadog
│   ├── server/           Central policy server, hot-reload, dashboard
│   ├── auth/             JWT/OIDC verification, SSO
│   ├── sequences/        Sequence-aware contracts
│   └── nl_authoring/     Natural language -> YAML contract generation
├── LICENSE               <- MIT (covers everything except ee/)
└── README.md
```

## THE ONE RULE

**Core code (src/edictum/) NEVER imports from ee/.**
**ee/ imports from core freely.**

Core provides protocols/interfaces. ee/ provides implementations.

## OSS Core (MIT)

- GovernancePipeline (evaluation engine)
- ToolEnvelope, Principal model, Session (MemoryBackend)
- YAML contract parsing + validation + templates + composition
- All 6 framework adapters
- Observe mode (shadow deploy)
- on_postcondition_warn callbacks
- edictum check + edictum test CLI
- AuditEvent dataclass + StdoutAuditSink + FileAuditSink (.jsonl) + RedactionPolicy
- OTel span instrumentation + GovernanceTelemetry
- PIIDetector protocol + RegexPIIDetector (8 built-in regex patterns)

## Enterprise (ee/) — not yet created

- PII detection backends: PresidioPIIDetector, CompositePIIDetector
- YAML `pii_detection` shorthand
- Audit sinks: Webhook, Splunk HEC, Datadog
- Alert rules (denial spikes, PII detections, session exhaustion)
- Sequence-aware contracts
- NL -> YAML contract authoring
- Central Policy Server (agent pull, versioning, hot-reload)
- Dashboard (denial rates, contract drift)
- RBAC for contract management
- SSO integration (Okta, Azure AD)
- JWT/OIDC principal verification
- Human approval workflows
- Cross-agent session tracking

## Boundary Principle

The tier split follows one rule: **evaluation engine = OSS, infrastructure = enterprise.**

- Pipeline that takes a tool call and returns allow/deny/warn -- OSS
- Persistence beyond local files, networking, coordination -- enterprise
- PIIDetector protocol + RegexPIIDetector in OSS. ML-grade detection (Presidio, Composite) -- enterprise
- Stdout + File (.jsonl) sinks for dev/local audit -- OSS. Network destinations (Webhook, Splunk, Datadog) -- enterprise
- OTel instrumentation (emitting spans) -- OSS. Dashboards and alerting -- enterprise
- Session (MemoryBackend) for single-process -- OSS. Multi-process coordination via Edictum Server -- enterprise

## Dropped Features (do NOT implement)

- `reset_session()` — new run_id handles this naturally
- Redis StorageBackend — not our problem, application layer concern
- DB StorageBackend — OTel already covers queryable audit data

## What's Shipped

- v0.5.0: Core library — pipeline, 6 adapters, YAML contracts, CLI check, OTel, observe mode
- v0.5.1: Adapter bug fixes (CrewAI, Agno, SK)
- v0.5.2: Adapter bug fixes (LangChain, OpenAI)
- v0.5.3: Claude SDK on_postcondition_warn callback, edictum test CLI
- v0.5.4: Dry-run evaluation API (evaluate, evaluate_batch), edictum test --calls
- v0.6.0: Postcondition enforcement effects (redact/deny), SideEffect classification
- v0.6.1: YAML tools: section for side-effect classifications
- v0.6.2: Renamed to_sdk_hooks() → to_hook_callables()
- v0.7.0: env.* selector, Edictum.from_multiple() guard merging, Claude Code GitHub Actions
- v0.8.0: Bundle composition (compose_bundles, from_yaml multi-file), dual-mode evaluation
- v0.8.1: RuleResult → ContractResult rename, terminology enforcement
- Docs overhaul: homepage, quickstart, concepts section, patterns, 7 guides
- edictum-demo repo: github.com/acartag7/edictum-demo

## Session Model

MemoryBackend stores counters in a Python dict — one process, one agent. This covers the vast majority of use cases. For multi-agent coordination across processes, the Edictum Server (planned, ee/) handles centralized session tracking. There is no DIY Redis/DynamoDB path.

## Build & Test

```bash
pytest tests/ -v              # full test suite
ruff check src/ tests/        # lint
python -m mkdocs build --strict  # docs build
edictum validate contracts.yaml  # validate YAML contracts
```

## Code Conventions

- Python 3.11+
- `from __future__ import annotations` in every file
- Frozen dataclasses for immutable data
- Type hints everywhere
- Async: all pipeline, session, and audit sink methods are async
- Testing: pytest + pytest-asyncio, maintain 97%+ coverage
- Commits: conventional commits (feat/fix/docs/test/refactor/chore), no Co-Authored-By
- PRs: small and focused, Linear ticket in PR description not title

## Terminology Enforcement

The binding glossary is `.docs-style-guide.md`. ALL code, comments, docstrings, CLI output, docs, release notes, and CHANGELOG entries MUST use these canonical terms:

| Wrong | Correct |
|-------|---------|
| rule / rules (in prose) | contract / contracts |
| blocked | denied |
| engine (for runtime) | pipeline |
| shadow mode | observe mode |
| alert | finding |

**Exception**: None. There are no exceptions. The class was renamed from `RuleResult` to `ContractResult` in v0.8.1 to eliminate the last holdout.

Before writing ANY user-facing string, comment, docstring, or documentation, check it against the glossary.

## Pre-Release Checklist

Before tagging a release:

1. `grep -rn` for banned terms (rule/rules in prose, blocked, engine, shadow mode, alert) in src/, docs/, CHANGELOG.md
2. Verify CLI output strings match .docs-style-guide.md terminology
3. Verify YAML examples in release notes use correct schema (`then:` block with `effect:` and `message:`, not `action:`)
4. Verify release notes prose uses canonical terms
5. Run: `pytest tests/ -v && ruff check src/ tests/ && python -m mkdocs build --strict`

## YAML Schema (locked)

- `apiVersion: edictum/v1`, `kind: ContractBundle`
- Contract types: `type: pre` (deny only), `type: post` (warn/redact/deny), `type: session` (deny only)
- Conditions: `when:` with boolean AST (`all/any/not`) and leaves (`selector: {operator: value}`)
- 15 operators: exists, equals, not_equals, in, not_in, contains, contains_any, starts_with, ends_with, matches, matches_any, gt, gte, lt, lte
- Missing fields evaluate to `false`. Type mismatches yield deny/warn + `policy_error: true`
- Regex: Python `re` module, single-quoted in YAML docs (`'\b'` not `"\b"`)
- Bundle hash: SHA256 of raw YAML bytes -> `policy_version` on every audit event
