intermediate
permit
conditions
compliance

Permit-Condition Reports

How permit_conditions rows drive the daily compliance scoreboard — fleet cap, equity-zone deployment, complaint SLA, EOD trip report, and corral utilization.

Levy Fleets TeamMay 18, 202613 min read

Permit-Condition Reports

Most city permits include enforceable conditions — fleet cap, equity-zone deployment percentage, complaint SLA, end-of-day trip report, parking corral utilization. Levy stores each as a row in permit_conditions, evaluates them on a schedule, and renders the pass/fail trend in both the operator dashboard and the city portal.

The five condition types

condition_typeWhat it checksCadence
fleet_capCOUNT(active vehicles in jurisdiction) <= capDaily
equity_zone_pct% of deployed fleet inside equity geographies >= thresholdHourly
complaint_slaEach complaint's responded_at within N hours of created_atDaily
trip_report_eodA trip CSV is generated daily at 23:59 jurisdiction-local timeDaily
corral_utilizationHourly snapshot of parking corral occupancyHourly

Each row in permit_conditions carries the jurisdiction_id, condition_type, and a config JSONB that varies by type.

Configuring a condition

From the operator dashboard at /dashboard/compliance/{jurisdiction-id} -> Permit conditions tab, click Add condition.

fleet_cap

{
  "cap": 250,
  "vehicle_types": ["scooter"],
  "exclude_statuses": ["maintenance", "removed"]
}

The evaluator counts vehicles where:

  • subaccount_id matches the jurisdiction's subaccount
  • last_lat/last_lng is inside mds_jurisdictions.geometry (or NULL with last_seen in the last 24h — generous to vehicles that just lost GPS)
  • status NOT IN config.exclude_statuses
  • model.vehicle_type IN config.vehicle_types (if set)

Pass = count <= cap. Fail = count > cap. The dashboard scoreboard shows the current count and the cap side by side.

equity_zone_pct

{
  "threshold_pct": 20,
  "equity_geography_ids": ["geo-1", "geo-2"],
  "min_vehicles_required": 5
}

Pass = deployed-in-equity / total-deployed >= threshold_pct / 100. The min_vehicles_required is a floor — if there are fewer than that deployed at all, the condition is marked n/a for the hour rather than failed (sparse early-morning deployments).

Equity geographies come from either the city's Policy feed (rules with rule_type = 'equity_zone') or a manually configured list (when the city doesn't publish them — common with smaller cities). The manual list lives in the same permit_conditions.config.

complaint_sla

{
  "max_response_hours": 24,
  "complaint_sources": ["311", "in-app", "email"]
}

The evaluator joins complaints to find rows where responded_at IS NULL AND created_at < now() - interval '<hours>' (open and overdue) or responded_at - created_at > interval '<hours>' (closed but late). Pass = zero late complaints in the window.

trip_report_eod

{
  "format": "csv",
  "delivery": "email",
  "recipient_email": "permits@city.gov"
}

A scheduled report runs at 23:59 jurisdiction-local time and emits a trip CSV for the day. Pass = the report was successfully delivered. Fail = email bounced or the report row didn't materialize.

corral_utilization

{
  "min_utilization_pct": 10,
  "max_utilization_pct": 90,
  "exclude_corral_ids": []
}

Hourly snapshot of each parking corral's occupancy. Pass = all corrals between the configured floor and ceiling. Fail = at least one corral outside the band. The condition is used by cities that want to ensure parking is actually being used (low end) and that operators aren't over-concentrating (high end).

The scoreboard

Operators see the scoreboard at /dashboard/compliance/{jurisdiction-id}. Cities see it inside the city portal at /city/{slug}. The shape is the same:

ConditionCurrent valueThresholdStatus (today)30-day pass rate
Fleet cap247 vehicles250Pass100%
Equity zone %18%>= 20%Fail88%
Complaint SLA2 open over 24h0Fail92%
Trip report EODDeliveredn/aPass100%
Corral utilization6 of 6 in bandAll in bandPass95%

Failing conditions are highlighted in red. The 30-day pass rate is computed from the city_compliance_reports history.

The reporter library

src/lib/compliance/permit-reporter.ts exposes per-condition evaluators. Each takes the jurisdiction + the relevant permit_conditions.config and returns:

type ConditionResult = {
  condition_id: string;
  passed: boolean;
  current_value: number | string;
  threshold: number | string;
  measured_at: string; // RFC3339
  details: Record<string, unknown>;
};

The aggregate report at digest time:

const report = await buildComplianceReport({
  jurisdictionId,
  period: 'daily', // or 'weekly' / 'monthly'
  date: '2026-05-18',
});

// report.conditions: ConditionResult[]
// report.summary.passed: number
// report.summary.failed: number
// report.trips: { count, total_distance_km, ... }
// report.complaints: { open, closed, late, ... }
// report.enforcement_events: { speed_limits_applied, locks_issued, ... }

This shape is what the digest email renders, what the compliance-report API serves, and what gets persisted to city_compliance_reports.payload.

The compliance-report API

City contacts pull formal reports from:

GET /api/city/{slug}/compliance-report?period=monthly&date=2026-05

Returns the same JSON shape as the digest plus a pdf_url when period=monthly (we render a one-page PDF for monthly reports — useful for permit renewals).

Every fetched report is persisted to city_compliance_reports — the audit trail of every report the city has actually retrieved. If a city auditor asks "did you send a report for January?", the row in city_compliance_reports is the answer.

When conditions fail

A failing condition does not auto-pause your fleet. It surfaces:

  • A red row on the scoreboard
  • A line in today's digest email
  • A Sentry breadcrumb tagged compliance.condition.failed

What you do about it depends on the condition. For fleet_cap overage, deploy fewer vehicles. For equity_zone_pct, re-distribute. For complaint_sla, respond to the open complaints. The system doesn't make operational decisions for you.

The exception is fleet_cap, where the dashboard offers a "Pause new ride starts in jurisdiction" toggle. This is a soft constraint — existing rides continue, no new rides start in the jurisdiction until the cap is back under threshold. The toggle is operator-controlled; we don't activate it automatically because false-positive auto-pauses would be much worse than a transient fleet_cap fail.

Custom conditions

If your permit includes a condition that doesn't fit the five built-in types (e.g., "must achieve 90% multilingual signage"), add a row with condition_type = 'custom' and a manual passed field. The evaluator returns whatever you set; cities still see it on the scoreboard. This is the escape hatch for permits that have one weird clause.

What's next