Turqoa Docs

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:

  1. Pre-check — Fast-fail conditions that immediately deny or flag a transaction
  2. Evaluation — Full rule set evaluation against enriched transaction data
  3. Resolution — Conflict resolution when multiple rules match
  4. 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:

CriterionThresholdConfigurable
Container OCR confidence>= 0.95Yes
Plate OCR confidence>= 0.90Yes
TOS appointment statusconfirmedNo
Customs clearancegrantedYes (can be disabled)
Damage severitynone or lowYes
Watchlist checkclearNo
Seal verification (if required)matchedYes

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

OperatorMeaningExample
eqEqualsvalue: "confirmed"
neqNot equalsvalue: "cancelled"
gtGreater thanvalue: 0.90
gteGreater than or equalvalue: 0.95
ltLess thanvalue: 5
lteLess than or equalvalue: 10
inIn listvalue: [granted, pre_cleared]
not_inNot in listvalue: [denied, pending]
containsString containsvalue: "MSC"
matchesRegex matchvalue: "^[A-Z]{4}\\d{7}$"
existsField 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