Files
Kordant/tasks/shieldai-unified-restructure/18-hometitle-router.md
2026-05-25 12:23:23 -04:00

5.5 KiB

18. Backend Router — HomeTitle (Property Monitoring)

meta: id: shieldai-unified-restructure-18 feature: shieldai-unified-restructure priority: P1 depends_on: [shieldai-unified-restructure-12, shieldai-unified-restructure-13, shieldai-unified-restructure-14] tags: [backend, trpc, hometitle, property, api]

objective:

  • Build the tRPC router for HomeTitle, the property fraud monitoring service. Port all logic from services/hometitle/ and packages/api/src/routes/hometitle.routes.ts into a unified hometitle router and service layer.

deliverables:

  • web/src/server/api/routers/hometitle.ts — HomeTitle router:
    • hometitle.getPropertiesprotectedProcedure returning watched properties
    • hometitle.addPropertyprotectedProcedure adding property to watchlist
    • hometitle.removePropertyprotectedProcedure removing property
    • hometitle.getSnapshotsprotectedProcedure returning property record snapshots
    • hometitle.getChangesprotectedProcedure returning detected property changes
    • hometitle.runScanprotectedProcedure triggering manual property scan
    • hometitle.getAlertsprotectedProcedure returning property fraud alerts
  • web/src/server/services/hometitle.service.ts — Core business logic:
    • addProperty(subscriptionId, address, parcelId?, ownerName?) — geocode address, save record
    • removeProperty(userId, propertyId) — delete and cascade
    • getSnapshots(propertyId) — query snapshot history
    • getChanges(propertyId, filters?) — query changes with severity filtering
    • runScan(userId) — scan all watched properties:
      • Fetch current county records
      • Compare with last snapshot
      • Detect changes: ownership transfer, lien filing, tax change, metadata change
    • generateAlert(change) — create alert if change severity is warning/critical
  • web/src/server/services/hometitle/scanner.ts — County record scanning:
    • fetchCountyRecords(parcelId, county, state) — query county assessor/recorder APIs
    • parseDeedRecords(html) — extract ownership, date, lien info from HTML/PDF
    • geocodeAddress(address) — convert address to lat/lng using geocoding API
  • web/src/server/services/hometitle/change.detector.ts — Change detection:
    • detectChanges(oldSnapshot, newData) — compare fields and classify changes
    • severityForChange(changeType, magnitude) — determine severity level
    • fuzzyMatchNames(name1, name2) — Levenshtein distance for owner name comparison

steps:

  1. Create web/src/server/api/routers/hometitle.ts.
  2. Define Zod schemas:
    • addPropertySchema: address: z.string(), parcelId: z.string().optional(), ownerName: z.string().optional()
    • changeFilterSchema: severity: z.enum(['info', 'warning', 'critical']).optional(), changeType: z.enum([...]).optional()
  3. Implement router procedures:
    • Property CRUD with subscription scoping
    • Snapshot and change queries
    • Manual scan with tier limit enforcement
  4. Create web/src/server/services/hometitle.service.ts:
    • Port from services/hometitle/src/
    • Implement property geocoding on add
    • Implement scan orchestration
  5. Create scanner module:
    • fetchCountyRecords: placeholder for county API integration. Many counties lack APIs — document which counties are supported.
    • parseDeedRecords: HTML parsing with Cheerio or similar
    • geocodeAddress: use Google Maps, OpenStreetMap, or similar geocoding service
  6. Create change detector:
    • Compare snapshot fields: ownerName, address, deedDate, taxAmount, lienCount
    • Use fuzzy string matching for owner names
    • Classify changes into: ownership_transfer, lien_filing, tax_change, metadata_change
  7. Implement alert pipeline:
    • On significant change (warning/critical), create Alert and NormalizedAlert
    • Trigger notification via task 14 service
  8. Wire router into web/src/server/api/root.ts.
  9. Write unit tests with mocked county data.

steps:

  • Unit: addProperty geocodes address and creates record
  • Unit: detectChanges identifies ownership transfer and lien filing
  • Unit: fuzzyMatchNames handles minor spelling variations
  • Unit: severityForChange returns correct severity per change type
  • Integration: tRPC procedures enforce subscription scoping

acceptance_criteria:

  • Properties can be added with address geocoding and optional parcel ID
  • Properties are scoped to the user's subscription
  • Snapshots capture property record state at a point in time
  • Changes are detected by comparing new data to last snapshot
  • Manual scan can be triggered and respects tier limits
  • Alerts are generated for warning/critical changes
  • Fuzzy matching handles minor variations in owner names

validation:

  • Add a test property, verify geocoding and record creation
  • Simulate a snapshot change, verify change detection identifies the difference
  • Run manual scan and verify scan completion
  • Run cd web && pnpm test for HomeTitle unit tests

notes:

  • Reference legacy: services/hometitle/src/, packages/api/src/routes/hometitle.routes.ts
  • County record APIs are highly fragmented. The scanner should be designed as a plugin system where each county has its own adapter.
  • For unsupported counties, the scan should gracefully degrade and inform the user.
  • Property data may be sensitive. Ensure all records are encrypted at rest if required by compliance.
  • Consider integrating with a third-party property data provider (e.g., CoreLogic, ATTOM) for broader coverage.
  • The change detector should be configurable: users can choose which change types they want alerts for.