Cre Rule Hierarchy
Source: docs/architecture/cre-rule-hierarchy.md
# Compliance Rules Engine (CRE) - Rule Hierarchy Design
**Source:** `_archived/services/compliance-rules-engine/README.md`
**Status:** Design reference for monolith integration
**Target Location:** `packages/rules-engine/`
---
## Overview
The CRE defines a 6-level hierarchical rule cascade for compliance validation. This design should be adopted into `packages/rules-engine/` rather than rebuilt from scratch.
---
## Hierarchy Levels
Rules cascade from general to specific. Child rules **accumulate** parent requirements (never remove them).
```
Level 0: Global (*)
↓
Level 1: Regional (EU, USMCA, ASEAN)
↓
Level 2: Country (CA, US, UK)
↓
Level 3: Province/State (ON, US-CA, TX)
↓
Level 4: Municipality (Toronto, LA)
↓
Level 5: Product Type (alcohol, dangerous_goods)
```
### Example Cascade
For a wine import to Ontario, Canada:
1. **Global** - Basic HS code format validation
2. **Regional** - (none for this case)
3. **Country (CA)** - GST required, CBSA documentation
4. **Province (ON)** - HST rate validation, LCBO requirements
5. **Municipality** - (none for this case)
6. **Product (alcohol)** - Excise license, age verification, ABV limits
---
## Rule Merging Logic
1. Rules applied in hierarchical order (global → specific)
2. Child rules **accumulate** requirements from parents
3. Child rules can **override** parent validations with `override: true` flag
4. Conflicts resolved by **severity** (error > warning > info)
5. Compiled rulesets cached for performance
### Conflict Resolution
When the same field has conflicting validations:
| Scenario | Resolution |
|----------|------------|
| Parent: warning, Child: error | Child wins (higher severity) |
| Parent: required, Child: optional | Parent wins (accumulation) |
| Parent: range 0-100, Child: range 10-50 | Child wins (more specific) with `override: true` |
---
## Rule Schema
From the archived CRE design:
```typescript
interface ComplianceRule {
id: string // UUID
rule_id: string // Human-readable ID (e.g., "ca-on-001")
version: string // Semantic version
level: HierarchyLevel // global, regional, country, province, municipality, product
applies_to: {
countries: string[] // ISO 3166-1 alpha-2
regions: string[] // EU, USMCA, ASEAN, etc.
provinces: string[] // ISO 3166-2 subdivision
municipalities: string[] // City names
product_categories: string[] // alcohol, dangerous_goods, etc.
}
requirements: {
fields: string[] // Required fields for this rule
conditions: string[] // Preconditions (e.g., "gst_rate > 0")
}
validations: Validation[] // List of validation checks
override: boolean // If true, can override parent rules
description: string
metadata: Record<string, unknown>
effective_date: Date // When rule becomes active
expiry_date?: Date // When rule expires (null = no expiry)
created_at: Date
updated_at: Date
}
// Consistency rule:
// - `level` must match the most specific populated field in `applies_to`.
// - Reject mismatches (e.g., level=country while provinces[] is populated).
type HierarchyLevel =
| 'global'
| 'regional'
| 'country'
| 'province'
| 'municipality'
| 'product'
```
---
## Validation Types
The CRE supports four validation types:
### 1. Presence
Field must be present and non-empty.
```json
{
"field": "sku",
"type": "presence",
"rule": "presence",
"message": "SKU is required",
"severity": "error"
}
```
### 2. Format
Field must match regex pattern.
```json
{
"field": "hs_code",
"type": "format",
"rule": "^[0-9]{4}\\.[0-9]{2}\\.[0-9]{2}\\.[0-9]{2}$",
"message": "HS code must be in format XXXX.XX.XX.XX",
"severity": "error"
}
```
### 3. Range
Field must be within numeric range.
```json
{
"field": "declared_value",
"type": "range",
"rule": "value > 0",
"message": "Declared value must be positive",
"severity": "error"
}
```
### 4. Custom
Extensible validation functions.
```json
{
"field": "license_number",
"type": "custom",
"rule": "check_license_validity(license_number)",
"message": "Invalid license",
"severity": "error"
}
```
---
## Validation Interface
```typescript
interface Validation {
field: string
type: 'presence' | 'format' | 'range' | 'custom'
rule: string // Regex, expression, or function name
message: string // Human-readable error message
severity: 'error' | 'warning' | 'info'
}
interface ValidationResult {
validation_id: string
passed: boolean
rules_applied: string[] // List of rule_ids that were evaluated
violations: Violation[]
execution_time_ms: number
timestamp: Date
correlation_id?: string
}
interface Violation {
rule_id: string
field: string
severity: 'error' | 'warning' | 'info'
message: string
actual_value?: unknown
expected?: string
}
```
---
## Caching Strategy
The CRE uses Redis for compiled ruleset caching:
```
Cache key: rules:compiled:{region}:{country}:{province}:{product_category}
Examples:
- rules:compiled:USMCA:CA:ON:alcohol → Rules for alcohol in Ontario, Canada within USMCA context
- rules:compiled::US:: → Rules for US (all products, no region)
- rules:compiled::: → Global rules only
```
**TTL:** 1 hour (3600 seconds)
**Invalidation:** On rule changes or `regulation.changed` events
---
## Integration with Detector Registry
The CRE validation types map to detector patterns:
| CRE Validation | Detector Pattern |
|----------------|------------------|
| Presence | Required field check in detector |
| Format | Input validation before detection |
| Range | Threshold-based detection (e.g., variance > X) |
| Custom | Detector-specific logic |
### Proposed Integration
```typescript
// In packages/rules-engine/src/hierarchy.ts
export function compileRulesForContext(context: {
country: string
province?: string
productCategory?: string
}): CompiledRuleset {
// 1. Load rules from DB ordered by hierarchy level
// 2. Merge rules (child accumulates parent)
// 3. Resolve conflicts by severity
// 4. Return compiled ruleset
}
// In apps/api/src/lib/ship/detectors/
export class AmountVarianceDetector implements Detector {
async detect(input, context, config) {
// Load rules for this context
const rules = await compileRulesForContext({
country: input.destinationCountry,
productCategory: 'parcel'
})
// Apply rules to detection logic
// ...
}
}
```
---
## Migration Path
### Phase 1.5: Adopt CRE Hierarchy
1. Add `level` and `applies_to` fields to `RuleDefinition` table
2. Implement `compileRulesForContext()` in `packages/rules-engine/`
3. Add Redis caching for compiled rulesets
4. Update detectors to load rules from hierarchy
### Schema Addition
```prisma
model RuleDefinition {
// ... existing fields
level HierarchyLevel
appliesTo Json // { countries: [], regions: [], provinces: [], ... }
requirements Json // { fields: [], conditions: [] }
validations Json // Array of Validation objects
override Boolean @default(false)
effectiveDate DateTime?
expiryDate DateTime?
}
enum HierarchyLevel {
global
regional
country
province
municipality
product
}
```
---
## References
- **Source design:** `_archived/services/compliance-rules-engine/README.md`
- **Current rules engine:** `packages/rules-engine/`
- **Data foundation strategy:** `docs/proposals/data-foundation-strategy.md`