Skip to content

Upgrade plan to v0.9 #19

@thumpersecure

Description

@thumpersecure

AI Agent Coding Plan: Council District & Councilmember Resolution

Feature Overview

Automatically resolve a property to its City Council district and current councilmember, displaying contact information and flagging committee-relevant routing when applicable.


Agent Objectives

Primary Goal

Given a property address or OPA number, the agent must:

  1. Extract or normalize the property coordinates (latitude, longitude)
  2. Perform point-in-polygon lookup against Philadelphia’s Council District shapefile
  3. Identify the current councilmember for that district
  4. Return councilmember name, district number, office email, and phone
  5. Flag if the L&I Committee chair is the relevant member (for licensing/violations issues)

Success Criteria

  • Coordinate lookup: <100ms (in-memory shapefile or cached GeoJSON)
  • Councilmember data: always current (refreshed weekly or on-demand from city API)
  • Committee flagging: correctly identifies L&I Chair for violations/licensing contexts
  • User-facing output: councilmember contact + district + committee flag in <50 tokens

Data Sources & Dependencies

Primary Data

Source Purpose Format Update Cadence Notes
OPA Property API Property coordinates (lat/lon) JSON Real-time Use existing OPA lookup; extract centroid
City Council District Shapefile Point-in-polygon lookup GeoJSON or shapefile binary Annual or on-demand Available from City’s GIS; convert to GeoJSON for in-memory use
City Council Member Directory Name, district, contact info Published CSV/JSON or web scrape Weekly or triggered phila.gov or official council roster
L&I Committee Membership Current chair identification HTML table or API Monthly or event-driven Parse from council website or FOIA/public record

Secondary Validation

  • Cross-reference OPA district assignment (if available) as fallback/validation
  • Cache layer: Redis or in-memory for shapefile + councilmember directory (TTL: 7 days)

Agent Workflow

Step 1: Input & Normalization

Agent Task:

  • Accept property address (string) or OPA number (numeric)
  • Call OPA Property API to fetch property record
  • Extract LATITUDE and LONGITUDE fields
  • Validate coordinates are within Philadelphia bounds
  • Output: (lat, lon, opa_number, address_string)

Error Handling:

  • If OPA lookup fails: return user-friendly error + suggest alternative input
  • If coordinates missing: attempt reverse-geocode from address string
  • If outside Philly: inform user, suggest nearby properties

Step 2: District Resolution via Point-in-Polygon

Agent Task:

  • Load Philadelphia Council District shapefile (GeoJSON, in-memory or cached)
  • Perform point-in-polygon test against property coordinates
  • Match to district number (1–17)
  • Output: district_number

Implementation Notes:

  • Use lightweight geo library: point-in-polygon npm or similar
  • Preload shapefile on agent startup; cache as JSON for speed
  • If shapefile unavailable: fall back to OPA’s internal district field (if present)

Fallback:

  • Query City’s GIS API directly if local lookup fails (slower, network-dependent)

Step 3: Councilmember Lookup

Agent Task:

  • Query cached councilmember directory by district_number
  • Return: name, office email, phone, committee assignments
  • Output: {name, email, phone, committees: []}

Data Refresh Logic:

  • On startup: fetch from phila.gov or official source
  • Cache TTL: 7 days
  • Manual refresh endpoint: agent.refresh_councilmembers()
  • Fallback: embedded JSON snapshot (updated quarterly in code)

Step 4: Committee Flagging (Conditional)

Agent Task:

  • Check if current issue context includes licensing violations or rental license expiration
  • Retrieve L&I Committee chair name + contact
  • If district’s councilmember == L&I Chair: flag explicitly
  • If district’s councilmember != L&I Chair: note chair’s committee role for escalation
  • Output: {committee_relevant: bool, flag_text: string}

Flag Logic:

IF issue_type IN ['license_violation', 'rental_license_expired', 'unfit_structure', 'dangerous_building']:
  IF district_councilmember == li_committee_chair:
    flag = "Your councilmember, [NAME], chairs the L&I Committee — direct jurisdiction."
  ELSE:
    flag = "For licensing issues, consider L&I Committee Chair [CHAIR_NAME] — direct contact: [EMAIL/PHONE]."
ELSE:
  flag = null

Output Format (User-Facing)

Standard Response

Council District: 2
Councilmember: Kenyatta Johnson
Email: kenyatta.johnson@phila.gov
Phone: (215) 686-XXXX

With Committee Flag (Licensing/Violations)

