advanced
rider-score
interventions
throttle-cap

Intervention Ladder

The 7-step ladder for unsafe riders - from in-app nudge to permanent ban, and exactly how each step reaches the vehicle.

Levy Fleets TeamMay 18, 202612 min read

Intervention Ladder

When a rider's score drops, the intervention engine walks a 7-step ladder. Each step is graduated. None of them are punitive on their own. The ladder is configured per-subaccount at Settings > Ladder rules.

The kernel lives at src/lib/rider-score/intervention-engine.ts. The pre-unlock gate is getActiveInterventionState() in src/app/api/mobile/rides/start/route.ts - it runs before any ride creation and can block ride start outright.

Navigation

Edit thresholds at Dashboard > Rider Score > Settings > Ladder rules. View active interventions at Dashboard > Rider Score > Interventions.

The 7 steps

StepTriggerAction
1Rolling score drops below 70In-app nudge after the ride, no friction at next unlock
22 consecutive rides below 60Push warning plus a "How to improve your score" guide
3Score below 50 OR a new open violationForce a 5-question educational quiz before the next unlock
4Score below 40Beginner-mode throttle cap on the next ride
5Score below 3025% price uplift on the next 10 rides
6Score below 20 OR 3 unpaid violations7-day temporary lockout
7Step 6 repeats within 60 daysPermanent ban (requires ops review)

Steps 1-3 are friction without performance impact. Steps 4-7 actually constrain the ride. Each step writes to rider_interventions with rider + ride context and an appeal window.

How each step reaches the rider

Step 1 - In-app nudge

Soft. A banner appears on the post-ride breakdown and the home tab telling the rider their score dropped and what hurt it most. Acknowledging the banner clears the intervention.

Step 2 - Push warning

Sent through the existing push pipeline. Includes a link to the "Improve your score" guide. Acknowledgement clears it.

Step 3 - Educational quiz

Before the next unlock, the rider has to pass a 5-question randomized quiz drawn from a 6-question bank. Each question's options are shuffled. The answer key is returned to the client as an HMAC-signed token to prevent tampering. Passing threshold is 4 out of 5.

The quiz must be passed before any ride can start. Failures do not penalize the score; they just block until pass.

Step 4 - Throttle cap (Beginner Mode)

This step reaches the vehicle. After the ride row is created, the post-start hook checks for an open step-4 intervention. If one exists, applyThrottleCapToVehicle() calls into src/lib/iot/disable-throttle.ts.

  • OKAI, Segway, Omni 4G: targeted for true partial cap (Beginner-mode throttle) when per-OEM commands are wired in. Today the implementation falls back to full disable as the conservative bound until OEM-specific partial commands ship.
  • Vehicles that do not support throttle commands at all skip step 4 and jump to step 5 on the next unsafe trip.
  • The cap is lifted automatically when the next ride ends, or sooner if the appeal is upheld.

Step 5 - Price uplift

A 25% uplift is applied to the next 10 rides. The mechanism is consumeUpliftForRide(), which decrements the counter after each ride and closes the intervention when it hits zero. The uplift is surfaced clearly to the rider at unlock - it is not a hidden charge.

Step 6 - 7-day temp lockout

Pre-unlock gate. The rider opens the app and sees a lockout screen with the reason, the expiration timestamp, and an Appeal button. Ride start is blocked at the API layer - getActiveInterventionState() returns a 403 with reason='temp_lockout'. The lockout clears automatically at expires_at.

Step 7 - Permanent ban

Same pre-unlock gate as step 6, but with no automatic expiration. Step 7 only fires if step 6 has fired and resolved within the last 60 days for the same rider. The intervention is created with status='open' but is gated behind manual ops review by default. An ops reviewer must approve before the ban activates. This is intentional - permanent bans are the highest-stakes intervention and should not be touched by automation alone.

Pre-unlock gate

Steps 3, 4, 6, and 7 all gate at ride start. The gate is implemented in the mobile rides-start API:

getActiveInterventionState(customerId, subaccountId)
  -> blocked: 'permanent_ban' | 'temp_lockout' | 'force_quiz_required'
  -> throttleCap: { mode: 'beginner' } | null
  -> upliftPct: 25 | null

The mobile app receives the block reason and renders a screen with the next step the rider has to take (acknowledge, take quiz, file appeal, or wait for expiration).

Appeals pause the ladder

Filing an appeal on a triggering ride flips the linked intervention to status='paused_pending_appeal'. The intervention is not enforced while the appeal is pending. Resolution:

  • Accepted: the trip score is overridden, rolling score recomputes, and the intervention may close on its own if the new score no longer crosses the threshold.
  • Rejected: the intervention re-opens with its original expiration math.

See Appeals and Disputes for the full appeal workflow and SLA.

Per-step idempotency

A partial unique index on rider_interventions(customer, subaccount, step) WHERE status='open' guarantees we never open two of the same step at once for the same rider. If a rider's score keeps dropping after a step is already open, the engine evaluates whether to escalate, not whether to duplicate.

Audit trail

Every intervention - creation, escalation, acknowledgement, expiration, appeal-resolution, lift, ban - writes a row to score_audit_log with the actor user_id, before/after JSON, and a reason. The operator dashboard exposes this at Dashboard > Rider Score > Audit Log.

Per-subaccount tuning

You can edit thresholds, the number of uplifted rides at step 5, the lockout duration at step 6, the 60-day window for step 7, and whether step 7 requires manual approval. Defaults are conservative and match the table at the top of this article.

You cannot remove a step entirely - the ladder is ordered. You can effectively disable a step by setting its threshold to a value the rider will never hit.

What the operator sees

  • Dashboard > Rider Score > Interventions lists every active intervention with rider, ride context, step, status, expiration, and a "Lift" button (with mandatory reason).
  • Audit Log shows the full history.
  • Appeals shows pending appeals tied to interventions.

What the rider sees

  • The post-ride breakdown surfaces a banner if a new intervention was opened.
  • The Rider Score home tab shows active interventions with a plain-English description of how to clear them.
  • The pre-unlock screen blocks ride start if a hard intervention is active.

Next

See Appeals and Disputes for how to handle a rider's pushback. See Reaction-Time Safe Ride Check for the late-night unlock flow that can also feed step 6.