Decision Flow
The gate decision flow defines how Turqoa evaluates transaction data and produces gate decisions. It is a configurable pipeline of rules, conditions, and actions that transforms raw perception and validation outputs into structured approve/review/deny decisions.
Gate Decision Pipeline
The decision pipeline processes each transaction through four sequential phases:
- Pre-check — Fast-fail conditions that immediately deny or flag a transaction
- Evaluation — Full rule set evaluation against enriched transaction data
- Resolution — Conflict resolution when multiple rules match
- Post-processing — Decision enrichment, notification triggers, and audit packaging
Transaction Data
│
▼
┌──────────┐ deny ┌─────────┐
│ Pre-check │───────────→│ Output │
│ (watchlist,│ └─────────┘
│ expired) │
└─────┬──────┘
│ pass
▼
┌──────────┐
│ Evaluate │
│ (rule set)│
└─────┬──────┘
│ matches
▼
┌──────────┐
│ Resolve │
│ (priority)│
└─────┬──────┘
│ decision
▼
┌──────────────┐
│ Post-process │
│ (notify, log) │
└───────┬───────┘
│
▼
┌─────────┐
│ Output │
└─────────┘
Rule Configuration
Rules are defined in YAML and loaded by the decision engine at startup. Each rule has a name, priority, conditions, and actions:
# ~/.turqoa/sites/my-terminal/rules.yaml
rules:
# Pre-check rules (high priority, evaluated first)
- name: deny_watchlist
priority: 1
phase: precheck
when:
any:
- field: validation.watchlist.container
op: eq
value: true
- field: validation.watchlist.vehicle
op: eq
value: true
then:
decision: deny
reason: "Watchlist match detected"
alert: critical
notify:
- security_team
- terminal_manager
- name: deny_no_appointment
priority: 2
phase: precheck
when:
- field: validation.tos.appointment.status
op: in
value: [not_found, cancelled]
then:
decision: deny
reason: "No valid appointment found"
# Standard evaluation rules
- name: auto_approve_clean
priority: 100
phase: evaluation
when:
all:
- field: ocr.container.confidence
op: gte
value: 0.95
- field: ocr.plate.confidence
op: gte
value: 0.90
- field: validation.tos.appointment.status
op: eq
value: confirmed
- field: validation.customs.clearance
op: eq
value: granted
- field: damage.max_severity
op: in
value: [none, low]
then:
decision: approve
auto: true
reason: "All automated checks passed"
Auto-Approve Criteria
For a transaction to be auto-approved (no operator involvement), all of the following must be true by default:
| Criterion | Threshold | Configurable |
|---|---|---|
| Container OCR confidence | >= 0.95 | Yes |
| Plate OCR confidence | >= 0.90 | Yes |
| TOS appointment status | confirmed | No |
| Customs clearance | granted | Yes (can be disabled) |
| Damage severity | none or low | Yes |
| Watchlist check | clear | No |
| Seal verification (if required) | matched | Yes |
Note: Auto-approve criteria can be relaxed or tightened per lane and per transaction direction (inbound vs. outbound). Outbound transactions typically have fewer checks.
Manual Review Triggers
Transactions are routed to the operator review queue when:
- Any OCR confidence falls below the auto-approve threshold
- Damage severity is medium or high
- TOS appointment data is ambiguous (e.g., multiple matching appointments)
- Seal number does not match the expected value
- No rule produces an auto-approve decision
Review queue entries include full context:
{
"transaction_id": "txn_8b3c4d2e",
"lane": "01",
"direction": "inbound",
"timestamp": "2026-04-06T09:15:23Z",
"review_reason": "Container OCR confidence below threshold",
"ocr_results": {
"container": { "value": "MSCU1234567", "confidence": 0.82 },
"plate": { "value": "34 ABC 123", "confidence": 0.96 }
},
"damage": { "detected": false },
"images": ["front.jpg", "rear.jpg", "plate.jpg"],
"suggested_decision": "approve",
"suggested_reason": "OCR marginally below threshold, all other checks pass"
}
Escalation Logic
Turqoa supports multi-tier escalation for transactions that are not resolved within configured time windows:
escalation:
tiers:
- name: operator
timeout: 120s
notify: gate_operator
channels: [dashboard, sms]
- name: supervisor
timeout: 300s
notify: gate_supervisor
channels: [dashboard, sms, email]
- name: manager
timeout: 600s
notify: terminal_manager
channels: [dashboard, sms, email, phone]
action: hold_lane # Pause lane processing until resolved
If a transaction reaches the final escalation tier without resolution, the lane is placed in hold status and no further transactions are processed until the pending review is completed.
Warning: Configure escalation timeouts based on your terminal's staffing model. Aggressive timeouts in understaffed shifts can cause unnecessary lane holds.
Policy DSL
Turqoa's policy DSL supports complex rule logic beyond simple field comparisons:
Logical Operators
# All conditions must match (AND)
when:
all:
- field: ocr.container.confidence
op: gte
value: 0.95
- field: damage.detected
op: eq
value: false
# Any condition can match (OR)
when:
any:
- field: validation.customs.clearance
op: eq
value: granted
- field: validation.customs.pre_cleared
op: eq
value: true
# Nested logic
when:
all:
- field: ocr.container.confidence
op: gte
value: 0.90
- any:
- field: validation.tos.appointment.type
op: eq
value: import_full
- field: validation.tos.appointment.type
op: eq
value: export_empty
Comparison Operators
| Operator | Meaning | Example |
|---|---|---|
eq | Equals | value: "confirmed" |
neq | Not equals | value: "cancelled" |
gt | Greater than | value: 0.90 |
gte | Greater than or equal | value: 0.95 |
lt | Less than | value: 5 |
lte | Less than or equal | value: 10 |
in | In list | value: [granted, pre_cleared] |
not_in | Not in list | value: [denied, pending] |
contains | String contains | value: "MSC" |
matches | Regex match | value: "^[A-Z]{4}\\d{7}$" |
exists | Field is present | (no value needed) |
Time-Based Conditions
- name: off_hours_review
priority: 50
when:
all:
- field: _time.hour
op: not_in
value: [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
- field: damage.detected
op: eq
value: true
then:
decision: review
reason: "Damage detected during off-hours — requires supervisor review"
escalation_tier: supervisor
Variables and References
Rules can reference computed values and cross-field comparisons:
- name: seal_mismatch
priority: 20
when:
- field: ocr.seal.value
op: neq
ref: validation.tos.appointment.expected_seal
then:
decision: review
reason: "Seal number does not match appointment record"
flags:
- seal_mismatch