Council District: 2
Councilmember: Mark Squilla
Email: mark.squilla@phila.gov
Phone: (215) 686-XXXX

⚠️ Committee Note: Your councilmember chairs the L&I Committee, 
which has direct oversight of rental licensing and building violations.

Escalation Path (Non-Chair District)

Council District: 3
Councilmember: Jamie Gauthier
Email: jamie.gauthier@phila.gov
Phone: (215) 686-XXXX

For licensing or violation issues, the L&I Committee Chair is:
Mark Squilla | mark.squilla@phila.gov | (215) 686-XXXX

Implementation Checklist

Phase 1: Core Lookup (Minimum Viable)

  • Parse property coordinates from OPA lookup
  • Convert City Council District shapefile to GeoJSON
  • Implement point-in-polygon lookup (geolocation library)
  • Build councilmember directory cache (JSON or embedded)
  • Return basic councilmember card (name, email, phone)
  • Unit tests: 5 test properties across districts 1, 5, 10, 15, 17

Phase 2: Committee Flagging & Context Awareness

  • Parse issue type from upstream context (violations, licensing, etc.)
  • Fetch/maintain L&I Committee roster
  • Implement flag logic (chair vs. member vs. escalation)
  • Add committee context to output
  • Test with licensing + violations scenarios

Phase 3: Robustness & Refresh

  • Implement cache invalidation (TTL + manual refresh)
  • Add fallback to OPA district field
  • Reverse-geocode if coordinates unavailable
  • Monitor council roster for changes; alert on updates
  • Integration test with live phila.gov data

Performance & Costs

Operation Latency Cost Notes
OPA lookup ~200ms $0 (cached) Reuse from prior property fetch
Point-in-polygon <10ms $0 (in-memory) Preloaded shapefile
Councilmember lookup <5ms $0 (cached JSON) 7-day TTL
Committee check <5ms $0 (cached) Embedded roster
Total ~200ms $0 Dominated by OPA call

Scope Notes

In Scope (This Feature)

✅ Property → Council district resolution (point-in-polygon)
✅ Councilmember name, contact, committee assignments
✅ L&I Committee chair identification for licensing/violations
✅ Committee-relevant routing flags
✅ Caching for performance & cost control

Out of Scope

❌ Individual councilmember voting records or positions
❌ Constituent casework tracking or ticket submission
❌ Real-time council hearing schedules or bill tracking
❌ Other city boards (ZBA, HRC, etc.) — scope is council only


Dependencies & Libraries

{
  "point-in-polygon": "^2.2.0",
  "geojson": "^0.5.0",
  "lru-cache": "^10.x",
  "axios": "^1.6.0"
}

Success Metrics (Post-Launch)

  • Accuracy: 100% district assignment (validated against sample of 100 properties)
  • Councilmember data freshness: <7 days stale
  • Committee flag precision: 0 false positives in violations/licensing queries
  • User satisfaction: “Contact info was helpful” (binary feedback)
  • Latency: P95 <300ms (including OPA call)

Notes for Implementation

  1. Shapefile Conversion: Download from City’s GIS portal, convert using GDAL or ogr2ogr to GeoJSON once, commit to repo. No runtime shape parsing needed.
  2. Councilmember Directory: Scrape phila.gov on-demand (weekly cron), or manually maintain a JSON snapshot. Include district, name, email, phone, committees.
  3. L&I Chair: Currently Mark Squilla (District 5, Chair). Maintain a small JSON config mapping “li_committee_chair” → current person + contact. Update whenever council changes chair.
  4. Issue Context: When agent receives a property + issue type (e.g., “rental_license_expired”), pass issue_type to the councilmember lookup routine so it can decide whether to flag committee relevance.
  5. Fallback Chain: OPA coords → reverse-geocode → OPA district field → error. Do not fail silently.

Example Agent Invocation

# Input
agent.resolve_council_member(
    property_address="315 N 12th St, Philadelphia, PA",
    issue_type="rental_license_expired"
)

# Output
{
    "district": 2,
    "councilmember": {
        "name": "Kenyatta Johnson",
        "email": "kenyatta.johnson@phila.gov",
        "phone": "(215) 686-XXXX"
    },
    "committee_flag": {
        "relevant": false,
        "text": "For licensing issues, contact L&I Committee Chair Mark Squilla: mark.squilla@phila.gov"
    }
}

Timeline Recommendation

Priority: HIGH (ship before PhilaDox/ZBA work)
Estimated effort: 3–5 days (Phase 1 + 2)
Dependencies: None (OPA already integrated)
Blocking: None

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions