CommunityPay evaluates risk conditions against recorded signals and creates enforcement holds — called exclusions or bind blocks — when conditions warrant intervention. This page describes how triggers are defined, how exclusions are created and managed, and what audit trail is produced at each step.
What Is an Exclusion?
An exclusion is an active enforcement hold on an entity (HOA or vendor). What a hold does depends on its effect type:
| Effect Type | What It Does |
|---|---|
| BLOCK | The l5_exclusion guard refuses every posting for the association while the exclusion is ACTIVE. The guard cannot be overridden with an AuditOverride; the exclusion's own lifecycle — lifted, placed under appeal, overturned on appeal, or expired — is the only way out. The guard reads exclusions on the association only; an exclusion on a vendor is recorded and refuses no posting |
| REVIEW | Recorded and flagged for manual review. No guard refuses a posting because of it |
| WARN | Recorded as a warning. No guard refuses a posting because of it |
Exclusions are not punitive. They are protective controls that prevent high-risk operations from executing before the risk condition is evaluated and addressed.
Trigger Architecture
How Triggers Are Defined
An ExclusionTrigger is a versioned definition that specifies:
- What to evaluate: Which signals to read from the signal registry (namespaces RISK_SIGNAL, TELEMETRY, UNDERWRITING, VENDOR_COMPLIANCE, and DEBT_SERVICE)
- What condition to check: A JSON expression format that references specific signal values
- What to do when the condition is met: Block, require review, or warn
- How severe the condition is: LOW, MEDIUM, HIGH, or CRITICAL
- How long the resulting exclusion lasts: Configurable duration in days, or indefinite until manually resolved
Trigger Types
| Type | Evaluation Pattern |
|---|---|
| THRESHOLD | A numeric signal exceeds or falls below a defined value |
| PATTERN | A detectable pattern across multiple signals |
| EVENT | A single discrete event triggers the exclusion |
| COMPOUND | Multiple conditions must be met simultaneously |
Immutability of Active Triggers
When a trigger is in DRAFT status, its conditions can be modified freely. Once a trigger is activated (status = ACTIVE), its conditions become immutable.
This is enforced at the application layer:
- At every save, a SHA-256 hash of the conditions JSON is computed and stored in
conditions_hash - If an existing ACTIVE trigger is being saved and the new
conditions_hashdiffers from the stored hash, the save is rejected with aValueError - To change an active trigger's conditions, a new version must be created (the old version is archived)
This ensures that every ExclusionTriggerHit can always reference the exact conditions that were in effect when it fired. Historical evaluation can be reconstructed precisely.
Version Management
Triggers use a correlation-based versioning model:
trigger_id: A stable UUID that remains constant across all versions of the same logical triggerversion: An incrementing integer (1, 2, 3...)parent_version: A foreign key to the previous version
Only one version of a given trigger_id can be ACTIVE at any time, and a database constraint holds that line. The workflow is: archive the current version, then activate the new version.
A trigger may be ACTIVE only while every signal its conditions read has a live source. A trigger whose signals nothing writes is held: it records why it is not active and decides nothing. In production today, 0 triggers are ACTIVE and 0 eligibility rules are active.
Exclusion Lifecycle
When a trigger fires, the following sequence occurs:
1. Trigger Evaluation
The eligibility evaluator evaluates active triggers against current signal values. When conditions are met, an ExclusionTriggerHit is created. Evaluation runs when an eligibility evaluation is requested, through the eligibility API or the backfill command; no timer drives it today.
The trigger hit is an ARTIFACT — fully immutable. It captures:
- The trigger version that fired
- All evaluated signal values at the moment of evaluation
- The computed result
- The timestamp
2. Exclusion Creation
An Exclusion record is created with status ACTIVE for a BLOCK or WARN trigger, and with status PENDING for a trigger that requires review. The exclusion references:
- The trigger hit that caused it
- The affected entity (HOA or vendor)
- The effect type (copied from the trigger)
- The severity
- The expiration date (current time + trigger's
exclusion_duration_days, or null for indefinite)
3. Notification
If the trigger has auto_notify enabled, the notification service sends alerts to configured recipients. Notification events are recorded in the append-only ExclusionNotificationEvent log.
4. Enforcement
While a BLOCK exclusion is ACTIVE, the l5_exclusion guard refuses every posting for the association, in every flow and transaction type, and the refusal is recorded in the EnforcementDecision with the exclusion's identifier and the trigger that created it. REVIEW and WARN exclusions are recorded and surfaced; no guard refuses a posting for them.
5. Resolution
An exclusion leaves ACTIVE in four ways, and an exclusion under appeal no longer blocks:
| Status | How | Audit |
|---|---|---|
| LIFTED | A user lifts the exclusion early with a documented reason | Reason, user, and timestamp recorded |
| APPEALED | A user starts an appeal | The appeal is recorded; the exclusion's status is Under Appeal |
| OVERTURNED | The appeal succeeds | Status transition and timestamp recorded |
| EXPIRED | The exclusion's duration elapses | Automatic transition by the monitoring task; timestamp recorded |
All status transitions are recorded in the append-only ExclusionStatusHistory table. No status history record can be updated or deleted. The monitoring task runs every four hours: it expires exclusions past their expiry, tracks approaching deadlines, and clears finished cooldown periods.
Cooldown Periods
After an exclusion is resolved, the trigger enters a cooldown period for the affected entity. During cooldown, the same trigger cannot fire again for the same entity.
The default cooldown is 30 days, configurable per trigger. This prevents rapid cycling of exclusion creation and resolution that could indicate an unresolved underlying condition.
Escalation Chain
Unresolved exclusions follow the association's escalation rules. Each rule names the event it applies to (an exclusion pending review, an active exclusion, a compliance alert, or an expiring document), the minimum severity, who is told (the association administrator, a board member, the property manager, an executive, or an external contact), and how long to wait.
By default a rule waits three days without action before it escalates, and two more days before the chain moves to the next rule. Each escalation is recorded as an immutable event. Rules are processed every four hours, escalation chains are checked every two hours, and reminders go out at 9:00 AM.
SLA Tracking
The escalation system tracks:
- Acknowledgment: Has the responsible party acknowledged the exclusion?
- Response time: How long between notification and acknowledgment?
- Resolution time: How long between creation and resolution?
- SLA compliance: Which notified escalations were acknowledged within 48 hours?
These metrics feed into the Control Effectiveness measurement system and the Governance Controls Attestation (GCA).
Severity Classification
Each trigger and each exclusion carries a severity — LOW, MEDIUM, HIGH, or CRITICAL. An escalation rule applies from a minimum severity upward, so a board that wants a quicker chain for CRITICAL conditions writes a separate rule for them.
What This System Proves
The exclusion enforcement system creates a complete, verifiable record of risk management:
- Detection is deterministic: The same signal values always produce the same hits, and every hit is recorded
- Response is documented: Every notification, acknowledgment, and escalation is recorded
- Resolution is auditable: Every exclusion closure includes who resolved it, when, and why
- History is preserved: Trigger hits, exclusion records, and status transitions are immutable
- Controls are measurable: SLA tracking and escalation metrics prove that the system operates, not just that it exists
An auditor reviewing the exclusion history can reconstruct: what triggered the hold, what the signals showed at that moment, who was notified, whether they responded within SLA, how the hold was resolved, and what documentation supported the resolution.
CARI Integration
Exclusions do not enter the CARI score. The CARI Enforcement Integrity sub-score reads two signals measured near these: the enforcement block rate and approval SLA breaches over the trailing twelve months. The trigger, resolution, and escalation measures on this page are not inputs.
For published methodology and component weights, see CARI Methodology and Scoring Framework.
This describes production controls as of October 2026
How CommunityPay Enforces This