A small, runnable reference implementation of the control path around an AI-enabled workflow. It demonstrates how a request can move through deterministic intake policy, explicit human approval, a replaceable provider adapter, pre-release evaluation, and content-minimizing audit logging.
The default provider is local and deterministic. It makes no network calls, needs no API key, and is intentionally not presented as an AI model. That keeps the control behavior observable and reproducible.
This repository is an educational starting point, not a production service or a compliance certification. Production use requires organization-specific identity, authorization, storage, observability, model-risk, privacy, and incident-response controls.
| Control | Default behavior |
|---|---|
| Typed intake | Rejects malformed request and approval documents before orchestration |
| Policy gate | Allow-lists task types and blocks configured classifications, terms, and oversized input |
| Human approval | Requires a matching, named approval record before provider invocation |
| Provider boundary | Uses a protocol so the local adapter can be replaced without bypassing controls |
| Output evaluation | Checks non-empty output, size, prohibited terms, and an expected prefix |
| Audit trail | Appends state transitions and SHA-256 fingerprints without storing prompt or output text |
| Safe failure | Suppresses output when policy, approval, provider, evaluation, or audit controls fail |
Requirements: Python 3.11 or newer. There are no runtime dependencies.
git clone https://github.com/Tier9AI/controlled-ai-workflow-reference.git
cd controlled-ai-workflow-reference
make demoThe successful demonstration writes an append-only JSONL trail to artifacts/audit.jsonl and returns evaluated output on stdout.
Run the same request without approval:
PYTHONPATH=src python3 -m tier9_workflow.cli \
--request examples/request-safe.json \
--prettyIt returns awaiting_approval with exit code 2; the provider is not called. Try the blocked classification path:
PYTHONPATH=src python3 -m tier9_workflow.cli \
--request examples/request-blocked.json \
--prettyThat returns rejected with exit code 3, also before provider execution.
flowchart LR
A[Typed intake] --> B{Policy gate}
B -->|blocked| R[Reject]
B -->|allowed| C{Human approval}
C -->|missing| P[Pending]
C -->|denied| D[Deny]
C -->|approved| E[Provider adapter]
E -->|error| F[Fail safe]
E --> G{Output evaluation}
G -->|failed| S[Suppress output]
G -->|passed| H[Release output]
A -. state events .-> J[(JSONL audit)]
B -.-> J
C -.-> J
E -. fingerprints .-> J
G -. results .-> J
See Architecture for trust boundaries and sequence details, and Control mapping for implementation locations and production extensions.
config/ Example policy and evaluation configuration
docs/ Architecture and control-design notes
examples/ Safe, blocked, approved, and denied sample inputs
src/tier9_workflow/ Minimal workflow implementation
tests/ Positive and negative-path tests
.github/workflows/ Dependency-free CI check
make checkThe tests verify the happy path as well as policy rejection, missing or denied approval, mismatched approval, provider failure, evaluation failure, audit failure, and raw-content exclusion from the audit trail.
config/policy.example.json contains two independent control groups:
policyis evaluated before any provider call.evaluationis evaluated after provider output but before release.
Configuration is deliberately small and inspectable. A production implementation should load versioned policy from an approved control plane, validate its provenance, and record the policy version with each run.
Implement the Provider protocol in providers.py, then inject that adapter when constructing ControlledWorkflow. Keep authentication, retries, rate limits, timeouts, and provider-specific data handling inside the adapter. Do not move provider calls ahead of policy or approval gates.
A production adapter should also:
- Use short-lived credentials from a secrets manager.
- Apply explicit request timeouts and bounded retries.
- Record provider and model versions without logging sensitive content.
- Define retention and training-use settings contractually and technically.
- Map provider errors to stable internal categories.
- The audit trail stores request and output fingerprints, sizes, decisions, and stable reason codes—not the original text.
- SHA-256 fingerprints provide correlation and change detection; they do not anonymize low-entropy input. Use keyed hashing when dictionary attacks are a realistic concern.
- The local JSON approval proves control flow, not approver identity. Production approval must come from an authenticated, authorized system and should be bound to request content and policy version.
- Regex-free literal blocked-term checks keep this example understandable; they are not a substitute for data-loss prevention or adversarial-content controls.
- Evaluation examples are deterministic assertions, not a claim of model quality or safety.
See SECURITY.md for reporting guidance and the explicit security boundary.
MIT. See LICENSE.